Merge "Disabled debug logs" into nyc-dev
diff --git a/Android.mk b/Android.mk
index 3ac5889..ffc0421 100644
--- a/Android.mk
+++ b/Android.mk
@@ -241,11 +241,13 @@
 	core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
 	core/java/android/service/notification/IConditionListener.aidl \
 	core/java/android/service/notification/IConditionProvider.aidl \
+	core/java/android/service/vr/IVrListener.aidl \
 	core/java/android/print/ILayoutResultCallback.aidl \
 	core/java/android/print/IPrinterDiscoveryObserver.aidl \
 	core/java/android/print/IPrintDocumentAdapter.aidl \
 	core/java/android/print/IPrintDocumentAdapterObserver.aidl \
 	core/java/android/print/IPrintJobStateChangeListener.aidl \
+	core/java/android/print/IPrintServicesChangeListener.aidl \
 	core/java/android/print/IPrintManager.aidl \
 	core/java/android/print/IPrintSpooler.aidl \
 	core/java/android/print/IPrintSpoolerCallbacks.aidl \
diff --git a/api/current.txt b/api/current.txt
index e5e1248..5ec93cb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -38,6 +38,7 @@
     field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
     field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
     field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+    field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
     field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
     field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
     field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -114,6 +115,7 @@
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+    field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
     field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
@@ -3604,7 +3606,7 @@
     method public deprecated void setTitleColor(int);
     method public void setVisible(boolean);
     method public final void setVolumeControlStream(int);
-    method public void setVrMode(boolean);
+    method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean shouldShowRequestPermissionRationale(java.lang.String);
     method public boolean shouldUpRecreateTask(android.content.Intent);
     method public boolean showAssist(android.os.Bundle);
@@ -19720,7 +19722,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19866,7 +19868,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19940,7 +19942,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -19948,7 +19950,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -20384,6 +20386,7 @@
     field public static final int ERROR_NO_KEY = 1; // 0x1
     field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
     field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
   }
 
   public static final class MediaCodec.CryptoInfo {
@@ -32170,6 +32173,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -32207,6 +32211,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String BOOT_COUNT = "boot_count";
     field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -34487,7 +34492,6 @@
     method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
     method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
-    field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -34782,6 +34786,17 @@
 
 }
 
+package android.service.vr {
+
+  public abstract class VrListenerService extends android.app.Service {
+    ctor public VrListenerService();
+    method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+    method public android.os.IBinder onBind(android.content.Intent);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+  }
+
+}
+
 package android.service.wallpaper {
 
   public abstract class WallpaperService extends android.app.Service {
diff --git a/api/system-current.txt b/api/system-current.txt
index 7d94199..4224792 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -52,6 +52,7 @@
     field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
     field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
     field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+    field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
     field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
     field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
     field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -193,6 +194,7 @@
     field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+    field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
     field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -3721,7 +3723,7 @@
     method public deprecated void setTitleColor(int);
     method public void setVisible(boolean);
     method public final void setVolumeControlStream(int);
-    method public void setVrMode(boolean);
+    method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean shouldShowRequestPermissionRationale(java.lang.String);
     method public boolean shouldUpRecreateTask(android.content.Intent);
     method public boolean showAssist(android.os.Bundle);
@@ -21201,7 +21203,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -21355,7 +21357,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -21432,7 +21434,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -21440,7 +21442,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -21876,6 +21878,7 @@
     field public static final int ERROR_NO_KEY = 1; // 0x1
     field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
     field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
   }
 
   public static final class MediaCodec.CryptoInfo {
@@ -34657,6 +34660,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -34694,6 +34698,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String BOOT_COUNT = "boot_count";
     field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -36953,37 +36958,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 android.os.IBinder onBind(android.content.Intent);
-    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);
-    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 = 14; // 0xe
-    field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
-    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();
@@ -37014,7 +36988,6 @@
     method public final void setNotificationsShown(java.lang.String[]);
     method public final void setOnNotificationPostedTrim(int);
     method public void unregisterAsSystemService() throws android.os.RemoteException;
-    field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -37054,6 +37027,37 @@
     field public static final android.os.Parcelable.Creator<android.service.notification.NotificationListenerService.RankingMap> CREATOR;
   }
 
+  public abstract class NotificationRankerService extends android.service.notification.NotificationListenerService {
+    ctor public NotificationRankerService();
+    method public final void adjustImportance(java.lang.String, android.service.notification.NotificationRankerService.Adjustment);
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onNotificationActionClick(java.lang.String, long, int);
+    method public void onNotificationClick(java.lang.String, long);
+    method public abstract android.service.notification.NotificationRankerService.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);
+    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 = 14; // 0xe
+    field public static final int REASON_PROFILE_TURNED_OFF = 15; // 0xf
+    field public static final int REASON_USER_STOPPED = 6; // 0x6
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationRankerService";
+  }
+
+  public class NotificationRankerService.Adjustment {
+    ctor public NotificationRankerService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+  }
+
   public class StatusBarNotification implements android.os.Parcelable {
     ctor public StatusBarNotification(java.lang.String, java.lang.String, int, java.lang.String, int, int, int, android.app.Notification, android.os.UserHandle, long);
     ctor public StatusBarNotification(android.os.Parcel);
@@ -37367,6 +37371,17 @@
 
 }
 
+package android.service.vr {
+
+  public abstract class VrListenerService extends android.app.Service {
+    ctor public VrListenerService();
+    method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+    method public android.os.IBinder onBind(android.content.Intent);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+  }
+
+}
+
 package android.service.wallpaper {
 
   public abstract class WallpaperService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index d1ed20c..894971b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -38,6 +38,7 @@
     field public static final java.lang.String BIND_TV_INPUT = "android.permission.BIND_TV_INPUT";
     field public static final java.lang.String BIND_VOICE_INTERACTION = "android.permission.BIND_VOICE_INTERACTION";
     field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE";
+    field public static final java.lang.String BIND_VR_LISTENER_SERVICE = "android.permission.BIND_VR_LISTENER_SERVICE";
     field public static final java.lang.String BIND_WALLPAPER = "android.permission.BIND_WALLPAPER";
     field public static final java.lang.String BLUETOOTH = "android.permission.BLUETOOTH";
     field public static final java.lang.String BLUETOOTH_ADMIN = "android.permission.BLUETOOTH_ADMIN";
@@ -114,6 +115,7 @@
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
+    field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
     field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
     field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
@@ -3604,7 +3606,7 @@
     method public deprecated void setTitleColor(int);
     method public void setVisible(boolean);
     method public final void setVolumeControlStream(int);
-    method public void setVrMode(boolean);
+    method public void setVrModeEnabled(boolean, android.content.ComponentName) throws android.content.pm.PackageManager.NameNotFoundException;
     method public boolean shouldShowRequestPermissionRationale(java.lang.String);
     method public boolean shouldUpRecreateTask(android.content.Intent);
     method public boolean showAssist(android.os.Bundle);
@@ -19731,7 +19733,7 @@
     method public void adjustVolume(int, int);
     method public void dispatchMediaKeyEvent(android.view.KeyEvent);
     method public int generateAudioSessionId();
-    method public android.media.AudioRecordConfiguration[] getActiveRecordConfigurations();
+    method public android.media.AudioRecordingConfiguration[] getActiveRecordingConfigurations();
     method public android.media.AudioDeviceInfo[] getDevices(int);
     method public int getMode();
     method public java.lang.String getParameters(java.lang.String);
@@ -19877,7 +19879,7 @@
 
   public static abstract class AudioManager.AudioRecordingCallback {
     ctor public AudioManager.AudioRecordingCallback();
-    method public void onRecordConfigChanged(android.media.AudioRecordConfiguration[]);
+    method public void onRecordConfigChanged(android.media.AudioRecordingConfiguration[]);
   }
 
   public static abstract interface AudioManager.OnAudioFocusChangeListener {
@@ -19951,7 +19953,7 @@
     method public abstract void onRoutingChanged(android.media.AudioRecord);
   }
 
-  public final class AudioRecordConfiguration implements android.os.Parcelable {
+  public final class AudioRecordingConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioDeviceInfo getAudioDevice();
     method public int getClientAudioSessionId();
@@ -19959,7 +19961,7 @@
     method public android.media.AudioFormat getClientFormat();
     method public android.media.AudioFormat getFormat();
     method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.media.AudioRecordConfiguration> CREATOR;
+    field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
   }
 
   public abstract interface AudioRouting {
@@ -20395,6 +20397,7 @@
     field public static final int ERROR_NO_KEY = 1; // 0x1
     field public static final int ERROR_RESOURCE_BUSY = 3; // 0x3
     field public static final int ERROR_SESSION_NOT_OPENED = 5; // 0x5
+    field public static final int ERROR_UNSUPPORTED_OPERATION = 6; // 0x6
   }
 
   public static final class MediaCodec.CryptoInfo {
@@ -32185,6 +32188,7 @@
     field public static final java.lang.String ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE = "android.settings.VOICE_CONTROL_BATTERY_SAVER_MODE";
     field public static final java.lang.String ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE = "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE";
     field public static final java.lang.String ACTION_VOICE_INPUT_SETTINGS = "android.settings.VOICE_INPUT_SETTINGS";
+    field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
     field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
     field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
@@ -32222,6 +32226,7 @@
     field public static final java.lang.String AUTO_TIME = "auto_time";
     field public static final java.lang.String AUTO_TIME_ZONE = "auto_time_zone";
     field public static final java.lang.String BLUETOOTH_ON = "bluetooth_on";
+    field public static final java.lang.String BOOT_COUNT = "boot_count";
     field public static final java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATA_ROAMING = "data_roaming";
@@ -34504,7 +34509,6 @@
     method public static void requestRebind(android.content.ComponentName) throws android.os.RemoteException;
     method public final void requestUnbind() throws android.os.RemoteException;
     method public final void setNotificationsShown(java.lang.String[]);
-    field public static final java.lang.String CATEGORY_VR_NOTIFICATIONS = "android.intent.category.vr.notifications";
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
@@ -34799,6 +34803,17 @@
 
 }
 
+package android.service.vr {
+
+  public abstract class VrListenerService extends android.app.Service {
+    ctor public VrListenerService();
+    method public static final boolean isVrModePackageEnabled(android.content.Context, android.content.ComponentName);
+    method public android.os.IBinder onBind(android.content.Intent);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+  }
+
+}
+
 package android.service.wallpaper {
 
   public abstract class WallpaperService extends android.app.Service {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 8f361ce..ea53e59 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -68,8 +68,7 @@
 
 // ---------------------------------------------------------------------------
 
-BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
-{
+BootAnimation::BootAnimation() : Thread(false), mZip(NULL), mClockEnabled(true) {
     mSession = new SurfaceComposerClient();
 }
 
@@ -450,6 +449,69 @@
     return true;
 }
 
+// The time glyphs are stored in a single image of height 64 pixels. Each digit is 40 pixels wide,
+// and the colon character is half that at 20 pixels. The glyph order is '0123456789:'.
+// We render 24 hour time.
+void BootAnimation::drawTime(const Texture& clockTex, const int yPos) {
+    static constexpr char TIME_FORMAT[] = "%H:%M";
+    static constexpr int TIME_LENGTH = sizeof(TIME_FORMAT);
+
+    static constexpr int DIGIT_HEIGHT = 64;
+    static constexpr int DIGIT_WIDTH = 40;
+    static constexpr int COLON_WIDTH = DIGIT_WIDTH / 2;
+    static constexpr int TIME_WIDTH = (DIGIT_WIDTH * 4) + COLON_WIDTH;
+
+    if (clockTex.h < DIGIT_HEIGHT || clockTex.w < (10 * DIGIT_WIDTH + COLON_WIDTH)) {
+        ALOGE("Clock texture is too small; abandoning boot animation clock");
+        mClockEnabled = false;
+        return;
+    }
+
+    time_t rawtime;
+    time(&rawtime);
+    struct tm* timeInfo = localtime(&rawtime);
+
+    char timeBuff[TIME_LENGTH];
+    size_t length = strftime(timeBuff, TIME_LENGTH, TIME_FORMAT, timeInfo);
+
+    if (length != TIME_LENGTH - 1) {
+        ALOGE("Couldn't format time; abandoning boot animation clock");
+        mClockEnabled = false;
+        return;
+    }
+
+    glEnable(GL_BLEND);  // Allow us to draw on top of the animation
+    glBindTexture(GL_TEXTURE_2D, clockTex.name);
+
+    int xPos = (mWidth - TIME_WIDTH) / 2;
+    int cropRect[4] = { 0, DIGIT_HEIGHT, DIGIT_WIDTH, -DIGIT_HEIGHT };
+
+    for (int i = 0; i < TIME_LENGTH - 1; i++) {
+        char c = timeBuff[i];
+        int width = DIGIT_WIDTH;
+        int pos = c - '0';  // Position in the character list
+        if (pos < 0 || pos > 10) {
+            continue;
+        }
+        if (c == ':') {
+            width = COLON_WIDTH;
+        }
+
+        // Crop the texture to only the pixels in the current glyph
+        int left = pos * DIGIT_WIDTH;
+        cropRect[0] = left;
+        cropRect[2] = width;
+        glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, cropRect);
+
+        glDrawTexiOES(xPos, yPos, 0, width, DIGIT_HEIGHT);
+
+        xPos += width;
+    }
+
+    glDisable(GL_BLEND);  // Return to the animation's default behaviour
+    glBindTexture(GL_TEXTURE_2D, 0);
+}
+
 bool BootAnimation::movie()
 {
     String8 desString;
@@ -477,7 +539,12 @@
         if (endl == NULL) break;
         String8 line(s, endl - s);
         const char* l = line.string();
-        int fps, width, height, count, pause;
+        int fps = 0;
+        int width = 0;
+        int height = 0;
+        int count = 0;
+        int pause = 0;
+        int clockPosY = -1;
         char path[ANIM_ENTRY_NAME_MAX];
         char color[7] = "000000"; // default to black if unspecified
 
@@ -487,14 +554,15 @@
             animation.width = width;
             animation.height = height;
             animation.fps = fps;
-        }
-        else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {
-            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
+        } else if (sscanf(l, " %c %d %d %s #%6s %d",
+                          &pathType, &count, &pause, path, color, &clockPosY) >= 4) {
+            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s, clockPosY=%d", pathType, count, pause, path, color, clockPosY);
             Animation::Part part;
             part.playUntilComplete = pathType == 'c';
             part.count = count;
             part.pause = pause;
             part.path = path;
+            part.clockPosY = clockPosY;
             part.audioFile = NULL;
             if (!parseColor(color, part.backgroundColor)) {
                 ALOGE("> invalid color '#%s'", color);
@@ -556,6 +624,8 @@
 
     mZip->endIteration(cookie);
 
+    // Blend required to draw time on top of animation frames.
+    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
     glShadeModel(GL_FLAT);
     glDisable(GL_DITHER);
     glDisable(GL_SCISSOR_TEST);
@@ -569,6 +639,12 @@
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 
+    bool clockTextureInitialized = false;
+    if (mClockEnabled) {
+        clockTextureInitialized = (initTexture(&mClock, mAssets, "images/clock64.png") == NO_ERROR);
+        mClockEnabled = clockTextureInitialized;
+    }
+
     const int xc = (mWidth - animation.width) / 2;
     const int yc = ((mHeight - animation.height) / 2);
     nsecs_t frameDuration = s2ns(1) / animation.fps;
@@ -629,6 +705,10 @@
                 // which is equivalent to mHeight - (yc + animation.height)
                 glDrawTexiOES(xc, mHeight - (yc + animation.height),
                               0, animation.width, animation.height);
+                if (mClockEnabled && part.clockPosY >= 0) {
+                    drawTime(mClock, part.clockPosY);
+                }
+
                 eglSwapBuffers(mDisplay, mSurface);
 
                 nsecs_t now = systemTime();
@@ -665,6 +745,10 @@
         }
     }
 
+    if (clockTextureInitialized) {
+        glDeleteTextures(1, &mClock.name);
+    }
+
     return false;
 }
 
diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
index f968b25..83e2b38 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -67,8 +67,10 @@
             }
         };
         struct Part {
-            int count;
-            int pause;
+            int count;  // The number of times this part should repeat, 0 for infinite
+            int pause;  // The number of frames to pause for at the end of this part
+            int clockPosY;  // The y position of the clock, in pixels, from the bottom of the
+                            // display (the clock is centred horizontally). -1 to disable the clock
             String8 path;
             SortedVector<Frame> frames;
             bool playUntilComplete;
@@ -86,6 +88,7 @@
     bool android();
     bool readFile(const char* name, String8& outString);
     bool movie();
+    void drawTime(const Texture& clockTex, const int yPos);
 
     void checkExit();
 
@@ -93,6 +96,7 @@
     sp<AudioPlayer>                 mAudioPlayer;
     AssetManager mAssets;
     Texture     mAndroid[2];
+    Texture     mClock;
     int         mWidth;
     int         mHeight;
     EGLDisplay  mDisplay;
@@ -101,6 +105,7 @@
     sp<SurfaceControl> mFlingerSurfaceControl;
     sp<Surface> mFlingerSurface;
     ZipFileRO   *mZip;
+    bool        mClockEnabled;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index b87e9fa2..6b67b95 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6177,14 +6177,24 @@
     /**
      * Enable or disable virtual reality (VR) mode.
      *
-     * <p>VR mode is a hint to Android system services to switch to modes optimized for
-     * high-performance stereoscopic rendering.</p>
+     * <p>VR mode is a hint to Android system services to switch to a mode optimized for
+     * high-performance stereoscopic rendering.  This mode will be enabled while this Activity has
+     * focus.</p>
      *
      * @param enabled {@code true} to enable this mode.
+     * @param requestedComponent the name of the component to use as a
+     *        {@link android.service.vr.VrListenerService} while VR mode is enabled.
+     *
+     * @throws android.content.pm.PackageManager.NameNotFoundException;
      */
-    public void setVrMode(boolean enabled) {
+    public void setVrModeEnabled(boolean enabled, @NonNull ComponentName requestedComponent)
+          throws PackageManager.NameNotFoundException {
         try {
-            ActivityManagerNative.getDefault().setVrMode(mToken, enabled);
+            if (ActivityManagerNative.getDefault().setVrMode(mToken, enabled, requestedComponent)
+                    != 0) {
+                throw new PackageManager.NameNotFoundException(
+                        requestedComponent.flattenToString());
+            }
         } catch (RemoteException e) {
             // pass
         }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a4e5b90..2f6907e 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1575,6 +1575,7 @@
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
             dest.writeInt(numActivities);
             dest.writeInt(numRunning);
+            dest.writeInt(isDockable ? 1 : 0);
         }
 
         public void readFromParcel(Parcel source) {
@@ -1590,6 +1591,7 @@
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             numActivities = source.readInt();
             numRunning = source.readInt();
+            isDockable = source.readInt() != 0;
         }
 
         public static final Creator<RunningTaskInfo> CREATOR = new Creator<RunningTaskInfo>() {
@@ -3371,6 +3373,15 @@
         }
     }
 
+    /** {@hide} */
+    public boolean isVrModePackageEnabled(ComponentName component) {
+        try {
+            return ActivityManagerNative.getDefault().isVrModePackageEnabled(component);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Perform a system dump of various state associated with the given application
      * package name.  This call blocks while the dump is being performed, so should
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index ff7f70d..6fbb430 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2902,8 +2902,18 @@
             data.enforceInterface(IActivityManager.descriptor);
             final IBinder token = data.readStrongBinder();
             final boolean enable = data.readInt() == 1;
-            setVrMode(token, enable);
+            final ComponentName packageName = ComponentName.CREATOR.createFromParcel(data);
+            int res = setVrMode(token, enable, packageName);
             reply.writeNoException();
+            reply.writeInt(res);
+            return true;
+        }
+        case IS_VR_PACKAGE_ENABLED_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            final ComponentName packageName = ComponentName.CREATOR.createFromParcel(data);
+            boolean res = isVrModePackageEnabled(packageName);
+            reply.writeNoException();
+            reply.writeInt(res ? 1 : 0);
             return true;
         }
         case IS_APP_FOREGROUND_TRANSACTION: {
@@ -6247,16 +6257,34 @@
         return res;
     }
 
-    public void setVrMode(IBinder token, boolean enabled) throws RemoteException {
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
+            throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
         data.writeInt(enabled ? 1 : 0);
+        packageName.writeToParcel(data, 0);
         mRemote.transact(SET_VR_MODE_TRANSACTION, data, reply, 0);
         reply.readException();
+        int res = reply.readInt();
         data.recycle();
         reply.recycle();
+        return res;
+    }
+
+    public boolean isVrModePackageEnabled(ComponentName packageName)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        packageName.writeToParcel(data, 0);
+        mRemote.transact(IS_VR_PACKAGE_ENABLED_TRANSACTION, data, reply, 0);
+        reply.readException();
+        int res = reply.readInt();
+        data.recycle();
+        reply.recycle();
+        return res == 1;
     }
 
     @Override
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 70bff80..eadf497 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -33,6 +33,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.IPackageDataObserver;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.ProviderInfo;
 import android.content.pm.UserInfo;
@@ -599,7 +600,10 @@
 
     public void enterPictureInPicture(IBinder token) throws RemoteException;
 
-    public void setVrMode(IBinder token, boolean enabled) throws RemoteException;
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName)
+            throws RemoteException;
+
+    public boolean isVrModePackageEnabled(ComponentName packageName) throws RemoteException;
 
     public boolean isAppForeground(int uid) throws RemoteException;
 
@@ -993,4 +997,5 @@
     int SET_LENIENT_BACKGROUND_CHECK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+368;
     int GET_MEMORY_TRIM_LEVEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+369;
     int RESIZE_PINNED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 370;
+    int IS_VR_PACKAGE_ENABLED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 371;
 }
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 5697924..2c270fc 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -61,7 +61,7 @@
     StatusBarNotification[] getActiveNotifications(String callingPkg);
     StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
 
-    void registerListener(in INotificationListener listener, in ComponentName component, int userid);
+    void registerListener(in INotificationListener listener, in ComponentName component, int userid, boolean asRanker);
     void unregisterListener(in INotificationListener listener, int userid);
 
     void cancelNotificationFromListener(in INotificationListener token, String pkg, String tag, int id);
@@ -80,7 +80,7 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter);
 
-    void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
+    void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2c34371..d8f0ac5 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2994,7 +2994,6 @@
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
             contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
             contentView.setViewVisibility(R.id.header_content_info, View.GONE);
-            contentView.setViewVisibility(R.id.number_of_children, View.GONE);
             contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
             contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
             contentView.setViewVisibility(R.id.time_divider, View.GONE);
@@ -3095,7 +3094,6 @@
 
         private void bindNotificationHeader(RemoteViews contentView) {
             bindSmallIcon(contentView);
-            bindChildCountColor(contentView);
             bindHeaderAppName(contentView);
             bindHeaderSubText(contentView);
             bindContentInfo(contentView);
@@ -3104,10 +3102,6 @@
             bindProfileBadge(contentView);
         }
 
-        private void bindChildCountColor(RemoteViews contentView) {
-            contentView.setTextColor(R.id.number_of_children, resolveColor());
-        }
-
         private void bindContentInfo(RemoteViews contentView) {
             boolean visible = false;
             if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b4e9f60..c684447 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -120,6 +120,8 @@
 
     int checkUidSignatures(int uid1, int uid2);
 
+    List<String> getAllPackages();
+
     String[] getPackagesForUid(int uid);
 
     String getNameForUid(int uid);
@@ -380,6 +382,13 @@
      */
     void clearApplicationUserData(in String packageName, IPackageDataObserver observer, int userId);
 
+    /**
+     * Clear the profile data of an application.
+     * @param packageName The package name of the application whose profile data
+     * need to be deleted
+     */
+    void clearApplicationProfileData(in String packageName);
+
    /**
      * Get package statistics including the code, data and cache size for
      * an already installed package
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 9a80e37..5eb8cc2 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -16,12 +16,14 @@
 
 package android.print;
 
+import android.content.ComponentName;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.print.IPrinterDiscoveryObserver;
 import android.print.IPrintDocumentAdapter;
 import android.print.PrintJobId;
 import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintServicesChangeListener;
 import android.print.PrinterId;
 import android.print.PrintJobInfo;
 import android.print.PrintAttributes;
@@ -45,8 +47,47 @@
     void removePrintJobStateChangeListener(in IPrintJobStateChangeListener listener,
             int userId);
 
-    List<PrintServiceInfo> getInstalledPrintServices(int userId);
-    List<PrintServiceInfo> getEnabledPrintServices(int userId);
+    /**
+     * Listen for changes to the installed and enabled print services.
+     *
+     * @param listener the listener to add
+     * @param userId the id of the user listening
+     *
+     * @see android.print.PrintManager#getPrintServices(int, String)
+     */
+    void addPrintServicesChangeListener(in IPrintServicesChangeListener listener,
+            int userId);
+
+    /**
+     * Stop listening for changes to the installed and enabled print services.
+     *
+     * @param listener the listener to remove
+     * @param userId the id of the user requesting the removal
+     *
+     * @see android.print.PrintManager#getPrintServices(int, String)
+     */
+    void removePrintServicesChangeListener(in IPrintServicesChangeListener listener,
+            int userId);
+
+    /**
+     * Get the print services.
+     *
+     * @param selectionFlags flags selecting which services to get
+     * @param selectedService if not null, the id of the print service to get
+     * @param userId the id of the user requesting the services
+     *
+     * @return the list of selected print services.
+     */
+    List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId);
+
+    /**
+     * Enable or disable a print service.
+     *
+     * @param service The service to enabled or disable
+     * @param isEnabled whether the service should be enabled or disabled
+     * @param userId the id of the user requesting the services
+     */
+    void setPrintServiceEnabled(in ComponentName service, boolean isEnabled, int userId);
 
     void createPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId);
     void startPrinterDiscovery(in IPrinterDiscoveryObserver observer,
diff --git a/core/java/android/print/IPrintServicesChangeListener.aidl b/core/java/android/print/IPrintServicesChangeListener.aidl
new file mode 100644
index 0000000..2a7ce89
--- /dev/null
+++ b/core/java/android/print/IPrintServicesChangeListener.aidl
@@ -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 android.print;
+
+/**
+ * Interface for observing print services changes.
+ *
+ * @hide
+ */
+oneway interface IPrintServicesChangeListener {
+    void onPrintServicesChanged();
+}
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index 0540036..25fc968 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.app.Activity;
 import android.app.Application.ActivityLifecycleCallbacks;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
@@ -111,6 +112,38 @@
     private static final boolean DEBUG = false;
 
     private static final int MSG_NOTIFY_PRINT_JOB_STATE_CHANGED = 1;
+    private static final int MSG_NOTIFY_PRINT_SERVICES_CHANGED = 2;
+
+    /**
+     * Package name of print spooler.
+     *
+     * @hide
+     */
+    public static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+
+    /**
+     * Select enabled services.
+     * </p>
+     * @see #getPrintServices
+     * @hide
+     */
+    public static final int ENABLED_SERVICES = 1 << 0;
+
+    /**
+     * Select disabled services.
+     * </p>
+     * @see #getPrintServices
+     * @hide
+     */
+    public static final int DISABLED_SERVICES = 1 << 1;
+
+    /**
+     * Select all services.
+     * </p>
+     * @see #getPrintServices
+     * @hide
+     */
+    public static final int ALL_SERVICES = ENABLED_SERVICES | DISABLED_SERVICES;
 
     /**
      * The action for launching the print dialog activity.
@@ -165,7 +198,10 @@
 
     private final Handler mHandler;
 
-    private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper> mPrintJobStateChangeListeners;
+    private Map<PrintJobStateChangeListener, PrintJobStateChangeListenerWrapper>
+            mPrintJobStateChangeListeners;
+    private Map<PrintServicesChangeListener, PrintServicesChangeListenerWrapper>
+            mPrintServicesChangeListeners;
 
     /** @hide */
     public interface PrintJobStateChangeListener {
@@ -178,6 +214,15 @@
         public void onPrintJobStateChanged(PrintJobId printJobId);
     }
 
+    /** @hide */
+    public interface PrintServicesChangeListener {
+
+        /**
+         * Callback notifying that the print services changed.
+         */
+        public void onPrintServicesChanged();
+    }
+
     /**
      * Creates a new instance.
      *
@@ -207,6 +252,15 @@
                         }
                         args.recycle();
                     } break;
+                    case MSG_NOTIFY_PRINT_SERVICES_CHANGED: {
+                        PrintServicesChangeListenerWrapper wrapper =
+                                (PrintServicesChangeListenerWrapper) message.obj;
+                        PrintServicesChangeListener listener = wrapper.getListener();
+                        if (listener != null) {
+                            listener.onPrintServicesChanged();
+                        }
+                    } break;
+
                 }
             }
         };
@@ -478,42 +532,82 @@
     }
 
     /**
-     * Gets the list of enabled print services.
+     * Listen for changes to the installed and enabled print services.
      *
-     * @return The enabled service list or an empty list.
-     * @hide
+     * @param listener the listener to add
+     *
+     * @see android.print.PrintManager#getPrintServices
      */
-    public List<PrintServiceInfo> getEnabledPrintServices() {
+    void addPrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
         if (mService == null) {
             Log.w(LOG_TAG, "Feature android.software.print not available");
-            return Collections.emptyList();
+            return;
         }
+        if (mPrintServicesChangeListeners == null) {
+            mPrintServicesChangeListeners = new ArrayMap<PrintServicesChangeListener,
+                    PrintServicesChangeListenerWrapper>();
+        }
+        PrintServicesChangeListenerWrapper wrappedListener =
+                new PrintServicesChangeListenerWrapper(listener, mHandler);
         try {
-            List<PrintServiceInfo> enabledServices = mService.getEnabledPrintServices(mUserId);
-            if (enabledServices != null) {
-                return enabledServices;
-            }
+            mService.addPrintServicesChangeListener(wrappedListener, mUserId);
+            mPrintServicesChangeListeners.put(listener, wrappedListener);
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
         }
-        return Collections.emptyList();
     }
 
     /**
-     * Gets the list of installed print services.
+     * Stop listening for changes to the installed and enabled print services.
      *
-     * @return The installed service list or an empty list.
-     * @hide
+     * @param listener the listener to remove
+     *
+     * @see android.print.PrintManager#getPrintServices
      */
-    public List<PrintServiceInfo> getInstalledPrintServices() {
+    void removePrintServicesChangeListener(@NonNull PrintServicesChangeListener listener) {
         if (mService == null) {
             Log.w(LOG_TAG, "Feature android.software.print not available");
-            return Collections.emptyList();
+            return;
         }
+        if (mPrintServicesChangeListeners == null) {
+            return;
+        }
+        PrintServicesChangeListenerWrapper wrappedListener =
+                mPrintServicesChangeListeners.remove(listener);
+        if (wrappedListener == null) {
+            return;
+        }
+        if (mPrintServicesChangeListeners.isEmpty()) {
+            mPrintServicesChangeListeners = null;
+        }
+        wrappedListener.destroy();
         try {
-            List<PrintServiceInfo> installedServices = mService.getInstalledPrintServices(mUserId);
-            if (installedServices != null) {
-                return installedServices;
+            mService.removePrintServicesChangeListener(wrappedListener, mUserId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error removing print services change listener", re);
+        }
+    }
+
+
+    /**
+     * Gets the list of print services, but does not register for updates. The user has to register
+     * for updates by itself, or use {@link PrintServicesLoader}.
+     *
+     * @param selectionFlags flags selecting which services to get. Either
+     *                       {@link #ENABLED_SERVICES},{@link #DISABLED_SERVICES}, or both.
+     *
+     * @return The enabled service list or an empty list.
+     *
+     * @see #addPrintServicesChangeListener(PrintServicesChangeListener)
+     * @see #removePrintServicesChangeListener(PrintServicesChangeListener)
+     *
+     * @hide
+     */
+    public @NonNull List<PrintServiceInfo> getPrintServices(int selectionFlags) {
+        try {
+            List<PrintServiceInfo> services = mService.getPrintServices(selectionFlags, mUserId);
+            if (services != null) {
+                return services;
             }
         } catch (RemoteException re) {
             throw re.rethrowFromSystemServer();
@@ -533,6 +627,26 @@
     }
 
     /**
+     * Enable or disable a print service.
+     *
+     * @param service The service to enabled or disable
+     * @param isEnabled whether the service should be enabled or disabled
+     *
+     * @hide
+     */
+    public void setPrintServiceEnabled(@NonNull ComponentName service, boolean isEnabled) {
+        if (mService == null) {
+            Log.w(LOG_TAG, "Feature android.software.print not available");
+            return;
+        }
+        try {
+            mService.setPrintServiceEnabled(service, isEnabled, mUserId);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error enabling or disabling " + service, re);
+        }
+    }
+
+    /**
      * @hide
      */
     public static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub
@@ -1096,4 +1210,36 @@
             return mWeakListener.get();
         }
     }
+
+    /**
+     * @hide
+     */
+    public static final class PrintServicesChangeListenerWrapper extends
+            IPrintServicesChangeListener.Stub {
+        private final WeakReference<PrintServicesChangeListener> mWeakListener;
+        private final WeakReference<Handler> mWeakHandler;
+
+        public PrintServicesChangeListenerWrapper(PrintServicesChangeListener listener,
+                Handler handler) {
+            mWeakListener = new WeakReference<>(listener);
+            mWeakHandler = new WeakReference<>(handler);
+        }
+
+        @Override
+        public void onPrintServicesChanged() {
+            Handler handler = mWeakHandler.get();
+            PrintServicesChangeListener listener = mWeakListener.get();
+            if (handler != null && listener != null) {
+                handler.obtainMessage(MSG_NOTIFY_PRINT_SERVICES_CHANGED, this).sendToTarget();
+            }
+        }
+
+        public void destroy() {
+            mWeakListener.clear();
+        }
+
+        public PrintServicesChangeListener getListener() {
+            return mWeakListener.get();
+        }
+    }
 }
diff --git a/core/java/android/print/PrintServicesLoader.java b/core/java/android/print/PrintServicesLoader.java
new file mode 100644
index 0000000..ed41114
--- /dev/null
+++ b/core/java/android/print/PrintServicesLoader.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 android.print;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Handler;
+import android.os.Message;
+import android.printservice.PrintServiceInfo;
+
+import java.util.List;
+
+/**
+ * Loader for the list of print services. Can be parametrized to select a subset.
+ *
+ * @hide
+ */
+public class PrintServicesLoader extends Loader<List<PrintServiceInfo>> {
+    /** What type of services to load. */
+    private final int mSelectionFlags;
+
+    /** The print manager to be used by this object */
+    private final @NonNull PrintManager mPrintManager;
+
+    /** Handler to sequentialize the delivery of the results to the main thread */
+    private Handler mHandler;
+
+    /** Listens for updates to the data from the platform */
+    private PrintManager.PrintServicesChangeListener mListener;
+
+    /**
+     * Create a new PrintServicesLoader.
+     *
+     * @param selectionFlags What type of services to load.
+     */
+    public PrintServicesLoader(@NonNull PrintManager printManager, @NonNull Context context,
+            int selectionFlags) {
+        super(context);
+        mPrintManager = printManager;
+        mSelectionFlags = selectionFlags;
+    }
+
+    @Override
+    protected void onForceLoad() {
+        queueNewResult();
+    }
+
+    /**
+     * Read the print services and queue it to be delivered on the main thread.
+     */
+    private void queueNewResult() {
+        Message m = mHandler.obtainMessage(0);
+        m.obj = mPrintManager.getPrintServices(mSelectionFlags);
+        mHandler.sendMessage(m);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        mHandler = new MyHandler();
+        mListener = new PrintManager.PrintServicesChangeListener() {
+            @Override public void onPrintServicesChanged() {
+                queueNewResult();
+            }
+        };
+
+        mPrintManager.addPrintServicesChangeListener(mListener);
+
+        // Immediately deliver a result
+        deliverResult(mPrintManager.getPrintServices(mSelectionFlags));
+    }
+
+    @Override
+    protected void onStopLoading() {
+        if (mListener != null) {
+            mPrintManager.removePrintServicesChangeListener(mListener);
+            mListener = null;
+        }
+
+        if (mHandler != null) {
+            mHandler.removeMessages(0);
+            mHandler = null;
+        }
+    }
+
+    @Override
+    protected void onReset() {
+        onStopLoading();
+    }
+
+    /**
+     * Handler to sequentialize all the updates to the main thread.
+     */
+    private class MyHandler extends Handler {
+        /**
+         * Create a new handler on the main thread.
+         */
+        public MyHandler() {
+            super(getContext().getMainLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            super.handleMessage(msg);
+
+            if (isStarted()) {
+                deliverResult((List<PrintServiceInfo>) msg.obj);
+            }
+        }
+    }
+}
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 91e01f2..45e3d47 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -55,6 +55,8 @@
 
     private final String mId;
 
+    private boolean mIsEnabled;
+
     private final ResolveInfo mResolveInfo;
 
     private final String mSettingsActivityName;
@@ -70,6 +72,7 @@
      */
     public PrintServiceInfo(Parcel parcel) {
         mId = parcel.readString();
+        mIsEnabled = parcel.readByte() != 0;
         mResolveInfo = parcel.readParcelable(null);
         mSettingsActivityName = parcel.readString();
         mAddPrintersActivityName = parcel.readString();
@@ -180,6 +183,24 @@
     }
 
     /**
+     * If the service was enabled when it was read from the system.
+     *
+     * @return The id.
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * Mark a service as enabled or not
+     *
+     * @param isEnabled If the service should be marked as enabled.
+     */
+    public void setIsEnabled(boolean isEnabled) {
+        mIsEnabled = isEnabled;
+    }
+
+    /**
      * The service {@link ResolveInfo}.
      *
      * @return The info.
@@ -238,6 +259,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flagz) {
         parcel.writeString(mId);
+        parcel.writeByte((byte)(mIsEnabled ? 1 : 0));
         parcel.writeParcelable(mResolveInfo, 0);
         parcel.writeString(mSettingsActivityName);
         parcel.writeString(mAddPrintersActivityName);
@@ -276,6 +298,7 @@
         StringBuilder builder = new StringBuilder();
         builder.append("PrintServiceInfo{");
         builder.append("id=").append(mId);
+        builder.append("isEnabled=").append(mIsEnabled);
         builder.append(", resolveInfo=").append(mResolveInfo);
         builder.append(", settingsActivityName=").append(mSettingsActivityName);
         builder.append(", addPrintersActivityName=").append(mAddPrintersActivityName);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a40cf96..d43ff4e 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1254,6 +1254,19 @@
     public static final String ACTION_SHOW_REMOTE_BUGREPORT_DIALOG
             = "android.settings.SHOW_REMOTE_BUGREPORT_DIALOG";
 
+    /**
+     * Activity Action: Show VR listener settings.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @see android.service.vr.VrListenerService
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_VR_LISTENER_SETTINGS
+            = "android.settings.VR_LISTENER_SETTINGS";
+
     // End of Intent actions for Settings
 
     /**
@@ -5854,15 +5867,6 @@
         public static final String ASSIST_SCREENSHOT_ENABLED = "assist_screenshot_enabled";
 
         /**
-         * Names of the service component that the current user has explicitly allowed to
-         * see and change the importance of all of the user's notifications.
-         *
-         * @hide
-         */
-        public static final String ENABLED_NOTIFICATION_ASSISTANT
-                = "enabled_notification_assistant";
-
-        /**
          * Names of the service components that the current user has explicitly allowed to
          * see all of the user's notifications, separated by ':'.
          *
@@ -6041,6 +6045,14 @@
         public static final String BRIGHTNESS_USE_TWILIGHT = "brightness_use_twilight";
 
         /**
+         * Names of the service components that the current user has explicitly allowed to
+         * be a VR mode listener, separated by ':'.
+         *
+         * @hide
+         */
+        public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -6067,6 +6079,7 @@
             BACKUP_AUTO_RESTORE,
             ENABLED_ACCESSIBILITY_SERVICES,
             ENABLED_NOTIFICATION_LISTENERS,
+            ENABLED_VR_LISTENERS,
             ENABLED_INPUT_METHODS,
             TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
             TOUCH_EXPLORATION_ENABLED,
@@ -8260,6 +8273,13 @@
                 "allow_user_switching_when_system_user_locked";
 
         /**
+         * Boot count since the device starts running APK level 24.
+         * <p>
+         * Type: int
+         */
+        public static final String BOOT_COUNT = "boot_count";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index a0de17f..8c35901 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,7 +23,7 @@
 /** @hide */
 oneway interface INotificationListener
 {
-    // listeners and assistants
+    // listeners and rankers
     void onListenerConnected(in NotificationRankingUpdate update);
     void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
             in NotificationRankingUpdate update);
@@ -33,7 +33,7 @@
     void onListenerHintsChanged(int hints);
     void onInterruptionFilterChanged(int interruptionFilter);
 
-    // assistants only
+    // rankers only
     void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
     void onNotificationVisibilityChanged(String key, long time, boolean visible);
     void onNotificationClick(String key, long time);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 73a890f..a8b8da1 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -69,16 +69,6 @@
  *         &lt;action android:name="android.service.notification.NotificationListenerService" />
  *     &lt;/intent-filter>
  * &lt;/service></pre>
- * <p> Typically, while enabled in user settings, this service will be bound on boot or when a
- * settings change occurs that could affect whether this service should run.  However, for some
- * system usage modes, the you may instead specify that this service is instead bound and unbound
- * in response to mode changes by including a category in the intent filter.  Currently
- * supported categories are:
- * <ul>
- *   <li>{@link #CATEGORY_VR_NOTIFICATIONS} - this service is bound when an Activity has enabled
- *   VR mode. {@see android.app.Activity#setVrMode(boolean)}.</li>
- * </ul>
- * </p>
  */
 public abstract class NotificationListenerService extends Service {
     // TAG = "NotificationListenerService[MySubclass]"
@@ -180,13 +170,18 @@
 
     private INotificationManager mNoMan;
 
-    /** Only valid after a successful call to (@link registerAsService}. */
-    private int mCurrentUser;
+    /**
+     * Only valid after a successful call to (@link registerAsService}.
+     * @hide
+     */
+    protected int mCurrentUser;
 
-
-    // This context is required for system services since NotificationListenerService isn't
-    // started as a real Service and hence no context is available.
-    private Context mSystemContext;
+    /**
+     * This context is required for system services since NotificationListenerService isn't
+     * started as a real Service and hence no context is available..
+     * @hide
+     */
+    protected Context mSystemContext;
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -195,17 +190,6 @@
     public static final String SERVICE_INTERFACE
             = "android.service.notification.NotificationListenerService";
 
-    /**
-     * If this category is declared in the application manifest for a service of this type, this
-     * service will be bound when VR mode is enabled, and unbound when VR mode is disabled rather
-     * than the normal lifecycle for a notification service.
-     *
-     * {@see android.app.Activity#setVrMode(boolean)}
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.INTENT_CATEGORY)
-    public static final String CATEGORY_VR_NOTIFICATIONS =
-        "android.intent.category.vr.notifications";
-
     @Override
     protected void attachBaseContext(Context base) {
         super.attachBaseContext(base);
@@ -696,12 +680,18 @@
     @SystemApi
     public void registerAsSystemService(Context context, ComponentName componentName,
             int currentUser) throws RemoteException {
+        registerAsSystemServiceImpl(context, componentName, currentUser, false /* asRanker */);
+    }
+
+    /** @hide */
+    protected void registerAsSystemServiceImpl(Context context, ComponentName componentName,
+            int currentUser, boolean asRanker) throws RemoteException {
         mSystemContext = context;
         if (mWrapper == null) {
             mWrapper = new NotificationListenerWrapper();
         }
         INotificationManager noMan = getNotificationInterface();
-        noMan.registerListener(mWrapper, componentName, currentUser);
+        noMan.registerListener(mWrapper, componentName, currentUser, asRanker);
         mCurrentUser = currentUser;
         mHandler = new MyHandler(context.getMainLooper());
     }
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationRankerService.java
similarity index 90%
rename from core/java/android/service/notification/NotificationAssistantService.java
rename to core/java/android/service/notification/NotificationRankerService.java
index 41af837..520b4c2 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -31,31 +31,20 @@
 import com.android.internal.os.SomeArgs;
 
 /**
- * A service that helps the user manage notifications by modifying the
- * relative importance of notifications.
- * <p>To extend this class, you must declare the service in your manifest file with
- * the {@link android.Manifest.permission#BIND_NOTIFICATION_ASSISTANT_SERVICE} permission
- * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
- * <pre>
- * &lt;service android:name=".NotificationAssistant"
- *          android:label="&#64;string/service_name"
- *          android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE">
- *     &lt;intent-filter>
- *         &lt;action android:name="android.service.notification.NotificationAssistantService" />
- *     &lt;/intent-filter>
- * &lt;/service></pre>
+ * A service that helps the user manage notifications. This class is only used to
+ * extend the framework service and may not be implemented by non-framework components.
  * @hide
  */
 @SystemApi
-public abstract class NotificationAssistantService extends NotificationListenerService {
-    private static final String TAG = "NotificationAssistant";
+public abstract class NotificationRankerService extends NotificationListenerService {
+    private static final String TAG = "NotificationRanker";
 
     /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     public static final String SERVICE_INTERFACE
-            = "android.service.notification.NotificationAssistantService";
+            = "android.service.notification.NotificationRankerService";
 
     /** Notification was canceled by the status bar reporting a click. */
     public static final int REASON_DELEGATE_CLICK = 1;
@@ -130,7 +119,7 @@
     @Override
     public void registerAsSystemService(Context context, ComponentName componentName,
             int currentUser) throws RemoteException {
-        super.registerAsSystemService(context, componentName, currentUser);
+        registerAsSystemServiceImpl(context, componentName, currentUser, true /* as Ranker */);
         mHandler = new MyHandler(getContext().getMainLooper());
     }
 
@@ -143,7 +132,7 @@
     @Override
     public final IBinder onBind(Intent intent) {
         if (mWrapper == null) {
-            mWrapper = new NotificationAssistantWrapper();
+            mWrapper = new NotificationRankingServiceWrapper();
         }
         return mWrapper;
     }
@@ -216,14 +205,14 @@
     public final void adjustImportance(String key, Adjustment adjustment) {
         if (!isBound()) return;
         try {
-            getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
+            getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
                     adjustment.mImportance, adjustment.mExplanation);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
     }
 
-    private class NotificationAssistantWrapper extends NotificationListenerWrapper {
+    private class NotificationRankingServiceWrapper extends NotificationListenerWrapper {
         @Override
         public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
                 int importance, boolean user) {
diff --git a/core/java/android/service/vr/IVrListener.aidl b/core/java/android/service/vr/IVrListener.aidl
new file mode 100644
index 0000000..b7273ba
--- /dev/null
+++ b/core/java/android/service/vr/IVrListener.aidl
@@ -0,0 +1,22 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+/** @hide */
+oneway interface IVrListener {
+
+}
\ No newline at end of file
diff --git a/core/java/android/service/vr/VrListenerService.java b/core/java/android/service/vr/VrListenerService.java
new file mode 100644
index 0000000..5f1f659
--- /dev/null
+++ b/core/java/android/service/vr/VrListenerService.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * A service that is bound from the system while running in virtual reality (VR) mode.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with
+ * the {@link android.Manifest.permission#BIND_VR_LISTENER_SERVICE} permission
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
+ * <pre>
+ * &lt;service android:name=".VrListener"
+ *          android:label="&#64;string/service_name"
+ *          android:permission="android.permission.BIND_VR_LISTENER_SERVICE">
+ *     &lt;intent-filter>
+ *         &lt;action android:name="android.service.vr.VrListenerService" />
+ *     &lt;/intent-filter>
+ * &lt;/service>
+ * </pre>
+ *
+ * <p>
+ * This service is bound when the system enters VR mode and is unbound when the system leaves VR
+ * mode.
+ * {@see android.app.Activity#setVrMode(boolean)}
+ * </p>
+ */
+public abstract class VrListenerService extends Service {
+
+    /**
+     * The {@link Intent} that must be declared as handled by the service.
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String SERVICE_INTERFACE = "android.service.vr.VrListenerService";
+
+    /**
+     * @hide
+     */
+    public static class VrListenerBinder extends IVrListener.Stub {
+    }
+
+    private final VrListenerBinder mBinder = new VrListenerBinder();
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+
+    /**
+     * Check if the given package is available to be enabled/disabled in VR mode settings.
+     *
+     * @param context the {@link Context} to use for looking up the requested component.
+     * @param requestedComponent the name of the component that implements
+     * {@link android.service.vr.VrListenerService} to check.
+     *
+     * @return {@code true} if this package is enabled in settings.
+     */
+    public static final boolean isVrModePackageEnabled(@NonNull Context context,
+            @NonNull ComponentName requestedComponent) {
+        ActivityManager am = context.getSystemService(ActivityManager.class);
+        if (am == null) {
+            return false;
+        }
+        return am.isVrModePackageEnabled(requestedComponent);
+    }
+}
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 9af65e4..2645f86 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -47,6 +47,7 @@
     private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     private CalculateSlide mSlideCalculator = sCalculateBottom;
     private @GravityFlag int mSlideEdge = Gravity.BOTTOM;
+    private float mSlideFraction = 1;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -56,16 +57,16 @@
     private interface CalculateSlide {
 
         /** Returns the translation value for view when it goes out of the scene */
-        float getGoneX(ViewGroup sceneRoot, View view);
+        float getGoneX(ViewGroup sceneRoot, View view, float fraction);
 
         /** Returns the translation value for view when it goes out of the scene */
-        float getGoneY(ViewGroup sceneRoot, View view);
+        float getGoneY(ViewGroup sceneRoot, View view, float fraction);
     }
 
     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
 
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
             return view.getTranslationY();
         }
     }
@@ -73,27 +74,27 @@
     private static abstract class CalculateSlideVertical implements CalculateSlide {
 
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             return view.getTranslationX();
         }
     }
 
     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
-            return view.getTranslationX() - sceneRoot.getWidth();
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationX() - sceneRoot.getWidth() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() + sceneRoot.getWidth();
+                x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
             } else {
-                x = view.getTranslationX() - sceneRoot.getWidth();
+                x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
             }
             return x;
         }
@@ -101,27 +102,27 @@
 
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
-            return view.getTranslationY() - sceneRoot.getHeight();
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationY() - sceneRoot.getHeight() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
-            return view.getTranslationX() + sceneRoot.getWidth();
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationX() + sceneRoot.getWidth() * fraction;
         }
     };
 
     private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
         @Override
-        public float getGoneX(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view, float fraction) {
             final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
             final float x;
             if (isRtl) {
-                x = view.getTranslationX() - sceneRoot.getWidth();
+                x = view.getTranslationX() - sceneRoot.getWidth() * fraction;
             } else {
-                x = view.getTranslationX() + sceneRoot.getWidth();
+                x = view.getTranslationX() + sceneRoot.getWidth() * fraction;
             }
             return x;
         }
@@ -129,8 +130,8 @@
 
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
-        public float getGoneY(ViewGroup sceneRoot, View view) {
-            return view.getTranslationY() + sceneRoot.getHeight();
+        public float getGoneY(ViewGroup sceneRoot, View view, float fraction) {
+            return view.getTranslationY() + sceneRoot.getHeight() * fraction;
         }
     };
 
@@ -237,8 +238,8 @@
         int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
         float endX = view.getTranslationX();
         float endY = view.getTranslationY();
-        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
-        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+        float startX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+        float startY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
         return TranslationAnimationCreator
                 .createAnimation(view, endValues, position[0], position[1],
                         startX, startY, endX, endY, sDecelerate, this);
@@ -253,10 +254,15 @@
         int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
         float startX = view.getTranslationX();
         float startY = view.getTranslationY();
-        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
-        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+        float endX = mSlideCalculator.getGoneX(sceneRoot, view, mSlideFraction);
+        float endY = mSlideCalculator.getGoneY(sceneRoot, view, mSlideFraction);
         return TranslationAnimationCreator
                 .createAnimation(view, startValues, position[0], position[1],
                         startX, startY, endX, endY, sAccelerate, this);
     }
+
+    /** @hide */
+    public void setSlideFraction(float slideFraction) {
+        mSlideFraction = slideFraction;
+    }
 }
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index b9a7421..cff9d8e 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -35,7 +35,6 @@
 public class NotificationHeaderView extends ViewGroup {
     public static final int NO_COLOR = -1;
     private final int mChildMinWidth;
-    private final int mExpandTopPadding;
     private final int mContentEndMargin;
     private View mAppName;
     private View mSubTextView;
@@ -43,12 +42,10 @@
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private ImageView mExpandButton;
     private View mIcon;
-    private TextView mChildCount;
     private View mProfileBadge;
     private View mInfo;
     private int mIconColor;
     private int mOriginalNotificationColor;
-    private boolean mGroupHeader;
     private boolean mExpanded;
     private boolean mShowWorkBadgeAtEnd;
 
@@ -70,7 +67,6 @@
                 com.android.internal.R.dimen.notification_header_shrink_min_width);
         mContentEndMargin = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.notification_content_margin_end);
-        mExpandTopPadding = (int) (1 * getResources().getDisplayMetrics().density);
     }
 
     @Override
@@ -80,7 +76,6 @@
         mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
         mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
-        mChildCount = (TextView) findViewById(com.android.internal.R.id.number_of_children);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
         mInfo = findViewById(com.android.internal.R.id.header_content_info);
     }
@@ -193,17 +188,6 @@
         updateTouchListener();
     }
 
-    public void setChildCount(int childCount) {
-        if (childCount > 0) {
-            mChildCount.setText(getContext().getString(
-                    com.android.internal.R.string.notification_children_count_bracketed,
-                    childCount));
-            mChildCount.setVisibility(VISIBLE);
-        } else {
-            mChildCount.setVisibility(GONE);
-        }
-    }
-
     @RemotableViewMethod
     public void setOriginalIconColor(int color) {
         mIconColor = color;
@@ -222,11 +206,6 @@
         return mOriginalNotificationColor;
     }
 
-    public void setIsGroupHeader(boolean isGroupHeader) {
-        mGroupHeader = isGroupHeader;
-        updateExpandButton();
-    }
-
     @RemotableViewMethod
     public void setExpanded(boolean expanded) {
         mExpanded = expanded;
@@ -235,24 +214,13 @@
 
     private void updateExpandButton() {
         int drawableId;
-        int paddingTop = 0;
-        if (mGroupHeader) {
-            if (mExpanded) {
-                drawableId = com.android.internal.R.drawable.ic_collapse_bundle;
-            } else {
-                drawableId =com.android.internal.R.drawable.ic_expand_bundle;
-            }
+        if (mExpanded) {
+            drawableId = com.android.internal.R.drawable.ic_collapse_notification;
         } else {
-            if (mExpanded) {
-                drawableId = com.android.internal.R.drawable.ic_collapse_notification;
-            } else {
-                drawableId = com.android.internal.R.drawable.ic_expand_notification;
-            }
-            paddingTop = mExpandTopPadding;
+            drawableId = com.android.internal.R.drawable.ic_expand_notification;
         }
         mExpandButton.setImageDrawable(getContext().getDrawable(drawableId));
         mExpandButton.setColorFilter(mOriginalNotificationColor);
-        mExpandButton.setPadding(0, paddingTop, 0, 0);
     }
 
     public void setShowWorkBadgeAtEnd(boolean showWorkBadgeAtEnd) {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0e34067..406dc2b 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -215,7 +215,7 @@
 
     boolean mInBatchEditControllers;
     boolean mShowSoftInputOnFocus = true;
-    private boolean mPreserveDetachedSelection;
+    private boolean mPreserveSelection;
     boolean mTemporaryDetach;
 
     boolean mIsBeingLongClicked;
@@ -351,9 +351,14 @@
     }
 
     void replace() {
+        if (mSuggestionsPopupWindow == null) {
+            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
+        }
+        hideCursorAndSpanControllers();
+        mSuggestionsPopupWindow.show();
+
         int middle = (mTextView.getSelectionStart() + mTextView.getSelectionEnd()) / 2;
         Selection.setSelection((Spannable) mTextView.getText(), middle);
-        showSuggestions();
     }
 
     void onAttachedToWindow() {
@@ -376,13 +381,7 @@
         updateSpellCheckSpans(0, mTextView.getText().length(),
                 true /* create the spell checker if needed */);
 
-        if (mTextView.hasTransientState() &&
-                mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
-            // Since transient state is reference counted make sure it stays matched
-            // with our own calls to it for managing selection.
-            // The action mode callback will set this back again when/if the action mode starts.
-            mTextView.setHasTransientState(false);
-
+        if (mTextView.getSelectionStart() != mTextView.getSelectionEnd()) {
             // We had an active selection from before, start the selection mode.
             startSelectionActionMode();
         }
@@ -2092,7 +2091,7 @@
 
                     mShowSuggestionRunnable = new Runnable() {
                         public void run() {
-                            showSuggestions();
+                            replace();
                         }
                     };
                     // removeCallbacks is performed on every touch
@@ -2113,9 +2112,9 @@
     }
 
     private void stopTextActionModeWithPreservingSelection() {
-        mPreserveDetachedSelection = true;
+        mPreserveSelection = true;
         stopTextActionMode();
-        mPreserveDetachedSelection = false;
+        mPreserveSelection = false;
     }
 
     /**
@@ -2233,15 +2232,6 @@
         mCorrectionHighlighter.highlight(info);
     }
 
-    void showSuggestions() {
-        if (mSuggestionsPopupWindow == null) {
-            mSuggestionsPopupWindow = new SuggestionsPopupWindow();
-        }
-        hideCursorAndSpanControllers();
-        stopTextActionMode();
-        mSuggestionsPopupWindow.show();
-    }
-
     void onScrollChanged() {
         if (mPositionListener != null) {
             mPositionListener.onScrollChanged();
@@ -2515,7 +2505,7 @@
                 .setEnabled(mTextView.canSelectAllText())
                 .setOnMenuItemClickListener(mOnContextMenuItemClickListener);
 
-        mPreserveDetachedSelection = true;
+        mPreserveSelection = true;
     }
 
     private void replaceWithSuggestion(final SuggestionInfo suggestionInfo) {
@@ -3426,7 +3416,7 @@
 
         @Override
         protected int getTextOffset() {
-            return mTextView.getSelectionStart();
+            return (mTextView.getSelectionStart() + mTextView.getSelectionStart()) / 2;
         }
 
         @Override
@@ -3590,7 +3580,6 @@
             }
 
             if (menu.hasVisibleItems() || mode.getCustomView() != null) {
-                mTextView.setHasTransientState(true);
                 return true;
             } else {
                 return false;
@@ -3690,15 +3679,14 @@
                 customCallback.onDestroyActionMode(mode);
             }
 
-            /*
-             * If we're ending this mode because we're detaching from a window,
-             * we still have selection state to preserve. Don't clear it, we'll
-             * bring back the selection mode when (if) we get reattached.
-             */
-            if (!mPreserveDetachedSelection) {
+            if (!mPreserveSelection) {
+                /*
+                 * Leave current selection when we tentatively destroy action mode for the
+                 * selection. If we're detaching from a window, we'll bring back the selection
+                 * mode when (if) we get reattached.
+                 */
                 Selection.setSelection((Spannable) mTextView.getText(),
                         mTextView.getSelectionEnd());
-                mTextView.setHasTransientState(false);
             }
 
             if (mSelectionModifierCursorController != null) {
@@ -6037,7 +6025,7 @@
         private boolean fireIntent(Intent intent) {
             if (intent != null && Intent.ACTION_PROCESS_TEXT.equals(intent.getAction())) {
                 intent.putExtra(Intent.EXTRA_PROCESS_TEXT, mTextView.getSelectedText());
-                mEditor.mPreserveDetachedSelection = true;
+                mEditor.mPreserveSelection = true;
                 mTextView.startActivityForResult(intent, TextView.PROCESS_TEXT_REQUEST_CODE);
                 return true;
             }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index df2f575..dd6a41f 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1651,10 +1651,11 @@
         // to executing this method, so we can rely on that instead.
         final Transition exitTransition = mExitTransition;
         if (mIsAnchorRootAttached && exitTransition != null && decorView.isLaidOut()) {
-            // The decor view is non-interactive during exit transitions.
+            // The decor view is non-interactive and non-IME-focusable during exit transitions.
             final LayoutParams p = (LayoutParams) decorView.getLayoutParams();
             p.flags |= LayoutParams.FLAG_NOT_TOUCHABLE;
             p.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
+            p.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
             mWindowManager.updateViewLayout(decorView, p);
 
             // Once we start dismissing the decor view, all state (including
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 73f8fdc..ac3eaf7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8286,6 +8286,13 @@
                 if (newSelEnd < 0) {
                     newSelEnd = Selection.getSelectionEnd(buf);
                 }
+
+                if (newSelStart == newSelEnd && hasTransientState()) {
+                    setHasTransientState(false);
+                } else if (newSelStart != newSelEnd && !hasTransientState()) {
+                    setHasTransientState(true);
+                }
+
                 if (mEditor != null) {
                     mEditor.refreshTextActionMode();
                 }
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index 2ea225f..ea62899 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -21,6 +21,7 @@
 import android.app.ListFragment;
 import android.content.Context;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.LocaleList;
 import android.view.Menu;
 import android.view.MenuInflater;
@@ -50,6 +51,11 @@
     private Set<LocaleStore.LocaleInfo> mLocaleList;
     private LocaleStore.LocaleInfo mParentLocale;
     private boolean mTranslatedOnly = false;
+    private SearchView mSearchView = null;
+    private CharSequence mPreviousSearch = null;
+    private boolean mPreviousSearchHadFocus = false;
+    private int mFirstVisiblePosition = 0;
+    private int mTopDistance = 0;
 
     /**
      * Other classes can register to be notified when a locale was selected.
@@ -154,15 +160,35 @@
         super.onResume();
 
         if (mParentLocale != null) {
-            this.getActivity().setTitle(mParentLocale.getFullNameNative());
+            getActivity().setTitle(mParentLocale.getFullNameNative());
         } else {
-            this.getActivity().setTitle(R.string.language_selection_title);
+            getActivity().setTitle(R.string.language_selection_title);
         }
 
         getListView().requestFocus();
     }
 
     @Override
+    public void onPause() {
+        super.onPause();
+
+        // Save search status
+        if (mSearchView != null) {
+            mPreviousSearchHadFocus = mSearchView.hasFocus();
+            mPreviousSearch = mSearchView.getQuery();
+        } else {
+            mPreviousSearchHadFocus = false;
+            mPreviousSearch = null;
+        }
+
+        // Save scroll position
+        final ListView list = getListView();
+        final View firstChild = list.getChildAt(0);
+        mFirstVisiblePosition = list.getFirstVisiblePosition();
+        mTopDistance = (firstChild == null) ? 0 : (firstChild.getTop() - list.getPaddingTop());
+    }
+
+    @Override
     public void onListItemClick(ListView l, View v, int position, long id) {
         final LocaleStore.LocaleInfo locale =
                 (LocaleStore.LocaleInfo) getListAdapter().getItem(position);
@@ -193,12 +219,27 @@
         if (mParentLocale == null) {
             inflater.inflate(R.menu.language_selection_list, menu);
 
-            MenuItem mSearchMenuItem = menu.findItem(R.id.locale_search_menu);
-            SearchView mSearchView = (SearchView) mSearchMenuItem.getActionView();
+            final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu);
+            mSearchView = (SearchView) searchMenuItem.getActionView();
 
             mSearchView.setQueryHint(getText(R.string.search_language_hint));
             mSearchView.setOnQueryTextListener(this);
-            mSearchView.setQuery("", false /* submit */);
+
+            // Restore previous search status
+            if (!TextUtils.isEmpty(mPreviousSearch)) {
+                searchMenuItem.expandActionView();
+                mSearchView.setIconified(false);
+                mSearchView.setActivated(true);
+                if (mPreviousSearchHadFocus) {
+                    mSearchView.requestFocus();
+                }
+                mSearchView.setQuery(mPreviousSearch, true /* submit */);
+            } else {
+                mSearchView.setQuery(null, false /* submit */);
+            }
+
+            // Restore previous scroll position
+            getListView().setSelectionFromTop(mFirstVisiblePosition, mTopDistance);
         }
     }
 
diff --git a/core/java/com/android/internal/inputmethod/LocaleUtils.java b/core/java/com/android/internal/inputmethod/LocaleUtils.java
index 99bb4cb..2aa660e 100644
--- a/core/java/com/android/internal/inputmethod/LocaleUtils.java
+++ b/core/java/com/android/internal/inputmethod/LocaleUtils.java
@@ -18,15 +18,17 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.text.TextUtils;
+import android.icu.util.ULocale;
 import android.util.LocaleList;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Locale;
-import java.util.Objects;
 
 public final class LocaleUtils {
 
@@ -36,12 +38,138 @@
         Locale get(@Nullable T source);
     }
 
-    @Nullable
-    private static String getLanguage(@Nullable Locale locale) {
-        if (locale == null) {
-            return null;
+    /**
+     * Calculates a matching score for the single desired locale.
+     *
+     * @see LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])
+     *
+     * @param supported The locale supported by IME subtype.
+     * @param desired The locale preferred by user.
+     * @return A score based on the locale matching for the default subtype enabling.
+     */
+    @IntRange(from=1, to=3)
+    private static byte calculateMatchingSubScore(@NonNull final ULocale supported,
+            @NonNull final ULocale desired) {
+        // Assuming supported/desired is fully expanded.
+        if (supported.equals(desired)) {
+            return 3;  // Exact match.
         }
-        return locale.getLanguage();
+
+        // Skip language matching since it was already done in calculateMatchingScore.
+
+        final String supportedScript = supported.getScript();
+        if (supportedScript.isEmpty() || !supportedScript.equals(desired.getScript())) {
+            // TODO: Need subscript matching. For example, Hanb should match with Bopo.
+            return 1;
+        }
+
+        final String supportedCountry = supported.getCountry();
+        if (supportedCountry.isEmpty() || !supportedCountry.equals(desired.getCountry())) {
+            return 2;
+        }
+
+        // Ignore others e.g. variants, extensions.
+        return 3;
+    }
+
+    /**
+     * Calculates a matching score for the desired locale list.
+     *
+     * <p>The supported locale gets a matching score of 3 if all language, script and country of the
+     * supported locale matches with the desired locale.  The supported locale gets a matching
+     * score of 2 if the language and script of the supported locale matches with the desired
+     * locale. The supported locale gets a matching score of 1 if only language of the supported
+     * locale matches with the desired locale.  The supported locale gets a matching score of 0 if
+     * the language of the supported locale doesn't match with the desired locale.</p>
+     *
+     * @param supported The locale supported by IME subtyle.
+     * @param desired The locale list preferred by user. Typically system locale list.
+     * @param out The output buffer to be stored the individual score for the desired language list.
+     * The length of {@code out} must be same as the length of {@code desired} language list.
+     * @return {@code false} if supported locale doesn't match with any desired locale list.
+     * Otherwise {@code true}.
+     */
+    private static boolean calculateMatchingScore(@NonNull final ULocale supported,
+            @NonNull final LocaleList desired, @NonNull byte[] out) {
+        if (desired.isEmpty()) {
+            return false;
+        }
+
+        boolean allZeros = true;
+        final int N = desired.size();
+        for (int i = 0; i < N; ++i) {
+            final Locale locale = desired.get(i);
+
+            if (!locale.getLanguage().equals(supported.getLanguage())) {
+                // TODO: cache the result of addLikelySubtags if it is slow.
+                out[i] = 0;
+            } else {
+                out[i] = calculateMatchingSubScore(
+                        supported, ULocale.addLikelySubtags(ULocale.forLocale(locale)));
+                if (allZeros && out[i] != 0) {
+                    allZeros = false;
+                }
+            }
+        }
+        return !allZeros;
+    }
+
+    private static final class ScoreEntry implements Comparable<ScoreEntry> {
+        public int mIndex = -1;
+        @NonNull public final byte[] mScore;  // matching score of the i-th system languages.
+
+        ScoreEntry(@NonNull byte[] score, int index) {
+            mScore = new byte[score.length];
+            set(score, index);
+        }
+
+        private void set(@NonNull byte[] score, int index) {
+            for (int i = 0; i < mScore.length; ++i) {
+                mScore[i] = score[i];
+            }
+            mIndex = index;
+        }
+
+        /**
+         * Update score and index if the given score is better than this.
+         */
+        public void updateIfBetter(@NonNull byte[] score, int index) {
+            if (compare(mScore, score) == -1) {  // mScore < score
+                set(score, index);
+            }
+        }
+
+        /**
+         * Provides comaprison for bytes[].
+         *
+         * <p> Comparison does as follows. If the first value of {@code left} is larger than the
+         * first value of {@code right}, {@code left} is large than {@code right}.  If the first
+         * value of {@code left} is less than the first value of {@code right}, {@code left} is less
+         * than {@code right}. If the first value of {@code left} and the first value of
+         * {@code right} is equal, do the same comparison to the next value. Finally if all values
+         * in {@code left} and {@code right} are equal, {@code left} and {@code right} is equal.</p>
+         *
+         * @param left The length must be equal to {@code right}.
+         * @param right The length must be equal to {@code left}.
+         * @return 1 if {@code left} is larger than {@code right}. -1 if {@code left} is less than
+         * {@code right}. 0 if {@code left} and {@code right} is equal.
+         */
+        @IntRange(from=-1, to=1)
+        private static int compare(@NonNull byte[] left, @NonNull byte[] right) {
+            for (int i = 0; i < left.length; ++i) {
+                if (left[i] > right[i]) {
+                    return 1;
+                } else if (left[i] < right[i]) {
+                    return -1;
+                }
+            }
+            return 0;
+        }
+
+        @Override
+        public int compareTo(final ScoreEntry other) {
+            return -1 * compare(mScore, other.mScore);  // Order by descending order.
+        }
     }
 
     /**
@@ -52,14 +180,8 @@
      * {@code "en-GB", "ja", "en-AU", "fr-CA", "en-IN"} is specified to {@code preferredLanguages},
      * this method tries to copy at most one English locale, at most one Japanese, and at most one
      * French locale from {@code source} to {@code dest}.  Here the best matching English locale
-     * will be searched from {@code source} as follows.
-     * <ol>
-     *     <li>The first instance in {@code sources} that exactly matches {@code "en-GB"}</li>
-     *     <li>The first instance in {@code sources} that exactly matches {@code "en-AU"}</li>
-     *     <li>The first instance in {@code sources} that exactly matches {@code "en-IN"}</li>
-     *     <li>The first instance in {@code sources} that partially matches {@code "en"}</li>
-     * </ol>
-     * <p>Then this method iterates the same algorithm for Japanese then French.</p>
+     * will be searched from {@code source} based on matching score. For the score design, see
+     * {@link LocaleUtils#calculateMatchingScore(ULocale, LocaleList, byte[])}</p>
      *
      * @param sources Source items to be filtered.
      * @param extractor Type converter from the source items to {@link Locale} object.
@@ -74,69 +196,31 @@
             @NonNull LocaleExtractor<T> extractor,
             @NonNull LocaleList preferredLanguages,
             @NonNull ArrayList<T> dest) {
-        final Locale[] availableLocales = new Locale[sources.size()];
-        for (int i = 0; i < availableLocales.length; ++i) {
-            availableLocales[i] = extractor.get(sources.get(i));
-        }
-        final Locale[] sortedPreferredLanguages = new Locale[preferredLanguages.size()];
-        if (sortedPreferredLanguages.length > 0) {
-            int nextIndex = 0;
-            final int N = preferredLanguages.size();
-            languageLoop:
-            for (int i = 0; i < N; ++i) {
-                final String language = getLanguage(preferredLanguages.get(i));
-                for (int j = 0; j < nextIndex; ++j) {
-                    if (TextUtils.equals(getLanguage(sortedPreferredLanguages[j]), language)) {
-                        continue languageLoop;
-                    }
-                }
-                for (int j = i; j < N; ++j) {
-                    final Locale locale = preferredLanguages.get(j);
-                    if (TextUtils.equals(language, getLanguage(locale))) {
-                        sortedPreferredLanguages[nextIndex] = locale;
-                        ++nextIndex;
-                    }
-                }
+        final HashMap<String, ScoreEntry> scoreboard = new HashMap<>();
+        final byte[] score = new byte[preferredLanguages.size()];
+
+        final int sourceSize = sources.size();
+        for (int i = 0; i < sourceSize; ++i) {
+            final Locale locale = extractor.get(sources.get(i));
+            if (locale == null ||
+                    !calculateMatchingScore(ULocale.addLikelySubtags(ULocale.forLocale(locale)),
+                            preferredLanguages, score)) {
+                continue;
+            }
+
+            final String lang = locale.getLanguage();
+            final ScoreEntry bestScore = scoreboard.get(lang);
+            if (bestScore == null) {
+                scoreboard.put(lang, new ScoreEntry(score, i));
+            } else {
+                bestScore.updateIfBetter(score, i);
             }
         }
 
-
-        for (int languageIndex = 0; languageIndex < sortedPreferredLanguages.length;) {
-            // Finding the range.
-            final String language = getLanguage(sortedPreferredLanguages[languageIndex]);
-            int nextLanguageIndex = languageIndex;
-            for (; nextLanguageIndex < sortedPreferredLanguages.length; ++nextLanguageIndex) {
-                final Locale locale = sortedPreferredLanguages[nextLanguageIndex];
-                if (!TextUtils.equals(getLanguage(locale), language)) {
-                    break;
-                }
-            }
-
-            // Check exact match
-            boolean found = false;
-            for (int i = languageIndex; !found && i < nextLanguageIndex; ++i) {
-                final Locale locale = sortedPreferredLanguages[i];
-                for (int j = 0; j < availableLocales.length; ++j) {
-                    if (!Objects.equals(locale, availableLocales[j])) {
-                        continue;
-                    }
-                    dest.add(sources.get(j));
-                    found = true;
-                    break;
-                }
-            }
-
-            if (!found) {
-                // No exact match.  Use language match.
-                for (int j = 0; j < availableLocales.length; ++j) {
-                    if (!TextUtils.equals(language, getLanguage(availableLocales[j]))) {
-                        continue;
-                    }
-                    dest.add(sources.get(j));
-                    break;
-                }
-            }
-            languageIndex = nextLanguageIndex;
+        final ScoreEntry[] result = scoreboard.values().toArray(new ScoreEntry[scoreboard.size()]);
+        Arrays.sort(result);
+        for (final ScoreEntry entry : result) {
+            dest.add(sources.get(entry.mIndex));
         }
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/com/android/internal/widget/MediaNotificationView.java b/core/java/com/android/internal/widget/MediaNotificationView.java
index 6bba1b3..f63afad 100644
--- a/core/java/com/android/internal/widget/MediaNotificationView.java
+++ b/core/java/com/android/internal/widget/MediaNotificationView.java
@@ -89,36 +89,50 @@
         int topMargin = getMeasuredHeight() - mRightIcon.getMeasuredHeight()
                 - iconParams.bottomMargin;
         // If the topMargin is high enough we can also remove the header constraint!
+        boolean reMeasure = false;
         if (!hasIcon || topMargin >= mImageMinTopMargin) {
-            resetHeaderIndention();
+            reMeasure = resetHeaderIndention();
         } else {
             int paddingEnd = mNotificationContentImageMarginEnd;
             ViewGroup.MarginLayoutParams headerParams =
                     (MarginLayoutParams) mHeader.getLayoutParams();
-            headerParams.setMarginEnd(mRightIcon.getMeasuredWidth() + iconParams.getMarginEnd());
-            if (mHeader.getPaddingEnd() != paddingEnd) {
-                mHeader.setPadding(
-                        isLayoutRtl() ? paddingEnd : mHeader.getPaddingLeft(),
-                        mHeader.getPaddingTop(),
-                        isLayoutRtl() ? mHeader.getPaddingLeft() : paddingEnd,
-                        mHeader.getPaddingBottom());
+            int newMarginEnd = mRightIcon.getMeasuredWidth() + iconParams.getMarginEnd();
+            if (headerParams.getMarginEnd() != newMarginEnd) {
+                headerParams.setMarginEnd(newMarginEnd);
                 mHeader.setLayoutParams(headerParams);
+                reMeasure = true;
             }
+            if (mHeader.getPaddingEnd() != paddingEnd) {
+                mHeader.setPaddingRelative(mHeader.getPaddingStart(),
+                        mHeader.getPaddingTop(),
+                        paddingEnd,
+                        mHeader.getPaddingBottom());
+                reMeasure = true;
+            }
+        }
+        if (reMeasure) {
+            measureChildWithMargins(mHeader, widthMeasureSpec, 0, heightMeasureSpec, 0);
         }
     }
 
-    private void resetHeaderIndention() {
+    private boolean resetHeaderIndention() {
+        boolean remeasure = false;
         if (mHeader.getPaddingEnd() != mNotificationContentMarginEnd) {
-            ViewGroup.MarginLayoutParams headerParams =
-                    (MarginLayoutParams) mHeader.getLayoutParams();
-            headerParams.setMarginEnd(0);
-            mHeader.setPadding(
-                    isLayoutRtl() ? mNotificationContentMarginEnd : mHeader.getPaddingLeft(),
+            mHeader.setPaddingRelative(mHeader.getPaddingStart(),
                     mHeader.getPaddingTop(),
-                    isLayoutRtl() ? mHeader.getPaddingLeft() : mNotificationContentMarginEnd,
+                    mNotificationContentMarginEnd,
                     mHeader.getPaddingBottom());
-            mHeader.setLayoutParams(headerParams);
+            remeasure = true;
         }
+        ViewGroup.MarginLayoutParams headerParams =
+                (MarginLayoutParams) mHeader.getLayoutParams();
+        headerParams.setMarginEnd(0);
+        if (headerParams.getMarginEnd() != 0) {
+            headerParams.setMarginEnd(0);
+            mHeader.setLayoutParams(headerParams);
+            remeasure = true;
+        }
+        return remeasure;
     }
 
     public MediaNotificationView(Context context, AttributeSet attrs, int defStyleAttr,
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index 7a13fe4..5d496e5 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -94,8 +94,6 @@
             SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
                     &(page->mContentRect));
 
-            canvas->clipRect(page->mContentRect);
-            canvas->translate(page->mContentRect.left(), page->mContentRect.top());
             canvas->drawPicture(page->mPicture);
 
             document->endPage();
diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp
index 5751add..156e7bd 100644
--- a/core/jni/android_opengl_GLES31.cpp
+++ b/core/jni/android_opengl_GLES31.cpp
@@ -888,9 +888,69 @@
 static jint
 android_glCreateShaderProgramv
   (JNIEnv *_env, jobject _this, jint type, jobjectArray strings) {
+    jint _exception = 0;
+    const char * _exceptionType = NULL;
+    const char * _exceptionMessage = NULL;
+    GLsizei _count;
+    const GLchar** _strings = NULL;
+    jstring* _jstrings = NULL;
+    GLuint _returnValue = 0;
 
-    jniThrowException(_env, "java/lang/UnsupportedOperationException", "not yet implemented");
-    return 0;
+    if (!strings) {
+        _exception = 1;
+        _exceptionType = "java/lang/IllegalArgumentException";
+        _exceptionMessage = "strings == null";
+        goto exit;
+    }
+
+    _count = _env->GetArrayLength(strings);
+
+    _strings = (const GLchar**) calloc(_count, sizeof(const GLchar*));
+    if (!_strings) {
+        _exception = 1;
+        _exceptionType = "java/lang/OutOfMemoryError";
+        _exceptionMessage = "out of memory";
+        goto exit;
+    }
+
+    _jstrings = (jstring*) calloc(_count, sizeof(jstring));
+    if (!_jstrings) {
+        _exception = 1;
+        _exceptionType = "java/lang/OutOfMemoryError";
+        _exceptionMessage = "out of memory";
+        goto exit;
+    }
+
+    for(int i = 0; i < _count; i++) {
+        _jstrings[i] = (jstring) _env->GetObjectArrayElement(strings, i);
+        if (!_jstrings[i]) {
+            _exception = 1;
+            _exceptionType = "java/lang/IllegalArgumentException";
+            _exceptionMessage = "strings == null";
+            goto exit;
+        }
+        _strings[i] = _env->GetStringUTFChars(_jstrings[i], 0);
+    }
+
+    _returnValue = glCreateShaderProgramv((GLenum)type, _count, _strings);
+exit:
+    if (_strings && _jstrings) {
+        for(int i = 0; i < _count; i++) {
+            if (_strings[i] && _jstrings[i]) {
+                _env->ReleaseStringUTFChars(_jstrings[i], _strings[i]);
+            }
+        }
+    }
+    if (_strings) {
+        free(_strings);
+    }
+    if (_jstrings) {
+        free(_jstrings);
+    }
+    if (_exception) {
+        jniThrowException(_env, _exceptionType, _exceptionMessage);
+    }
+    return (jint)_returnValue;
 }
 /* void glBindProgramPipeline ( GLuint pipeline ) */
 static void
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8f85d4a..fbc96c2 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -168,7 +168,7 @@
         android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
-   <protected-broadcast
+    <protected-broadcast
         android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
     <protected-broadcast
         android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
@@ -984,6 +984,11 @@
     <permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- @SystemApi Allows an application to send SMS to premium shortcodes without user permission.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SEND_SMS_NO_CONFIRMATION"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to filter carrier specific sms.
          @hide -->
     <permission android:name="android.permission.CARRIER_FILTER_SMS"
@@ -2769,12 +2774,11 @@
         android:protectionLevel="signature" />
 
     <!-- Must be required by an {@link
-         android.service.notification.NotificationAssistantService},
-         to ensure that only the system can bind to it.
+         android.service.notification.NotificationRankerService         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"
+    <permission android:name="android.permission.BIND_NOTIFICATION_RANKER_SERVICE"
         android:protectionLevel="signature" />
 
     <!-- Must be required by a {@link
@@ -2929,6 +2933,12 @@
     <permission android:name="android.permission.WRITE_BLOCKED_NUMBERS"
                 android:protectionLevel="signature" />
 
+    <!-- Must be required by an {@link android.service.vr.VrListenerService}, to ensure that only
+         the system can bind to it.
+         <p>Protection level: signature -->
+    <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
+        android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/core/res/assets/images/clock64.png b/core/res/assets/images/clock64.png
new file mode 100644
index 0000000..493a1ea
--- /dev/null
+++ b/core/res/assets/images/clock64.png
Binary files differ
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 6669bae..b8b00bf 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -33,16 +33,6 @@
         android:layout_marginEnd="3dp"
         />
     <TextView
-        android:id="@+id/number_of_children"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification"
-        android:layout_marginEnd="3dp"
-        android:layout_marginStart="2dp"
-        android:visibility="gone"
-        android:singleLine="true"
-        />
-    <TextView
         android:id="@+id/app_name_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d8efd63..1d7034d 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -548,9 +548,6 @@
          [CHAR LIMIT=4] -->
     <string name="status_bar_notification_info_overflow">999+</string>
 
-    <!-- The number of notifications in the notification header. An example would be (2) or (12) -->
-    <string name="notification_children_count_bracketed">(<xliff:g id="notificationCount" example="1">%d</xliff:g>)</string>
-
     <!-- The divider symbol between different parts of the notification header. not translatable [CHAR LIMIT=1] -->
     <string name="notification_header_divider_symbol" translatable="false">•</string>
 
@@ -3135,11 +3132,13 @@
     <!-- Label to show for a service that is running because it is observing
          the user's notifications. -->
     <string name="notification_listener_binding_label">Notification listener</string>
+    <!-- Label to show for a service that is running because the system is in VR mode. -->
+    <string name="vr_listener_binding_label">VR listener</string>
     <!-- Label to show for a service that is running because it is providing conditions. -->
     <string name="condition_provider_service_binding_label">Condition provider</string>
     <!-- Label to show for a service that is running because it is observing and modifying the
          importance of the user's notifications. -->
-    <string name="notification_assistant_binding_label">Notification assistant</string>
+    <string name="notification_ranker_binding_label">Notification ranker service</string>
 
     <!-- Do Not Translate: Alternate eri.xml -->
     <string name="alternate_eri_file">/data/eri.xml</string>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index a9c8a06..a65a813 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -993,8 +993,7 @@
     </style>
 
     <style name="Widget.Material.SuggestionItem" parent="@android:style/TextAppearance.Material.Body1">
-        <item name="alpha">.87</item>
-        <item name="textColor">@color/black</item>
+        <item name="textColor">#8a000000</item>  <!-- alpha=.54, textColor=@color/black -->
         <item name="drawablePadding">8dip</item>
         <item name="gravity">start|center_vertical</item>
         <item name="layout_gravity">start|center_vertical</item>
@@ -1009,12 +1008,11 @@
     </style>
 
     <style name="TextAppearance.Material.TextSuggestionHighlight" parent="Widget.Material.SuggestionItem">
-        <item name="textColor">#009688</item>
+        <item name="textColor">#de000000</item>  <!-- alpha=.87, textColor=@color/black -->
     </style>
 
     <style name="Widget.Material.SuggestionButton" parent="@android:style/TextAppearance.Material.Button">
-        <item name="alpha">.87</item>
-        <item name="textColor">#009688</item>
+        <item name="textColor">#de009688</item>  <!-- alpha=.87, textColor=#009688 -->
         <item name="drawablePadding">8dip</item>
         <item name="gravity">start|center_vertical</item>
         <item name="layout_gravity">start|center_vertical</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 15521e4..0120128 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1842,8 +1842,9 @@
   <java-symbol type="string" name="low_internal_storage_view_text_no_boot" />
   <java-symbol type="string" name="low_internal_storage_view_title" />
   <java-symbol type="string" name="notification_listener_binding_label" />
+  <java-symbol type="string" name="vr_listener_binding_label" />
   <java-symbol type="string" name="condition_provider_service_binding_label" />
-  <java-symbol type="string" name="notification_assistant_binding_label" />
+  <java-symbol type="string" name="notification_ranker_binding_label" />
   <java-symbol type="string" name="report" />
   <java-symbol type="string" name="select_input_method" />
   <java-symbol type="string" name="select_keyboard_layout_notification_title" />
@@ -2405,11 +2406,9 @@
   <java-symbol type="id" name="notification_material_reply_text_2" />
   <java-symbol type="id" name="notification_material_reply_text_3" />
 
-  <java-symbol type="string" name="notification_children_count_bracketed" />
   <java-symbol type="string" name="notification_hidden_text" />
   <java-symbol type="string" name="notification_hidden_by_policy_text" />
   <java-symbol type="id" name="app_name_text" />
-  <java-symbol type="id" name="number_of_children" />
   <java-symbol type="id" name="header_sub_text" />
   <java-symbol type="id" name="expand_button" />
   <java-symbol type="id" name="notification_header" />
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index 3feb0e9..c9bc8aa 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -61,7 +61,6 @@
 public abstract class BasePrintTest extends InstrumentationTestCase {
 
     private static final long OPERATION_TIMEOUT = 30000;
-    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
     private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
     private static final String COMMAND_LIST_ENABLED_IME_COMPONENTS = "ime list -s";
     private static final String COMMAND_PREFIX_ENABLE_IME = "ime enable ";
@@ -249,8 +248,9 @@
     protected void clearPrintSpoolerData() throws Exception {
         assertTrue("failed to clear print spooler data",
                 runShellCommand(getInstrumentation(), String.format(
-                        "pm clear --user %d %s", CURRENT_USER_ID, PRINT_SPOOLER_PACKAGE_NAME))
-                                .contains(PM_CLEAR_SUCCESS_OUTPUT));
+                        "pm clear --user %d %s", CURRENT_USER_ID,
+                        PrintManager.PRINT_SPOOLER_PACKAGE_NAME))
+                        .contains(PM_CLEAR_SUCCESS_OUTPUT));
     }
 
     @SuppressWarnings("unchecked")
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 5179b42..cbf17a4 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -27,24 +27,9 @@
 import android.os.Process;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.print.IPrintDocumentAdapter;
-import android.print.IPrintJobStateChangeListener;
-import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
-import android.print.PageRange;
-import android.print.PrintAttributes;
 import android.print.PrintAttributes.Margins;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
-import android.print.PrintDocumentAdapter;
-import android.print.PrintJob;
-import android.print.PrintJobId;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterCapabilitiesInfo;
-import android.print.PrinterDiscoverySession;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
 
 import android.print.mockservice.MockPrintService;
@@ -134,7 +119,7 @@
 
                                 if (session.getPrinters().isEmpty()) {
                                     final String PRINTER_NAME = "good printer";
-                                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+                                    List<PrinterInfo> printers = new ArrayList<>();
 
                                     // Add the printer.
                                     mGoodPrinterId = session.getService()
@@ -184,6 +169,18 @@
     }
 
     /**
+     * Create a IPrintServicesChangeListener object.
+     *
+     * @return the object
+     * @throws Exception if the object could not be created.
+     */
+    private IPrintServicesChangeListener createMockIPrintServicesChangeListener() throws Exception {
+        return new PrintManager.PrintServicesChangeListenerWrapper(null,
+                new Handler(Looper.getMainLooper()));
+    }
+
+
+    /**
      * Create a IPrinterDiscoveryObserver object.
      *
      * @return the object
@@ -193,6 +190,16 @@
         return new PrinterDiscoverySession.PrinterDiscoveryObserver(null);
     }
 
+    private void startPrinting() {
+        mGoodPrintJob = print(createMockAdapter(), null);
+
+        // Wait for PrintActivity to be ready
+        waitForStartAdapterCallbackCalled();
+
+        // Wait for printer discovery session to be ready
+        waitForPrinterDiscoverySessionStartCallbackCalled();
+    }
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -201,20 +208,12 @@
 
         mGoodComponentName = getActivity().getComponentName();
 
-        mGoodPrintJob = print(createMockAdapter(), null);
-
         mIPrintManager = IPrintManager.Stub
                 .asInterface(ServiceManager.getService(Context.PRINT_SERVICE));
 
         // Generate dummy printerId which is a valid PrinterId object, but does not correspond to a
         // printer
         mBadPrinterId = new PrinterId(mGoodComponentName, "dummy printer");
-
-        // Wait for PrintActivity to be ready
-        waitForStartAdapterCallbackCalled();
-
-        // Wait for printer discovery session to be ready
-        waitForPrinterDiscoverySessionStartCallbackCalled();
     }
 
     /**
@@ -222,11 +221,11 @@
      */
     private interface Invokable {
         /**
-         * Execute the {@link Invokable}
+         * Execute the invokable
          *
          * @throws Exception
          */
-        public void run() throws Exception;
+        void run() throws Exception;
     }
 
     /**
@@ -255,6 +254,8 @@
      * test IPrintManager.getPrintJobInfo
      */
     public void testGetPrintJobInfo() throws Exception {
+        startPrinting();
+
         assertEquals(mGoodPrintJob.getId(), mIPrintManager.getPrintJobInfo(mGoodPrintJob.getId(),
                         mAppId, mUserId).getId());
         assertEquals(null, mIPrintManager.getPrintJobInfo(mBadPrintJobId, mAppId, mUserId));
@@ -274,6 +275,8 @@
      * test IPrintManager.getPrintJobInfos
      */
     public void testGetPrintJobInfos() throws Exception {
+        startPrinting();
+
         List<PrintJobInfo> infos = mIPrintManager.getPrintJobInfos(mAppId, mUserId);
 
         boolean foundPrintJob = false;
@@ -304,7 +307,7 @@
         final IPrintDocumentAdapter adapter = new PrintManager
                 .PrintDocumentAdapterDelegate(getActivity(), createMockAdapter());
 
-        // Valid parameters are tested in setUp()
+        startPrinting();
 
         assertException(new Invokable() {
             @Override
@@ -352,6 +355,8 @@
      * test IPrintManager.cancelPrintJob
      */
     public void testCancelPrintJob() throws Exception {
+        startPrinting();
+
         // Invalid print jobs IDs do not produce an exception
         mIPrintManager.cancelPrintJob(mBadPrintJobId, mAppId, mUserId);
         mIPrintManager.cancelPrintJob(null, mAppId, mUserId);
@@ -373,6 +378,8 @@
      * test IPrintManager.restartPrintJob
      */
     public void testRestartPrintJob() throws Exception {
+        startPrinting();
+
         mIPrintManager.restartPrintJob(mGoodPrintJob.getId(), mAppId, mUserId);
 
         // Invalid print jobs IDs do not produce an exception
@@ -438,22 +445,103 @@
     }
 
     /**
-     * test IPrintManager.getInstalledPrintServices
+     * test IPrintManager.addPrintServicesChangeListener
      */
-    public void testGetInstalledPrintServices() throws Exception {
-        List<PrintServiceInfo> printServices = mIPrintManager.getInstalledPrintServices(mUserId);
-        assertTrue(printServices.size() >= 2);
+    public void testAddPrintServicesChangeListener() throws Exception {
+        final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+        mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.addPrintServicesChangeListener(null, mUserId);
+            }
+        }, NullPointerException.class);
 
         // Cannot test bad user Id as these tests are allowed to call across users
     }
 
     /**
-     * test IPrintManager.getEnabledPrintServices
+     * test IPrintManager.removePrintServicesChangeListener
      */
-    public void testGetEnabledPrintServices() throws Exception {
-        List<PrintServiceInfo> printServices = mIPrintManager.getEnabledPrintServices(mUserId);
+    public void testRemovePrintServicesChangeListener() throws Exception {
+        final IPrintServicesChangeListener listener = createMockIPrintServicesChangeListener();
+
+        mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+        mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+        // Removing unknown listeners is a no-op
+        mIPrintManager.removePrintServicesChangeListener(listener, mUserId);
+
+        mIPrintManager.addPrintServicesChangeListener(listener, mUserId);
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.removePrintServicesChangeListener(null, mUserId);
+            }
+        }, NullPointerException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.getPrintServices
+     */
+    public void testGetPrintServices() throws Exception {
+        List<PrintServiceInfo> printServices = mIPrintManager.getPrintServices(
+                PrintManager.ALL_SERVICES, mUserId);
         assertTrue(printServices.size() >= 2);
 
+        printServices = mIPrintManager.getPrintServices(0, mUserId);
+        assertEquals(printServices, null);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.getPrintServices(~PrintManager.ALL_SERVICES, mUserId);
+            }
+        }, IllegalArgumentException.class);
+
+        // Cannot test bad user Id as these tests are allowed to call across users
+    }
+
+    /**
+     * test IPrintManager.setPrintServiceEnabled
+     */
+    public void testSetPrintServiceEnabled() throws Exception {
+        final ComponentName printService = mIPrintManager.getPrintServices(
+                PrintManager.ALL_SERVICES, mUserId).get(0).getComponentName();
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.setPrintServiceEnabled(printService, false, mUserId);
+            }
+        }, SecurityException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.setPrintServiceEnabled(printService, true, mUserId);
+            }
+        }, SecurityException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.setPrintServiceEnabled(new ComponentName("bad", "name"), true,
+                                mUserId);
+            }
+        }, SecurityException.class);
+
+        assertException(new Invokable() {
+            @Override
+            public void run() throws Exception {
+                mIPrintManager.setPrintServiceEnabled(null, true, mUserId);
+            }
+        }, SecurityException.class);
+
         // Cannot test bad user Id as these tests are allowed to call across users
     }
 
@@ -486,6 +574,8 @@
      * test IPrintManager.startPrinterDiscovery
      */
     public void testStartPrinterDiscovery() throws Exception {
+        startPrinting();
+
         final IPrinterDiscoveryObserver listener = createMockIPrinterDiscoveryObserver();
         final List<PrinterId> goodPrinters = new ArrayList<>();
         goodPrinters.add(mGoodPrinterId);
@@ -549,6 +639,8 @@
      * test IPrintManager.validatePrinters
      */
     public void testValidatePrinters() throws Exception {
+        startPrinting();
+
         final List<PrinterId> goodPrinters = new ArrayList<>();
         goodPrinters.add(mGoodPrinterId);
 
@@ -587,6 +679,8 @@
      * test IPrintManager.startPrinterStateTracking
      */
     public void testStartPrinterStateTracking() throws Exception {
+        startPrinting();
+
         mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
 
         // Bad printers do no cause exceptions
@@ -606,6 +700,8 @@
      * test IPrintManager.getCustomPrinterIcon
      */
     public void testGetCustomPrinterIcon() throws Exception {
+        startPrinting();
+
         mIPrintManager.getCustomPrinterIcon(mGoodPrinterId, mUserId);
 
         // Bad printers do no cause exceptions
@@ -625,6 +721,8 @@
      * test IPrintManager.stopPrinterStateTracking
      */
     public void testStopPrinterStateTracking() throws Exception {
+        startPrinting();
+
         mIPrintManager.startPrinterStateTracking(mGoodPrinterId, mUserId);
         mIPrintManager.stopPrinterStateTracking(mGoodPrinterId, mUserId);
 
diff --git a/core/tests/coretests/src/android/transition/SlideTransitionTest.java b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
new file mode 100644
index 0000000..8b9ec74
--- /dev/null
+++ b/core/tests/coretests/src/android/transition/SlideTransitionTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.transition;
+
+import android.animation.AnimatorSetActivity;
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.android.frameworks.coretests.R;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+
+public class SlideTransitionTest extends ActivityInstrumentationTestCase2<AnimatorSetActivity> {
+
+    Activity mActivity;
+
+    public SlideTransitionTest() {
+        super(AnimatorSetActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mActivity = getActivity();
+    }
+
+    @SmallTest
+    public void testShortSlide() throws Throwable {
+        final float slideFraction = 0.5f;
+        final View square1 = mActivity.findViewById(R.id.square1);
+        final View sceneRoot = mActivity.findViewById(R.id.container);
+        final SlideTranslationValueRatchet ratchet = new SlideTranslationValueRatchet(square1);
+        square1.getViewTreeObserver().addOnPreDrawListener(ratchet);
+
+        final Slide slideOut = new Slide(Gravity.BOTTOM);
+        final float finalOffsetOut = sceneRoot.getHeight() * slideFraction;
+        slideOut.setSlideFraction(slideFraction);
+        TransitionLatch latch = setVisibilityInTransition(slideOut, R.id.square1, View.INVISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(0f, square1.getTranslationY(), 0.1f);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        assertFalse(square1.getTranslationY() < 0.1
+                || square1.getTranslationY() > finalOffsetOut - 0.1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        // Give this 20% slop in case some frames get dropped.
+        assertTrue(finalOffsetOut * 0.8 < ratchet.maxY);
+        assertTrue(finalOffsetOut + 0.1 > ratchet.maxY);
+        assertEquals(View.INVISIBLE, square1.getVisibility());
+
+        ratchet.reset();
+        final Slide slideIn = new Slide(Gravity.BOTTOM);
+        final float initialOffsetIn = sceneRoot.getHeight() * slideFraction;
+        slideIn.setSlideFraction(slideFraction);
+        latch = setVisibilityInTransition(slideIn, R.id.square1, View.VISIBLE);
+        assertTrue(latch.startLatch.await(200, TimeUnit.MILLISECONDS));
+        assertEquals(initialOffsetIn, square1.getTranslationY(), 0.1f);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+        Thread.sleep(100);
+        assertFalse(square1.getTranslationY() < 0.1
+                || square1.getTranslationY() > initialOffsetIn - 0.1);
+        assertTrue(latch.endLatch.await(400, TimeUnit.MILLISECONDS));
+        assertEquals(0f, ratchet.minY, 0.1);
+        assertEquals(0f, square1.getTranslationY(), 0.1);
+        assertEquals(View.VISIBLE, square1.getVisibility());
+
+        square1.getViewTreeObserver().removeOnPreDrawListener(ratchet);
+    }
+
+    public TransitionLatch setVisibilityInTransition(final Transition transition, int viewId,
+            final int visibility) throws Throwable {
+        final ViewGroup sceneRoot = (ViewGroup) mActivity.findViewById(R.id.container);
+        final View view = sceneRoot.findViewById(viewId);
+        TransitionLatch latch = new TransitionLatch();
+        transition.addListener(latch);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                TransitionManager.beginDelayedTransition(sceneRoot, transition);
+                view.setVisibility(visibility);
+            }
+        });
+        return latch;
+    }
+
+    public static class TransitionLatch implements Transition.TransitionListener {
+        public CountDownLatch startLatch = new CountDownLatch(1);
+        public CountDownLatch endLatch = new CountDownLatch(1);
+        public CountDownLatch cancelLatch = new CountDownLatch(1);
+        public CountDownLatch pauseLatch = new CountDownLatch(1);
+        public CountDownLatch resumeLatch = new CountDownLatch(1);
+
+        @Override
+        public void onTransitionStart(Transition transition) {
+            startLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionEnd(Transition transition) {
+            endLatch.countDown();
+            transition.removeListener(this);
+        }
+
+        @Override
+        public void onTransitionCancel(Transition transition) {
+            cancelLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionPause(Transition transition) {
+            pauseLatch.countDown();
+        }
+
+        @Override
+        public void onTransitionResume(Transition transition) {
+            resumeLatch.countDown();
+        }
+    }
+
+    private static class SlideTranslationValueRatchet
+            implements ViewTreeObserver.OnPreDrawListener {
+
+        private final View mView;
+        private boolean mInitialized;
+        public float minX = Float.NaN;
+        public float minY = Float.NaN;
+        public float maxX = Float.NaN;
+        public float maxY = Float.NaN;
+
+        public SlideTranslationValueRatchet(View view) {
+            mView = view;
+        }
+
+        public void reset() {
+            minX = minY = maxX = maxY = Float.NaN;
+            mInitialized = false;
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            if (!mInitialized) {
+                minX = maxX = mView.getTranslationX();
+                minY = maxY = mView.getTranslationY();
+                mInitialized = true;
+            } else {
+                minX = Math.min(minX, mView.getTranslationX());
+                minY = Math.min(minY, mView.getTranslationY());
+                maxX = Math.max(maxX, mView.getTranslationX());
+                maxY = Math.max(maxY, mView.getTranslationY());
+            }
+            return true;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
index b9c2da7..deba40f 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/LocaleUtilsTest.java
@@ -50,6 +50,22 @@
     }
 
     @SmallTest
+    public void testFilterDoesNotMatchAnything() throws Exception {
+        final ArrayList<Locale> availableLocales = new ArrayList<>();
+        availableLocales.add(Locale.forLanguageTag("en-US"));
+        availableLocales.add(Locale.forLanguageTag("fr-CA"));
+        availableLocales.add(Locale.forLanguageTag("in"));
+        availableLocales.add(Locale.forLanguageTag("ja"));
+        availableLocales.add(Locale.forLanguageTag("fil"));
+
+        final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hans-TW");
+
+        final ArrayList<Locale> dest = new ArrayList<>();
+        LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+        assertEquals(0, dest.size());
+    }
+
+    @SmallTest
     public void testFilterByLanguageEmptySource() throws Exception {
         final ArrayList<Locale> availableLocales = new ArrayList<>();
 
@@ -124,21 +140,36 @@
 
     @SmallTest
     public void testFilterByLanguage() throws Exception {
-        final ArrayList<Locale> availableLocales = new ArrayList<>();
-        availableLocales.add(Locale.forLanguageTag("en-US"));
-        availableLocales.add(Locale.forLanguageTag("fr-CA"));
-        availableLocales.add(Locale.forLanguageTag("in"));
-        availableLocales.add(Locale.forLanguageTag("ja"));
-        availableLocales.add(Locale.forLanguageTag("fil"));
+        {
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("en-US"));
+            availableLocales.add(Locale.forLanguageTag("fr-CA"));
+            availableLocales.add(Locale.forLanguageTag("in"));
+            availableLocales.add(Locale.forLanguageTag("ja"));
+            availableLocales.add(Locale.forLanguageTag("fil"));
 
-        final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("fr,en-US,ja-JP");
 
-        final ArrayList<Locale> dest = new ArrayList<>();
-        LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
-        assertEquals(3, dest.size());
-        assertEquals(availableLocales.get(1), dest.get(0));  // "fr-CA"
-        assertEquals(availableLocales.get(0), dest.get(1));  // "en-US"
-        assertEquals(availableLocales.get(3), dest.get(2));  // "ja"
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(3, dest.size());
+            assertEquals(availableLocales.get(1), dest.get(0));  // "fr-CA"
+            assertEquals(availableLocales.get(0), dest.get(1));  // "en-US"
+            assertEquals(availableLocales.get(3), dest.get(2));  // "ja"
+        }
+        {
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("en-US"));
+            availableLocales.add(Locale.forLanguageTag("en-GB"));
+            availableLocales.add(Locale.forLanguageTag("en-IN"));
+
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("en-US");
+
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(0), dest.get(0));  // "en-US"
+        }
     }
 
     @SmallTest
@@ -191,4 +222,165 @@
             assertEquals(availableLocales.get(1), dest.get(0));  // "en-CA"
         }
     }
+
+    @SmallTest
+    public void testFilterByLanguageFallbackRules() throws Exception {
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS"
+        }
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS-x-android");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS"
+        }
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-BA-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-CS-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-ME-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-BA-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-CS-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-ME-x-android"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS-x-android"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(7), dest.get(0));  // "sr-Latn-RS-x-android"
+        }
+
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn-RS");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(2), dest.get(0));  // "sr-Latn"
+        }
+
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-RS");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr"));
+            availableLocales.add(Locale.forLanguageTag("sr-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(0), dest.get(0));  // "sr"
+        }
+
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr"));
+            availableLocales.add(Locale.forLanguageTag("sr-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(2), dest.get(0));  // "sr-Latn"
+        }
+
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr"));
+            availableLocales.add(Locale.forLanguageTag("sr-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(0), dest.get(0));  // "sr"
+        }
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Latn"));
+            availableLocales.add(Locale.forLanguageTag("sr-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(1), dest.get(0));  // "sr-RS"
+        }
+
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(0), dest.get(0));  // "sr-Cyrl-RS"
+        }
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("sr-Latn");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("sr-Latn-RS"));
+            availableLocales.add(Locale.forLanguageTag("sr-Cyrl-RS"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            assertEquals(availableLocales.get(0), dest.get(0));  // "sr-Latn-RS"
+        }
+    }
+
+    public void testFilterKnownLimitation() throws Exception {
+        // Following test cases are not for intentional behavior but checks for preventing the
+        // behavior from becoming worse.
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("ja-Hrkt");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("ja-Jpan"));
+            availableLocales.add(Locale.forLanguageTag("ja-Hrkt"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            // Should be ja-Jpan since it supports ja-Hrkt and listed before ja-Hrkt.
+            assertEquals(availableLocales.get(1), dest.get(0));
+        }
+        {
+            final LocaleList preferredLocales = LocaleList.forLanguageTags("zh-Hani");
+            final ArrayList<Locale> availableLocales = new ArrayList<>();
+            availableLocales.add(Locale.forLanguageTag("zh-Hans"));
+            availableLocales.add(Locale.forLanguageTag("zh-Hant"));
+            availableLocales.add(Locale.forLanguageTag("zh-Hanb"));
+            availableLocales.add(Locale.forLanguageTag("zh-Hani"));
+            final ArrayList<Locale> dest = new ArrayList<>();
+            LocaleUtils.filterByLanguage(availableLocales, sIdentityMapper, preferredLocales, dest);
+            assertEquals(1, dest.size());
+            // Should be zh-Hans since it supports zh-Hani. Also zh-Hant, zh-Hanb supports zh-Hani.
+            assertEquals(availableLocales.get(3), dest.get(0));
+        }
+    }
 }
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index f23fceb..6813c89 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1900,13 +1900,6 @@
 
     mutable Mutex               mLock;
 
-    // Mutex that controls access to the list of pre-filtered configurations
-    // to check when looking up entries.
-    // When iterating over a bag, the mLock mutex is locked. While mLock is locked,
-    // we do resource lookups.
-    // Mutex is not reentrant, so we must use a different lock than mLock.
-    mutable Mutex               mFilteredConfigLock;
-
     status_t                    mError;
 
     ResTable_config             mParams;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 49b3a51..bf6ff11 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -25,7 +25,6 @@
 #include <string.h>
 
 #include <limits>
-#include <memory>
 #include <type_traits>
 
 #include <androidfw/ByteBucketArray.h>
@@ -3147,9 +3146,6 @@
     StringPoolRef keyStr;
 };
 
-template <typename T>
-using SharedVector = std::shared_ptr<Vector<T>>;
-
 struct ResTable::Type
 {
     Type(const Header* _header, const Package* _package, size_t count)
@@ -3162,10 +3158,6 @@
     const uint32_t*                 typeSpecFlags;
     IdmapEntries                    idmapEntries;
     Vector<const ResTable_type*>    configs;
-
-    // The set of configurations that match the current parameters.
-    // This will be swapped with a new set when the parameters change.
-    SharedVector<const ResTable_type*> filteredConfigs;
 };
 
 struct ResTable::Package
@@ -4438,44 +4430,18 @@
 
 void ResTable::setParameters(const ResTable_config* params)
 {
-    AutoMutex _lock(mLock);
-    AutoMutex _lock2(mFilteredConfigLock);
-
+    mLock.lock();
     if (kDebugTableGetEntry) {
         ALOGI("Setting parameters: %s\n", params->toString().string());
     }
     mParams = *params;
-    for (size_t p = 0; p < mPackageGroups.size(); p++) {
-        PackageGroup* packageGroup = mPackageGroups.editItemAt(p);
+    for (size_t i=0; i<mPackageGroups.size(); i++) {
         if (kDebugTableNoisy) {
-            ALOGI("CLEARING BAGS FOR GROUP %zu!", p);
+            ALOGI("CLEARING BAGS FOR GROUP %zu!", i);
         }
-        packageGroup->clearBagCache();
-
-        for (size_t t = 0; t < packageGroup->types.size(); t++) {
-            TypeList& typeList = packageGroup->types.editItemAt(t);
-            for (size_t ts = 0; ts < typeList.size(); ts++) {
-                Type* type = typeList.editItemAt(ts);
-
-                SharedVector<const ResTable_type*> newFilteredConfigs =
-                        std::make_shared<Vector<const ResTable_type*>>();
-                for (size_t ti = 0; ti < type->configs.size(); ti++) {
-                    ResTable_config config;
-                    config.copyFromDtoH(type->configs[ti]->config);
-
-                    if (config.match(mParams)) {
-                        newFilteredConfigs->add(type->configs[ti]);
-                    }
-                }
-
-                if (kDebugTableNoisy) {
-                    ALOGD("Updating pkg=%zu type=%zu with %zu filtered configs",
-                          p, t, newFilteredConfigs->size());
-                }
-                type->filteredConfigs = newFilteredConfigs;
-            }
-        }
+        mPackageGroups[i]->clearBagCache();
     }
+    mLock.unlock();
 }
 
 void ResTable::getParameters(ResTable_config* params) const
@@ -6008,29 +5974,9 @@
             specFlags = -1;
         }
 
-        const Vector<const ResTable_type*>* candidateConfigs = &typeSpec->configs;
-
-        SharedVector<const ResTable_type*> filteredConfigs;
-        if (config && memcmp(&mParams, config, sizeof(mParams)) == 0) {
-            // Grab the lock first so we can safely get the current filtered list.
-            AutoMutex _lock(mFilteredConfigLock);
-
-            // This configuration is equal to the one we have previously cached for,
-            // so use the filtered configs.
-
-            if (typeSpec->filteredConfigs) {
-                // Grab a reference to the shared_ptr so it doesn't get destroyed while
-                // going through this list.
-                filteredConfigs = typeSpec->filteredConfigs;
-
-                // Use this filtered list.
-                candidateConfigs = filteredConfigs.get();
-            }
-        }
-
-        const size_t numConfigs = candidateConfigs->size();
+        const size_t numConfigs = typeSpec->configs.size();
         for (size_t c = 0; c < numConfigs; c++) {
-            const ResTable_type* const thisType = candidateConfigs->itemAt(c);
+            const ResTable_type* const thisType = typeSpec->configs[c];
             if (thisType == NULL) {
                 continue;
             }
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 5642170..fd5856a 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -203,8 +203,9 @@
         mCanvasState.setClippingOutline(mAllocator, &(properties.getOutline()));
     }
 
-    bool quickRejected = properties.getClipToBounds()
-            && mCanvasState.quickRejectConservative(0, 0, width, height);
+    bool quickRejected = mCanvasState.currentSnapshot()->getRenderTargetClip().isEmpty()
+            || (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()) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 8206d23..69d4487 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2730,15 +2730,16 @@
      * this abstract class and register it with
      * {@link AudioManager#registerAudioRecordingCallback(AudioRecordingCallback, Handler)}
      * to be notified.
-     * Use {@link AudioManager#getActiveRecordConfigurations()} to query the current configuration.
+     * Use {@link AudioManager#getActiveRecordingConfigurations()} to query the current
+     * configuration.
      */
     public static abstract class AudioRecordingCallback {
         /**
          * Called whenever the device recording configuration has changed.
          * @param configs array containing the results of
-         *      {@link AudioManager#getActiveRecordConfigurations()}.
+         *      {@link AudioManager#getActiveRecordingConfigurations()}.
          */
-        public void onRecordConfigChanged(AudioRecordConfiguration[] configs) {}
+        public void onRecordConfigChanged(AudioRecordingConfiguration[] configs) {}
     }
 
     private static class AudioRecordingCallbackInfo {
@@ -2752,10 +2753,10 @@
 
     private final static class RecordConfigChangeCallbackData {
         final AudioRecordingCallback mCb;
-        final AudioRecordConfiguration[] mConfigs;
+        final AudioRecordingConfiguration[] mConfigs;
 
         RecordConfigChangeCallbackData(AudioRecordingCallback cb,
-                AudioRecordConfiguration[] configs) {
+                AudioRecordingConfiguration[] configs) {
             mCb = cb;
             mConfigs = configs;
         }
@@ -2838,10 +2839,10 @@
      * @return a non-null array of recording configurations. An array of length 0 indicates there is
      *     no recording active when queried.
      */
-    public @NonNull AudioRecordConfiguration[] getActiveRecordConfigurations() {
+    public @NonNull AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
         final IAudioService service = getService();
         try {
-            return service.getActiveRecordConfigurations();
+            return service.getActiveRecordingConfigurations();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2896,7 +2897,7 @@
 
     private final IRecordingConfigDispatcher mRecCb = new IRecordingConfigDispatcher.Stub() {
 
-        public void dispatchRecordingConfigChange(AudioRecordConfiguration[] configs) {
+        public void dispatchRecordingConfigChange(AudioRecordingConfiguration[] configs) {
             synchronized(mRecordCallbackLock) {
                 if (mRecordCallbackList != null) {
                     for (int i=0 ; i < mRecordCallbackList.size() ; i++) {
diff --git a/media/java/android/media/AudioRecordConfiguration.aidl b/media/java/android/media/AudioRecordingConfiguration.aidl
similarity index 93%
rename from media/java/android/media/AudioRecordConfiguration.aidl
rename to media/java/android/media/AudioRecordingConfiguration.aidl
index afe912b..c63d30b 100644
--- a/media/java/android/media/AudioRecordConfiguration.aidl
+++ b/media/java/android/media/AudioRecordingConfiguration.aidl
@@ -15,4 +15,4 @@
 
 package android.media;
 
-parcelable AudioRecordConfiguration;
+parcelable AudioRecordingConfiguration;
diff --git a/media/java/android/media/AudioRecordConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
similarity index 82%
rename from media/java/android/media/AudioRecordConfiguration.java
rename to media/java/android/media/AudioRecordingConfiguration.java
index de78a5a..cd6f95a 100644
--- a/media/java/android/media/AudioRecordConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -27,13 +27,13 @@
 import java.util.Objects;
 
 /**
- * The AudioRecordConfiguration class collects the information describing an audio recording
+ * The AudioRecordingConfiguration class collects the information describing an audio recording
  * session. This information is returned through the
- * {@link AudioManager#getActiveRecordConfigurations()} method.
+ * {@link AudioManager#getActiveRecordingConfigurations()} method.
  *
  */
-public final class AudioRecordConfiguration implements Parcelable {
-    private final static String TAG = new String("AudioRecordConfiguration");
+public final class AudioRecordingConfiguration implements Parcelable {
+    private final static String TAG = new String("AudioRecordingConfiguration");
 
     private final int mSessionId;
 
@@ -47,7 +47,7 @@
     /**
      * @hide
      */
-    public AudioRecordConfiguration(int session, int source, AudioFormat devFormat,
+    public AudioRecordingConfiguration(int session, int source, AudioFormat devFormat,
             AudioFormat clientFormat, int patchHandle) {
         mSessionId = session;
         mClientSource = source;
@@ -136,18 +136,18 @@
         return null;
     }
 
-    public static final Parcelable.Creator<AudioRecordConfiguration> CREATOR
-            = new Parcelable.Creator<AudioRecordConfiguration>() {
+    public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
+            = new Parcelable.Creator<AudioRecordingConfiguration>() {
         /**
-         * Rebuilds an AudioRecordConfiguration previously stored with writeToParcel().
-         * @param p Parcel object to read the AudioRecordConfiguration from
-         * @return a new AudioRecordConfiguration created from the data in the parcel
+         * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel().
+         * @param p Parcel object to read the AudioRecordingConfiguration from
+         * @return a new AudioRecordingConfiguration created from the data in the parcel
          */
-        public AudioRecordConfiguration createFromParcel(Parcel p) {
-            return new AudioRecordConfiguration(p);
+        public AudioRecordingConfiguration createFromParcel(Parcel p) {
+            return new AudioRecordingConfiguration(p);
         }
-        public AudioRecordConfiguration[] newArray(int size) {
-            return new AudioRecordConfiguration[size];
+        public AudioRecordingConfiguration[] newArray(int size) {
+            return new AudioRecordingConfiguration[size];
         }
     };
 
@@ -170,7 +170,7 @@
         dest.writeInt(mPatchHandle);
     }
 
-    private AudioRecordConfiguration(Parcel in) {
+    private AudioRecordingConfiguration(Parcel in) {
         mSessionId = in.readInt();
         mClientSource = in.readInt();
         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
@@ -181,9 +181,9 @@
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
-        if (o == null || !(o instanceof AudioRecordConfiguration)) return false;
+        if (o == null || !(o instanceof AudioRecordingConfiguration)) return false;
 
-        AudioRecordConfiguration that = (AudioRecordConfiguration) o;
+        AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
 
         return ((mSessionId == that.mSessionId)
                 && (mClientSource == that.mClientSource)
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 987a8b6..97f670b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -20,7 +20,7 @@
 import android.bluetooth.BluetoothDevice;
 import android.content.ComponentName;
 import android.media.AudioAttributes;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -164,5 +164,5 @@
 
     oneway void unregisterRecordingCallback(in IRecordingConfigDispatcher rcdb);
 
-    AudioRecordConfiguration[] getActiveRecordConfigurations();
+    AudioRecordingConfiguration[] getActiveRecordingConfigurations();
 }
diff --git a/media/java/android/media/IRecordingConfigDispatcher.aidl b/media/java/android/media/IRecordingConfigDispatcher.aidl
index eaa92ca..e803283 100644
--- a/media/java/android/media/IRecordingConfigDispatcher.aidl
+++ b/media/java/android/media/IRecordingConfigDispatcher.aidl
@@ -16,7 +16,7 @@
 
 package android.media;
 
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 
 /**
  * AIDL for the RecordingActivity monitor in AudioService to signal audio recording updates.
@@ -25,6 +25,6 @@
  */
 oneway interface IRecordingConfigDispatcher {
 
-    void dispatchRecordingConfigChange(in AudioRecordConfiguration[] configs);
+    void dispatchRecordingConfigChange(in AudioRecordingConfiguration[] configs);
 
 }
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 4c6f0e6..c73cad4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -2074,6 +2074,16 @@
          */
         public static final int ERROR_SESSION_NOT_OPENED = 5;
 
+        /**
+         * This indicates that an operation was attempted that could not be
+         * supported by the crypto system of the device in its current
+         * configuration.  It may occur when the license policy requires
+         * device security features that aren't supported by the device,
+         * or due to an internal error in the crypto system that prevents
+         * the specified security policy from being met.
+         */
+        public static final int ERROR_UNSUPPORTED_OPERATION = 6;
+
         /** @hide */
         @IntDef({
             ERROR_NO_KEY,
@@ -2081,6 +2091,7 @@
             ERROR_RESOURCE_BUSY,
             ERROR_INSUFFICIENT_OUTPUT_PROTECTION,
             ERROR_SESSION_NOT_OPENED,
+            ERROR_UNSUPPORTED_OPERATION
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface CryptoErrorCode {}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index ada0e2c..3642155 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -1116,7 +1116,7 @@
                 }
             }
             mCallbacks.add(callback);
-            mOptionsList.add(options);
+            mOptionsList.add(options == null ? null : new Bundle(options));
         }
 
         public boolean removeCallback(Bundle options) {
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 63e3edc..7c9591d 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -349,12 +349,12 @@
     /**
      * Returns the number of tuners this TV input has.
      *
-     * <p>This method is valid only for the input of type {@link #TYPE_TUNER}.
+     * <p>This method is valid only for inputs of type {@link #TYPE_TUNER}. For inputs of other
+     * types, it returns 0.
      *
      * <p>Tuners correspond to physical/logical resources that allow reception of TV signal. Having
      * <i>N</i> tuners means that the TV input is capable of receiving <i>N</i> different channels
      * concurrently.
-     *
      */
     public int getTunerCount() {
         return mTunerCount;
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 6a44b1e..e623353 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -121,6 +121,8 @@
 
     /**
      * Returns the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * @throws IllegalStateException if not called on an audio track
      */
     public final int getAudioChannelCount() {
         if (mType != TYPE_AUDIO) {
@@ -131,6 +133,8 @@
 
     /**
      * Returns the audio sample rate, in the unit of Hz. Valid only for {@link #TYPE_AUDIO} tracks.
+     *
+     * @throws IllegalStateException if not called on an audio track
      */
     public final int getAudioSampleRate() {
         if (mType != TYPE_AUDIO) {
@@ -142,6 +146,8 @@
     /**
      * Returns the width of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final int getVideoWidth() {
         if (mType != TYPE_VIDEO) {
@@ -153,6 +159,8 @@
     /**
      * Returns the height of the video, in the unit of pixels. Valid only for {@link #TYPE_VIDEO}
      * tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final int getVideoHeight() {
         if (mType != TYPE_VIDEO) {
@@ -164,6 +172,8 @@
     /**
      * Returns the frame rate of the video, in the unit of fps (frames per second). Valid only for
      * {@link #TYPE_VIDEO} tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final float getVideoFrameRate() {
         if (mType != TYPE_VIDEO) {
@@ -175,6 +185,8 @@
     /**
      * Returns the pixel aspect ratio (the ratio of a pixel's width to its height) of the video.
      * Valid only for {@link #TYPE_VIDEO} tracks.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final float getVideoPixelAspectRatio() {
         if (mType != TYPE_VIDEO) {
@@ -189,6 +201,8 @@
      *
      * <p>The complete list of values are defined in ETSI TS 101 154 V1.7.1 Annex B, ATSC A/53 Part
      * 4 and SMPTE 2016-1-2007.
+     *
+     * @throws IllegalStateException if not called on a video track
      */
     public final byte getVideoActiveFormatDescription() {
         if (mType != TYPE_VIDEO) {
@@ -268,6 +282,8 @@
          * @param type The type of the track.
          * @param id The ID of the track that uniquely identifies the current track among all the
          *            other tracks in the same TV program.
+         * @throws IllegalArgumentException if the type is not any of {@link #TYPE_AUDIO},
+         *                                  {@link #TYPE_VIDEO} and {@link #TYPE_SUBTITLE}
          */
         public Builder(int type, @NonNull String id) {
             if (type != TYPE_AUDIO
@@ -304,6 +320,7 @@
          * Sets the audio channel count. Valid only for {@link #TYPE_AUDIO} tracks.
          *
          * @param audioChannelCount The audio channel count.
+         * @throws IllegalStateException if not called on an audio track
          */
         public final Builder setAudioChannelCount(int audioChannelCount) {
             if (mType != TYPE_AUDIO) {
@@ -318,6 +335,7 @@
          * tracks.
          *
          * @param audioSampleRate The audio sample rate.
+         * @throws IllegalStateException if not called on an audio track
          */
         public final Builder setAudioSampleRate(int audioSampleRate) {
             if (mType != TYPE_AUDIO) {
@@ -332,6 +350,7 @@
          * tracks.
          *
          * @param videoWidth The width of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoWidth(int videoWidth) {
             if (mType != TYPE_VIDEO) {
@@ -346,6 +365,7 @@
          * tracks.
          *
          * @param videoHeight The height of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoHeight(int videoHeight) {
             if (mType != TYPE_VIDEO) {
@@ -360,6 +380,7 @@
          * {@link #TYPE_VIDEO} tracks.
          *
          * @param videoFrameRate The frame rate of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoFrameRate(float videoFrameRate) {
             if (mType != TYPE_VIDEO) {
@@ -379,6 +400,7 @@
          * pixel aspect ratio for most video formats.
          *
          * @param videoPixelAspectRatio The pixel aspect ratio of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoPixelAspectRatio(float videoPixelAspectRatio) {
             if (mType != TYPE_VIDEO) {
@@ -398,6 +420,7 @@
          * 4 and SMPTE 2016-1-2007.
          *
          * @param videoActiveFormatDescription The AFD code of the video.
+         * @throws IllegalStateException if not called on a video track
          */
         public final Builder setVideoActiveFormatDescription(byte videoActiveFormatDescription) {
             if (mType != TYPE_VIDEO) {
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index d6994b3..810996e 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -65,6 +65,7 @@
     jint cryptoErrorResourceBusy;
     jint cryptoErrorInsufficientOutputProtection;
     jint cryptoErrorSessionNotOpened;
+    jint cryptoErrorUnsupportedOperation;
 } gCryptoErrorCodes;
 
 static struct CodecActionCodes {
@@ -869,6 +870,10 @@
             err = gCryptoErrorCodes.cryptoErrorSessionNotOpened;
             defaultMsg = "Attempted to use a closed session";
             break;
+        case ERROR_DRM_CANNOT_HANDLE:
+            err = gCryptoErrorCodes.cryptoErrorUnsupportedOperation;
+            defaultMsg = "Operation not supported in this configuration";
+            break;
         default:  /* Other negative DRM error codes go out as is. */
             break;
     }
@@ -1773,6 +1778,11 @@
     gCryptoErrorCodes.cryptoErrorSessionNotOpened =
         env->GetStaticIntField(clazz.get(), field);
 
+    field = env->GetStaticFieldID(clazz.get(), "ERROR_UNSUPPORTED_OPERATION", "I");
+    CHECK(field != NULL);
+    gCryptoErrorCodes.cryptoErrorUnsupportedOperation =
+        env->GetStaticIntField(clazz.get(), field);
+
     clazz.reset(env->FindClass("android/media/MediaCodec$CodecException"));
     CHECK(clazz.get() != NULL);
     field = env->GetStaticFieldID(clazz.get(), "ACTION_TRANSIENT", "I");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index c17be06..c56a12f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -282,6 +282,24 @@
      * Launches an intent to view the specified document.
      */
     private void openDocument(DocumentInfo doc, Model model) {
+
+        // Provide specialized handling of downloaded APKs This sends the APK
+        // details off to get extra security information added, and finally
+        // to be handled by the package manager.
+        if (MimePredicate.isApkType(doc.mimeType)) {
+            // First try managing the document; we expect manager to filter
+            // based on authority, so we don't grant.
+            final Intent manage = new Intent(DocumentsContract.ACTION_MANAGE_DOCUMENT);
+            manage.setData(doc.derivedUri);
+
+            try {
+                startActivity(manage);
+                return;
+            } catch (ActivityNotFoundException ex) {
+                // fall back to regular handling below.
+            }
+        }
+
         Intent intent = new QuickViewIntentBuilder(
                 getPackageManager(), getResources(), doc, model).build();
 
@@ -296,7 +314,7 @@
             }
         }
 
-        // Fallback to traditional VIEW action...
+        // Fall back to traditional VIEW action...
         intent = new Intent(Intent.ACTION_VIEW);
         intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
         intent.setData(doc.derivedUri);
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index 9df55a0..2f202e7 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
@@ -16,12 +16,15 @@
 
 package com.android.documentsui;
 
+import android.annotation.Nullable;
+
 import com.android.documentsui.model.DocumentInfo;
 import com.android.internal.util.Predicate;
 
 public class MimePredicate implements Predicate<DocumentInfo> {
     private final String[] mFilters;
 
+    private static final String APK_TYPE = "application/vnd.android.package-archive";
     /**
      * MIME types that are visual in nature. For example, they should always be
      * shown as thumbnails in list mode.
@@ -92,4 +95,8 @@
             return false;
         }
     }
+
+    public static boolean isApkType(@Nullable String mimeType) {
+        return APK_TYPE.equals(mimeType);
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 83838d3..1967923 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -17,7 +17,6 @@
 package com.android.documentsui.dirlist;
 
 import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_MANAGE;
 import static com.android.documentsui.State.MODE_GRID;
 import static com.android.documentsui.State.MODE_LIST;
 import static com.android.documentsui.State.SORT_ORDER_UNKNOWN;
@@ -1535,7 +1534,7 @@
                         mRoot.authority, mRoot.rootId, mQuery)
                         : DocumentsContract.buildChildDocumentsUri(
                                 mDocument.authority, mDocument.documentId);
-                if (state.action == ACTION_MANAGE) {
+                if (mTuner.enableManagedMode()) {
                     contentsUri = DocumentsContract.setManageMode(contentsUri);
                 }
                 return new DirectoryLoader(
@@ -1544,6 +1543,7 @@
             case TYPE_RECENT_OPEN:
                 final RootsCache roots = DocumentsApplication.getRootsCache(context);
                 return new RecentsLoader(context, roots, state);
+
             default:
                 throw new IllegalStateException("Unknown type " + mType);
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
index f99ec85c..adaa850 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/FragmentTuner.java
@@ -82,6 +82,12 @@
     abstract void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch);
 
     /**
+     * When managed mode is enabled, active downloads will be visible in the UI.
+     * Presumably this should only be true when in the downloads directory.
+     */
+    abstract boolean enableManagedMode();
+
+    /**
      * Provides support for Platform specific specializations of DirectoryFragment.
      */
     private static final class DocumentsTuner extends FragmentTuner {
@@ -157,6 +163,11 @@
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
         }
+
+        @Override
+        public boolean enableManagedMode() {
+            return false;
+        }
     }
 
     /**
@@ -166,6 +177,7 @@
 
         public DownloadsTuner(Context context, State state) {
             super(context, state);
+            assert(state.action == ACTION_MANAGE);
         }
 
         @Override
@@ -192,6 +204,11 @@
 
         @Override
         void onModelLoaded(Model model, @ResultType int resultType, boolean isSearch) {}
+
+        @Override
+        public boolean enableManagedMode() {
+            return mState.stack.root != null && mState.stack.root.isDownloads();
+        }
     }
 
     /**
@@ -233,6 +250,17 @@
                 ((BaseActivity) mContext).setRootsDrawerOpen(true);
             }
         }
+
+        @Override
+        public boolean enableManagedMode() {
+            // When in downloads top level directory, we also show active downloads.
+            // And while we don't allow folders in Downloads, we do allow Zip files in
+            // downloads that themselves can be opened and viewed like directories.
+            // This method helps us understand when to kick in on those special behaviors.
+            return mState.stack.root != null
+                    && mState.stack.root.isDownloads()
+                    && mState.stack.size() == 1;
+        }
     }
 
     private static boolean isDirectory(String mimeType) {
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index 498471e..2486209 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -21,6 +21,7 @@
 
 import android.os.RemoteException;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 import android.view.KeyEvent;
 
 import com.android.documentsui.model.RootInfo;
@@ -139,6 +140,7 @@
     }
 
     // Tests that pressing tab switches focus between the roots and directory listings.
+    @Suppress
     public void testKeyboard_tab() throws Exception {
         bots.main.pressKey(KeyEvent.KEYCODE_TAB);
         bots.roots.assertHasFocus();
@@ -147,6 +149,7 @@
     }
 
     // Tests that arrow keys do not switch focus away from the dir list.
+    @Suppress
     public void testKeyboard_arrowsDirList() throws Exception {
         for (int i = 0; i < 10; i++) {
             bots.main.pressKey(KeyEvent.KEYCODE_DPAD_LEFT);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
index 73c1c5f..621410a 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/RootsUiTest.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.StubProvider.ROOT_1_ID;
 
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 @LargeTest
 public class RootsUiTest extends ActivityTest<FilesActivity> {
@@ -44,6 +45,7 @@
         assertDefaultContentOfTestDir0();
     }
 
+    @Suppress
     public void testRootChanged_ClearSelection() throws Exception {
         bots.directory.selectDocument(fileName1);
         bots.main.assertInActionMode(true);
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
index 8c3fd90..a9451a6 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/SearchViewUiTest.java
@@ -20,6 +20,7 @@
 import static com.android.documentsui.StubProvider.ROOT_1_ID;
 
 import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.Suppress;
 
 @LargeTest
 public class SearchViewUiTest extends ActivityTest<FilesActivity> {
@@ -28,6 +29,7 @@
         super(FilesActivity.class);
     }
 
+    @Suppress
     public void testSearchView_ExpandsOnClick() throws Exception {
         bots.main.openSearchView();
         bots.main.assertSearchTextFiledAndIcon(true, false);
@@ -41,6 +43,7 @@
         bots.main.assertSearchTextFiledAndIcon(false, true);
     }
 
+    @Suppress
     public void testSearchView_ClearsTextOnBack() throws Exception {
         String query = "file2";
         bots.main.openSearchView();
@@ -51,6 +54,7 @@
         bots.main.assertSearchTextFiledAndIcon(false, true);
     }
 
+    @Suppress
     public void testSearch_ResultsFound() throws Exception {
         initTestFiles();
         assertDefaultContentOfTestDir0();
@@ -68,6 +72,7 @@
         bots.main.assertSearchTextField(false, query);
     }
 
+    @Suppress
     public void testSearchResultsFound_ClearsOnBack() throws Exception {
         initTestFiles();
         assertDefaultContentOfTestDir0();
@@ -82,6 +87,7 @@
         assertDefaultContentOfTestDir0();
     }
 
+    @Suppress
     public void testSearch_NoResults() throws Exception {
         initTestFiles();
         assertDefaultContentOfTestDir0();
@@ -101,6 +107,7 @@
         bots.main.assertSearchTextField(false, query);
     }
 
+    @Suppress
     public void testSearchNoResults_ClearsOnBack() throws Exception {
         initTestFiles();
         assertDefaultContentOfTestDir0();
@@ -116,6 +123,7 @@
         assertDefaultContentOfTestDir0();
     }
 
+    @Suppress
     public void testSearchResultsFound_ClearsOnDirectoryChange() throws Exception {
         initTestFiles();
         assertDefaultContentOfTestDir0();
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index af9d251..1bdb6d8 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -63,7 +63,7 @@
             android:name=".ui.PrintActivity"
             android:configChanges="screenSize|smallestScreenSize|orientation"
             android:permission="android.permission.BIND_PRINT_SPOOLER_SERVICE"
-            android:theme="@style/PrintActivity">
+            android:theme="@style/Theme.PrintActivity">
             <intent-filter>
                 <action android:name="android.print.PRINT_DIALOG" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -74,7 +74,14 @@
         <activity
             android:name=".ui.SelectPrinterActivity"
             android:label="@string/all_printers_label"
-            android:theme="@android:style/Theme.Material.Settings"
+            android:theme="@style/Theme.SelectPrinterActivity"
+            android:exported="false">
+        </activity>
+
+        <activity
+            android:name=".ui.AddPrinterActivity"
+            android:label="@string/print_add_printer"
+            android:theme="@style/Theme.AddPrinterActivity"
             android:exported="false">
         </activity>
 
diff --git a/packages/PrintSpooler/res/drawable/ic_add.xml b/packages/PrintSpooler/res/drawable/ic_add.xml
index 1442b1b..f728e7d 100644
--- a/packages/PrintSpooler/res/drawable/ic_add.xml
+++ b/packages/PrintSpooler/res/drawable/ic_add.xml
@@ -21,5 +21,5 @@
         android:viewportHeight="24.0">
     <path
         android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
-        android:fillColor="#FFFFFF"/>
+        android:fillColor="?android:attr/colorAccent"/>
 </vector>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/drawable/ic_print.xml b/packages/PrintSpooler/res/drawable/ic_print.xml
index dc6e0fb..e5e4d075 100644
--- a/packages/PrintSpooler/res/drawable/ic_print.xml
+++ b/packages/PrintSpooler/res/drawable/ic_print.xml
@@ -16,4 +16,4 @@
 
 <bitmap xmlns:android="http://schemas.android.com/apk/res/android"
     android:src="@*android:drawable/ic_print"
-    android:tint="@color/promoted_action_background_color" />
+    android:tint="?android:attr/colorAccent" />
diff --git a/packages/PrintSpooler/res/drawable/page_selector_background.xml b/packages/PrintSpooler/res/drawable/page_selector_background.xml
index 7f1da31..4d32328 100644
--- a/packages/PrintSpooler/res/drawable/page_selector_background.xml
+++ b/packages/PrintSpooler/res/drawable/page_selector_background.xml
@@ -22,14 +22,14 @@
         android:state_selected="true">
         <bitmap
             android:src="@drawable/ic_check_circle"
-            android:tint="@color/promoted_action_background_color">
+            android:tint="?android:attr/colorAccent">
         </bitmap>
     </item>
 
     <item>
         <bitmap
             android:src="@drawable/ic_remove_circle"
-            android:tint="@color/promoted_action_background_color">
+            android:tint="?android:attr/colorAccent">
         </bitmap>
     </item>
 
diff --git a/packages/PrintSpooler/res/drawable/print_button_background.xml b/packages/PrintSpooler/res/drawable/print_button_background.xml
index aec8474..ad16547 100644
--- a/packages/PrintSpooler/res/drawable/print_button_background.xml
+++ b/packages/PrintSpooler/res/drawable/print_button_background.xml
@@ -18,7 +18,7 @@
     android:shape="oval">
 
     <solid
-        android:color="@color/promoted_action_background_color">
+        android:color="?android:attr/colorAccent">
     </solid>
 
     <size
diff --git a/packages/PrintSpooler/res/layout/add_printer_activity.xml b/packages/PrintSpooler/res/layout/add_printer_activity.xml
new file mode 100644
index 0000000..117bba1
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_activity.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingTop="16dip"
+        android:paddingBottom="16dip">
+
+    <ListView android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="-16dip"
+            android:id="@android:id/list" />
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_header.xml b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
new file mode 100644
index 0000000..ff342cb
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_header.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="?android:attr/listPreferredItemHeightSmall"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+        android:orientation="horizontal"
+        android:gravity="start|center_vertical">
+
+    <TextView android:id="@+id/text"
+            style="?android:attr/listSeparatorTextViewStyle" />
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/add_printer_list_item.xml b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
new file mode 100644
index 0000000..1a2e774
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/add_printer_list_item.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="horizontal"
+    android:gravity="start|center_vertical">
+
+    <ImageView
+        android:layout_width="24dip"
+        android:layout_height="24dip"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null"
+        android:layout_marginEnd="16dip"
+        android:src="@drawable/ic_add" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dip">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:text="@string/print_add_printer" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/all_print_services_list_item.xml b/packages/PrintSpooler/res/layout/all_print_services_list_item.xml
new file mode 100644
index 0000000..a2471c6
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/all_print_services_list_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeightSmall"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="horizontal"
+    android:gravity="start|center_vertical">
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="56dip">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:text="@string/all_services_title" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
new file mode 100644
index 0000000..73d0933
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/disabled_print_services_list_item.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="horizontal"
+    android:gravity="start|center_vertical">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="40dip"
+        android:layout_height="40dip"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dip">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:singleLine="true"
+            android:ellipsize="end" />
+
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+            android:text="@string/enable_print_service" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
new file mode 100644
index 0000000..c13487a
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/enabled_print_services_list_item.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:orientation="horizontal"
+    android:gravity="start|center_vertical">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="40dip"
+        android:layout_height="40dip"
+        android:layout_gravity="center_vertical"
+        android:contentDescription="@null" />
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="16dip">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceListItem"
+            android:singleLine="true"
+            android:ellipsize="end" />
+
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/title"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary" />
+
+    </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
index defbf8d..103c157 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_item.xml
@@ -16,10 +16,8 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
-      android:paddingStart="8dip"
-      android:paddingEnd="8dip"
-      android:minHeight="56dip"
+      android:layout_height="?android:attr/listPreferredItemHeightSmall"
+      style="?android:attr/spinnerItemStyle"
       android:orientation="horizontal"
       android:gravity="start|center_vertical">
 
@@ -33,10 +31,9 @@
         android:visibility="invisible">
     </ImageView>
 
-    <LinearLayout
+    <RelativeLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="vertical"
         android:layout_marginStart="8dip"
         android:duplicateParentState="true">
 
@@ -47,8 +44,6 @@
             android:textAppearance="?android:attr/textAppearanceMedium"
             android:singleLine="true"
             android:ellipsize="end"
-            android:textIsSelectable="false"
-            android:gravity="top|start"
             android:textColor="?android:attr/textColorPrimary"
             android:duplicateParentState="true">
         </TextView>
@@ -57,15 +52,15 @@
             android:id="@+id/subtitle"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_below="@id/title"
             android:textAppearance="?android:attr/textAppearanceSmall"
             android:singleLine="true"
             android:ellipsize="end"
-            android:textIsSelectable="false"
             android:visibility="gone"
             android:textColor="?android:attr/textColorSecondary"
             android:duplicateParentState="true">
         </TextView>
 
-    </LinearLayout>
+    </RelativeLayout>
 
 </LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
index 11fef2d..60f7088 100644
--- a/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
+++ b/packages/PrintSpooler/res/layout/printer_dropdown_prompt.xml
@@ -16,13 +16,10 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="fill_parent"
-      android:layout_height="wrap_content"
+      android:layout_height="?android:attr/listPreferredItemHeightSmall"
       android:textAppearance="?android:attr/textAppearanceMedium"
-      android:textIsSelectable="false"
       android:textColor="?android:attr/textColorPrimary"
       android:paddingStart="20dip"
-      android:paddingEnd="8dip"
-      android:minHeight="56dip"
-      android:orientation="horizontal"
+      style="?android:attr/spinnerItemStyle"
       android:text="@string/destination_default_text"
       android:gravity="start|center_vertical" />
diff --git a/packages/PrintSpooler/res/layout/printer_list_item.xml b/packages/PrintSpooler/res/layout/printer_list_item.xml
index 1209aa6..0784bab 100644
--- a/packages/PrintSpooler/res/layout/printer_list_item.xml
+++ b/packages/PrintSpooler/res/layout/printer_list_item.xml
@@ -16,10 +16,9 @@
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="?android:attr/listPreferredItemHeight"
     android:paddingStart="?android:attr/listPreferredItemPaddingStart"
     android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:minHeight="?android:attr/listPreferredItemHeight"
     android:orientation="horizontal"
     android:gravity="start|center_vertical">
 
@@ -28,18 +27,15 @@
         android:layout_width="40dip"
         android:layout_height="40dip"
         android:layout_gravity="center_vertical"
-        android:layout_marginTop="8dip"
-        android:layout_marginBottom="8dip"
         android:duplicateParentState="true"
         android:contentDescription="@null"
         android:visibility="invisible">
     </ImageView>
 
     <RelativeLayout
-        android:layout_width="0dip"
+        android:layout_width="fill_parent"
         android:layout_weight="1"
         android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
         android:layout_marginStart="16dip"
         android:duplicateParentState="true">
 
@@ -47,15 +43,9 @@
             android:id="@+id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textAppearance="?android:attr/textAppearanceListItem"
             android:singleLine="true"
             android:ellipsize="end"
-            android:textIsSelectable="false"
-            android:layout_alignParentTop="true"
-            android:layout_alignParentStart="true"
-            android:fadingEdge="horizontal"
-            android:textAlignment="viewStart"
-            android:textColor="?android:attr/textColorPrimary"
             android:duplicateParentState="true">
         </TextView>
 
@@ -64,30 +54,31 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_below="@id/title"
-            android:layout_alignParentStart="true"
-            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:textAppearance="?android:attr/textAppearanceListItemSecondary"
             android:singleLine="true"
             android:ellipsize="end"
-            android:textIsSelectable="false"
             android:visibility="gone"
-            android:textColor="?android:attr/textColorSecondary"
-            android:textAlignment="viewStart"
             android:duplicateParentState="true">
         </TextView>
 
     </RelativeLayout>
 
-    <ImageView
+    <!-- wrapper for image view to increase the touch target size -->
+    <LinearLayout
         android:id="@+id/more_info"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_vertical"
-        android:paddingLeft="16dip"
-        android:contentDescription="@string/printer_info_desc"
-        android:src="@drawable/ic_info"
-        android:tint="?android:attr/colorControlNormal"
-        android:tintMode="src_in"
+        android:layout_height="fill_parent"
         android:visibility="gone">
-    </ImageView>
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:paddingLeft="16dip"
+            android:contentDescription="@string/printer_info_desc"
+            android:src="@drawable/ic_info"
+            android:tint="?android:attr/colorControlNormal"
+            android:tintMode="src_in" />
+    </LinearLayout>
 
 </LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 77c500a..564802a 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -22,11 +22,7 @@
     <ListView
         android:id="@android:id/list"
         android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:scrollbarStyle="outsideOverlay"
-        android:cacheColorHint="@android:color/transparent"
-        android:scrollbarAlwaysDrawVerticalTrack="true" >
-    </ListView>
+        android:layout_height="fill_parent" />
 
     <FrameLayout
         android:id="@+id/empty_print_state"
@@ -63,9 +59,18 @@
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
                 android:indeterminate="true"
-                style="@android:style/Widget.DeviceDefault.Light.ProgressBar.Horizontal">
+                style="?android:attr/progressBarStyleHorizontal">
             </ProgressBar>
 
+            <Button
+                    android:id="@+id/button"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    style="?android:attr/buttonBarButtonStyle"
+                    android:textAppearance="?android:attr/textAppearanceSmall"
+                    android:text="@string/print_add_printer"
+                    android:textAllCaps="true" />
+
         </LinearLayout>
 
     </FrameLayout>
diff --git a/packages/PrintSpooler/res/menu/select_printer_activity.xml b/packages/PrintSpooler/res/menu/select_printer_activity.xml
index 15cc139..60dfdca 100644
--- a/packages/PrintSpooler/res/menu/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/menu/select_printer_activity.xml
@@ -26,12 +26,4 @@
         android:imeOptions="actionSearch">
     </item>
 
-    <item
-        android:id="@+id/action_add_printer"
-        android:title="@string/print_add_printer"
-        android:icon="@drawable/ic_add"
-        android:showAsAction="ifRoom"
-        android:alphabeticShortcut="a">
-    </item>
-
 </menu>
diff --git a/packages/PrintSpooler/res/values/colors.xml b/packages/PrintSpooler/res/values/colors.xml
index d1bec32..47e616e 100644
--- a/packages/PrintSpooler/res/values/colors.xml
+++ b/packages/PrintSpooler/res/values/colors.xml
@@ -22,8 +22,6 @@
 
     <color name="print_preview_background_color">#F2F1F2</color>
 
-    <color name="promoted_action_background_color">#FF80CBC4</color>
-
     <color name="material_grey_500">#ffa3a3a3</color>
 
 </resources>
diff --git a/packages/PrintSpooler/res/values/donottranslate.xml b/packages/PrintSpooler/res/values/donottranslate.xml
index 8069a1d..589043b 100644
--- a/packages/PrintSpooler/res/values/donottranslate.xml
+++ b/packages/PrintSpooler/res/values/donottranslate.xml
@@ -25,4 +25,6 @@
     <string name="mediasize_default">ISO_A4</string>
     <string name="mediasize_standard">@string/mediasize_standard_iso</string>
 
+    <string name="uri_package_details">market://details?id=%1$s</string>
+
 </resources>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 76292a1..4b56622 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -129,7 +129,7 @@
     <!-- Utterance to announce that the search box is hidden. This is spoken to a blind user. [CHAR LIMIT=none] -->
     <string name="print_search_box_hidden_utterance">Search box hidden</string>
 
-    <!-- Title of the action bar button to got to add a printer. [CHAR LIMIT=25] -->
+    <!-- Label of add printers button when no printers are found. [CHAR LIMIT=25] -->
     <string name="print_add_printer">Add printer</string>
 
     <!-- Title of the menu item to select a printer. [CHAR LIMIT=25] -->
@@ -151,12 +151,7 @@
     <string name="printer_info_desc">More information about this printer</string>
 
     <!-- Notification that print services as disabled. [CHAR LIMIT=50] -->
-    <string name="print_services_disabled_toast">Some print services are disabled.</string>
-
-    <!-- Add printer dialog  -->
-
-    <!-- Title for the alert dialog for selecting a print service. [CHAR LIMIT=50] -->
-    <string name="choose_print_service">Choose print service</string>
+    <string name="print_services_disabled_toast">Some print services are disabled</string>
 
     <!-- Title for the prompt shown as a placeholder if no printers are found while not searching. [CHAR LIMIT=50] -->
     <string name="print_searching_for_printers">Searching for printers</string>
@@ -167,6 +162,29 @@
     <!-- Title for the prompt shown as a placeholder if there are no printers while searching. [CHAR LIMIT=50] -->
     <string name="print_no_printers">No printers found</string>
 
+    <!-- Add printer activity -->
+
+    <!-- Subtitle for services that cannot add printers. [CHAR LIMIT=50] -->
+    <string name="cannot_add_printer">Cannot add printers</string>
+
+    <!-- Subtitle for services that can add printers. [CHAR LIMIT=50] -->
+    <string name="select_to_add_printers">Select to add printer</string>
+
+    <!-- Subtitle for disabled services. [CHAR LIMIT=50] -->
+    <string name="enable_print_service">Select to enable</string>
+
+    <!-- Header for the list of enabled print services. [CHAR LIMIT=50] -->
+    <string name="enabled_services_title">Enabled services</string>
+
+    <!-- Header for the list of recommended print services. [CHAR LIMIT=50] -->
+    <string name="recommended_services_title">Recommended services</string>
+
+    <!-- Header for the list of disabled print services. [CHAR LIMIT=50] -->
+    <string name="disabled_services_title">Disabled services</string>
+
+    <!-- Label for the list item that links to the list of all print services. [CHAR LIMIT=50] -->
+    <string name="all_services_title">All services</string>
+
     <!-- Notifications -->
 
     <!-- Template for the notification label for a printing print job. [CHAR LIMIT=25] -->
diff --git a/packages/PrintSpooler/res/values/styles.xml b/packages/PrintSpooler/res/values/styles.xml
new file mode 100644
index 0000000..1e63a67e
--- /dev/null
+++ b/packages/PrintSpooler/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?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>
+    <!-- Preference styles -->
+    <eat-comment/>
+
+    <style name="ListItemSecondary" parent="@android:style/TextAppearance.Material.Body1">
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="ListSeparator">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginTop">16dip</item>
+        <item name="android:layout_marginBottom">16dip</item>
+        <item name="android:textColor">?android:attr/colorAccent</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:textSize">14sp</item>
+    </style>
+</resources>
diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml
index 05de5b7..a968ffa 100644
--- a/packages/PrintSpooler/res/values/themes.xml
+++ b/packages/PrintSpooler/res/values/themes.xml
@@ -15,8 +15,17 @@
 -->
 
 <resources>
+    <style name="Theme.AddPrinterActivity" parent="@android:style/Theme.DeviceDefault.Light.Dialog">
+        <item name="android:listSeparatorTextViewStyle">@style/ListSeparator</item>
+        <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+    </style>
 
-    <style name="PrintActivity" parent="@android:style/Theme.DeviceDefault">
+    <style name="Theme.SelectPrinterActivity"
+           parent="android:style/Theme.DeviceDefault.Light.DarkActionBar">
+        <item name="android:textAppearanceListItemSecondary">@style/ListItemSecondary</item>
+    </style>
+
+    <style name="Theme.PrintActivity" parent="@android:style/Theme.DeviceDefault">
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowContentOverlay">@null</item>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
index ac97ad0..f8b1343 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java
@@ -862,6 +862,9 @@
                 // Take a note that the content is rendered.
                 renderedPage.state = RenderedPage.STATE_RENDERED;
 
+                // Invalidate all caches of the old state of the bitmap
+                mRenderedPage.content.invalidateSelf();
+
                 // Announce success if needed.
                 if (mCallback != null) {
                     mCallback.onPageContentAvailable(renderedPage.content);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
new file mode 100644
index 0000000..f2b3e6e
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -0,0 +1,563 @@
+/*
+ * 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.printspooler.ui;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ListActivity;
+import android.app.LoaderManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.database.DataSetObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.print.PrintManager;
+import android.print.PrintServicesLoader;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Adapter;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.printspooler.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This is an activity for adding a printer or. It consists of a list fed from three adapters:
+ * <ul>
+ *     <li>{@link #mEnabledServicesAdapter} for all enabled services. If a service has an {@link
+ *         PrintServiceInfo#getAddPrintersActivityName() add printer activity} this is started
+ *         when the item is clicked.</li>
+ *     <li>{@link #mDisabledServicesAdapter} for all disabled services. Once clicked the settings page
+ *         for this service is opened.</li>
+ *     <li>{@link RecommendedServicesAdapter} for a link to all services. If this item is clicked
+ *         the market app is opened to show all print services.</li>
+ * </ul>
+ */
+public class AddPrinterActivity extends ListActivity implements
+        LoaderManager.LoaderCallbacks<List<PrintServiceInfo>>,
+        AdapterView.OnItemClickListener {
+    private static final String LOG_TAG = "AddPrinterActivity";
+
+    /** Ids for the loaders */
+    private static final int LOADER_ID_ENABLED_SERVICES = 1;
+    private static final int LOADER_ID_DISABLED_SERVICES = 2;
+
+    /**
+     * The enabled services list. This is filled from the {@link #LOADER_ID_ENABLED_SERVICES}
+     * loader in {@link #onLoadFinished}.
+     */
+    private EnabledServicesAdapter mEnabledServicesAdapter;
+
+    /**
+     * The disabled services list. This is filled from the {@link #LOADER_ID_DISABLED_SERVICES}
+     * loader in {@link #onLoadFinished}.
+     */
+    private DisabledServicesAdapter mDisabledServicesAdapter;
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.add_printer_activity);
+
+        mEnabledServicesAdapter = new EnabledServicesAdapter();
+        mDisabledServicesAdapter = new DisabledServicesAdapter();
+
+        ArrayList<ActionAdapter> adapterList = new ArrayList<>(3);
+        adapterList.add(mEnabledServicesAdapter);
+        adapterList.add(new RecommendedServicesAdapter());
+        adapterList.add(mDisabledServicesAdapter);
+
+        setListAdapter(new CombinedAdapter(adapterList));
+
+        getListView().setOnItemClickListener(this);
+
+        getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, this);
+        getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, this);
+        // TODO: Load recommended services
+    }
+
+    @Override
+    public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+        switch (id) {
+            case LOADER_ID_ENABLED_SERVICES:
+                return new PrintServicesLoader(
+                        (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+                        PrintManager.ENABLED_SERVICES);
+            case LOADER_ID_DISABLED_SERVICES:
+                return new PrintServicesLoader(
+                        (PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+                        PrintManager.DISABLED_SERVICES);
+            // TODO: Load recommended services
+            default:
+                // not reached
+                return null;
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        ((ActionAdapter) getListAdapter()).performAction(position);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+            List<PrintServiceInfo> data) {
+        switch (loader.getId()) {
+            case LOADER_ID_ENABLED_SERVICES:
+                mEnabledServicesAdapter.updateData(data);
+                break;
+            case LOADER_ID_DISABLED_SERVICES:
+                mDisabledServicesAdapter.updateData(data);
+                break;
+            // TODO: Load recommended services
+            default:
+                // not reached
+        }
+    }
+
+    @Override
+    public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+        if (!isFinishing()) {
+            switch (loader.getId()) {
+                case LOADER_ID_ENABLED_SERVICES:
+                    mEnabledServicesAdapter.updateData(null);
+                    break;
+                case LOADER_ID_DISABLED_SERVICES:
+                    mDisabledServicesAdapter.updateData(null);
+                    break;
+                // TODO: Reset recommended services
+                default:
+                    // not reached
+            }
+        }
+    }
+
+    /**
+     * Marks an adapter that can can perform an action for a position in it's list.
+     */
+    private abstract class ActionAdapter extends BaseAdapter {
+        /**
+         * Perform the action for a position in the list.
+         *
+         * @param position The position of the item
+         */
+        abstract void performAction(@IntRange(from = 0) int position);
+
+        @Override
+        public boolean areAllItemsEnabled() {
+            return false;
+        }
+    }
+
+    /**
+     * An adapter presenting multiple sub adapters as a single combined adapter.
+     */
+    private class CombinedAdapter extends ActionAdapter {
+        /** The adapters to combine */
+        private final @NonNull ArrayList<ActionAdapter> mAdapters;
+
+        /**
+         * Create a combined adapter.
+         *
+         * @param adapters the list of adapters to combine
+         */
+        CombinedAdapter(@NonNull ArrayList<ActionAdapter> adapters) {
+            mAdapters = adapters;
+
+            final int numAdapters = mAdapters.size();
+            for (int i = 0; i < numAdapters; i++) {
+                mAdapters.get(i).registerDataSetObserver(new DataSetObserver() {
+                    @Override
+                    public void onChanged() {
+                        notifyDataSetChanged();
+                    }
+
+                    @Override
+                    public void onInvalidated() {
+                        notifyDataSetChanged();
+                    }
+                });
+            }
+        }
+
+        @Override
+        public int getCount() {
+            int totalCount = 0;
+
+            final int numAdapters = mAdapters.size();
+            for (int i = 0; i < numAdapters; i++) {
+                totalCount += mAdapters.get(i).getCount();
+            }
+
+            return totalCount;
+        }
+
+        /**
+         * Find the sub adapter and the position in the sub-adapter the position in the combined
+         * adapter refers to.
+         *
+         * @param position The position in the combined adapter
+         *
+         * @return The pair of adapter and position in sub adapter
+         */
+        private @NonNull Pair<ActionAdapter, Integer> getSubAdapter(int position) {
+            final int numAdapters = mAdapters.size();
+            for (int i = 0; i < numAdapters; i++) {
+                ActionAdapter adapter = mAdapters.get(i);
+
+                if (position < adapter.getCount()) {
+                    return new Pair<>(adapter, position);
+                } else {
+                    position -= adapter.getCount();
+                }
+            }
+
+            throw new IllegalArgumentException("Invalid position");
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            int numLowerViewTypes = 0;
+
+            final int numAdapters = mAdapters.size();
+            for (int i = 0; i < numAdapters; i++) {
+                Adapter adapter = mAdapters.get(i);
+
+                if (position < adapter.getCount()) {
+                    return numLowerViewTypes + adapter.getItemViewType(position);
+                } else {
+                    numLowerViewTypes += adapter.getViewTypeCount();
+                    position -= adapter.getCount();
+                }
+            }
+
+            throw new IllegalArgumentException("Invalid position");
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            int totalViewCount = 0;
+
+            final int numAdapters = mAdapters.size();
+            for (int i = 0; i < numAdapters; i++) {
+                totalViewCount += mAdapters.get(i).getViewTypeCount();
+            }
+
+            return totalViewCount;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+            return realPosition.first.getView(realPosition.second, convertView, parent);
+        }
+
+        @Override
+        public Object getItem(int position) {
+            Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+            return realPosition.first.getItem(realPosition.second);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+            return realPosition.first.isEnabled(realPosition.second);
+        }
+
+        @Override
+        public void performAction(@IntRange(from = 0) int position) {
+            Pair<ActionAdapter, Integer> realPosition = getSubAdapter(position);
+
+            realPosition.first.performAction(realPosition.second);
+        }
+    }
+    
+    /**
+     * Superclass for all adapters that just display a list of {@link PrintServiceInfo}.
+     */
+    private abstract class PrintServiceInfoAdapter extends ActionAdapter {
+        /**
+         * Raw data of the list.
+         *
+         * @see #updateData(List)
+         */
+        private @NonNull List<PrintServiceInfo> mServices;
+
+        /**
+         * Create a new adapter.
+         */
+        PrintServiceInfoAdapter() {
+            mServices = Collections.emptyList();
+        }
+
+        /**
+         * Update the data.
+         *
+         * @param services The new raw data.
+         */
+        void updateData(@Nullable List<PrintServiceInfo> services) {
+            if (services == null || services.isEmpty()) {
+                mServices = Collections.emptyList();
+            } else {
+                mServices = services;
+            }
+
+            notifyDataSetChanged();
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            if (position == 0) {
+                return 0;
+            } else {
+                return 1;
+            }
+        }
+
+        @Override
+        public int getCount() {
+            if (mServices.isEmpty()) {
+                return 0;
+            } else {
+                return mServices.size() + 1;
+            }
+        }
+
+        @Override
+        public Object getItem(int position) {
+            if (position == 0) {
+                return null;
+            } else {
+                return mServices.get(position - 1);
+            }
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return position != 0;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+    }
+
+    /**
+     * Adapter for the enabled services.
+     */
+    private class EnabledServicesAdapter extends PrintServiceInfoAdapter {
+        @Override
+        public void performAction(@IntRange(from = 0) int position) {
+            PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+            String addPrinterActivityName = service.getAddPrintersActivityName();
+
+            if (!TextUtils.isEmpty(addPrinterActivityName)) {
+                Intent intent = new Intent(Intent.ACTION_MAIN);
+                intent.setComponent(new ComponentName(service.getComponentName().getPackageName(),
+                        addPrinterActivityName));
+
+                try {
+                    startActivity(intent);
+                } catch (ActivityNotFoundException e) {
+                    Log.e(LOG_TAG, "Cannot start add printers activity", e);
+                }
+            }
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (position == 0) {
+                if (convertView == null) {
+                    convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+                            parent, false);
+                }
+
+                ((TextView) convertView.findViewById(R.id.text))
+                        .setText(R.string.enabled_services_title);
+
+                return convertView;
+            }
+
+            if (convertView == null) {
+                convertView = getLayoutInflater().inflate(R.layout.enabled_print_services_list_item,
+                        parent, false);
+            }
+
+            PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+
+            TextView title = (TextView) convertView.findViewById(R.id.title);
+            ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+            TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
+
+            title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
+            icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
+
+            if (TextUtils.isEmpty(service.getAddPrintersActivityName())) {
+                subtitle.setText(getString(R.string.cannot_add_printer));
+            } else {
+                subtitle.setText(getString(R.string.select_to_add_printers));
+            }
+
+            return convertView;
+        }
+    }
+
+    /**
+     * Adapter for the disabled services.
+     */
+    private class DisabledServicesAdapter extends PrintServiceInfoAdapter {
+        @Override
+        public void performAction(@IntRange(from = 0) int position) {
+            ((PrintManager) getSystemService(Context.PRINT_SERVICE)).setPrintServiceEnabled(
+                    ((PrintServiceInfo) getItem(position)).getComponentName(), true);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (position == 0) {
+                if (convertView == null) {
+                    convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+                            parent, false);
+                }
+
+                ((TextView) convertView.findViewById(R.id.text))
+                        .setText(R.string.disabled_services_title);
+
+                return convertView;
+            }
+
+            if (convertView == null) {
+                convertView = getLayoutInflater().inflate(
+                        R.layout.disabled_print_services_list_item, parent, false);
+            }
+
+            PrintServiceInfo service = (PrintServiceInfo) getItem(position);
+
+            TextView title = (TextView) convertView.findViewById(R.id.title);
+            ImageView icon = (ImageView) convertView.findViewById(R.id.icon);
+
+            title.setText(service.getResolveInfo().loadLabel(getPackageManager()));
+            icon.setImageDrawable(service.getResolveInfo().loadIcon(getPackageManager()));
+
+            return convertView;
+        }
+    }
+
+    /**
+     * Adapter for the recommended services.
+     */
+    private class RecommendedServicesAdapter extends ActionAdapter {
+        @Override
+        public int getCount() {
+            return 2;
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            if (position == 0) {
+                return 0;
+            } else {
+                return 1;
+            }
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return null;
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (position == 0) {
+                if (convertView == null) {
+                    convertView = getLayoutInflater().inflate(R.layout.add_printer_list_header,
+                            parent, false);
+                }
+
+                ((TextView) convertView.findViewById(R.id.text))
+                        .setText(R.string.recommended_services_title);
+
+                return convertView;
+            }
+
+            if (convertView == null) {
+                convertView = getLayoutInflater().inflate(R.layout.all_print_services_list_item,
+                        parent, false);
+            }
+
+            return convertView;
+        }
+
+        @Override
+        public boolean isEnabled(int position) {
+            return position != 0;
+        }
+
+        @Override
+        public void performAction(@IntRange(from = 0) int position) {
+            String searchUri = Settings.Secure
+                    .getString(getContentResolver(), Settings.Secure.PRINT_SERVICE_SEARCH_URI);
+
+            if (searchUri != null) {
+                try {
+                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)));
+                } catch (ActivityNotFoundException e) {
+                    Log.e(LOG_TAG, "Cannot start market", e);
+                }
+            }
+        }
+    }
+}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
index 46a2098..3b5513a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java
@@ -16,7 +16,10 @@
 
 package com.android.printspooler.ui;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.Activity;
+import android.app.LoaderManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Loader;
@@ -28,9 +31,11 @@
 import android.location.LocationRequest;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
 import android.print.PrintManager;
+import android.print.PrintServicesLoader;
 import android.print.PrinterDiscoverySession;
 import android.print.PrinterDiscoverySession.OnPrintersChangeListener;
 import android.print.PrinterId;
@@ -127,11 +132,11 @@
         }
     }
 
-    public FusedPrintersProvider(Context context) {
-        super(context);
+    public FusedPrintersProvider(Activity activity, int internalLoaderId) {
+        super(activity);
         mLocationLock = new Object();
-        mPersistenceManager = new PersistenceManager(context);
-        mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+        mPersistenceManager = new PersistenceManager(activity, internalLoaderId);
+        mLocationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
     }
 
     public void addHistoricalPrinter(PrinterInfo printer) {
@@ -383,7 +388,6 @@
         mPrinters.clear();
         if (mDiscoverySession != null) {
             mDiscoverySession.destroy();
-            mDiscoverySession = null;
         }
     }
 
@@ -499,7 +503,8 @@
         updatePrinters(mDiscoverySession.getPrinters(), newFavoritePrinters, getCurrentLocation());
     }
 
-    private final class PersistenceManager {
+    private final class PersistenceManager implements
+            LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
         private static final String PERSIST_FILE_NAME = "printer_history.xml";
 
         private static final String TAG_PRINTERS = "printers";
@@ -520,6 +525,15 @@
 
         private final AtomicFile mStatePersistFile;
 
+        /**
+         * Whether the enabled print services have been updated since last time the history was
+         * read.
+         */
+        private boolean mAreEnabledServicesUpdated;
+
+        /** The enabled services read when they were last updated */
+        private @NonNull List<PrintServiceInfo> mEnabledServices;
+
         private List<Pair<PrinterInfo, Location>> mHistoricalPrinters = new ArrayList<>();
 
         private boolean mReadHistoryCompleted;
@@ -528,9 +542,52 @@
 
         private volatile long mLastReadHistoryTimestamp;
 
-        private PersistenceManager(Context context) {
-            mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
+        private PersistenceManager(final Activity activity, final int internalLoaderId) {
+            mStatePersistFile = new AtomicFile(new File(activity.getFilesDir(),
                     PERSIST_FILE_NAME));
+
+            // Initialize enabled services to make sure they are set are the read task might be done
+            // before the loader updated the services the first time.
+            mEnabledServices = ((PrintManager) activity
+                    .getSystemService(Context.PRINT_SERVICE))
+                    .getPrintServices(PrintManager.ENABLED_SERVICES);
+
+            mAreEnabledServicesUpdated = true;
+
+            // Cannot start a loader while starting another, hence delay this loader
+            (new Handler(activity.getMainLooper())).post(new Runnable() {
+                @Override
+                public void run() {
+                    activity.getLoaderManager().initLoader(internalLoaderId, null,
+                            PersistenceManager.this);
+                }
+            });
+        }
+
+
+        @Override
+        public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+            return new PrintServicesLoader(
+                    (PrintManager) getContext().getSystemService(Context.PRINT_SERVICE),
+                    getContext(), PrintManager.ENABLED_SERVICES);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+                List<PrintServiceInfo> services) {
+            mAreEnabledServicesUpdated = true;
+            mEnabledServices = services;
+
+            // Ask the fused printer provider to reload which will cause the persistence manager to
+            // reload the history and reconsider the enabled services.
+            if (isStarted()) {
+                forceLoad();
+            }
+        }
+
+        @Override
+        public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+            // no data is cached
         }
 
         public boolean isReadHistoryInProgress() {
@@ -644,7 +701,8 @@
         }
 
         public boolean isHistoryChanged() {
-            return mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
+            return mAreEnabledServicesUpdated ||
+                    mLastReadHistoryTimestamp != mStatePersistFile.getBaseFile().lastModified();
         }
 
         /**
@@ -738,19 +796,15 @@
                 }
 
                 // Ignore printer records whose target services are not enabled.
-                PrintManager printManager = (PrintManager) getContext()
-                        .getSystemService(Context.PRINT_SERVICE);
-                List<PrintServiceInfo> services = printManager
-                        .getEnabledPrintServices();
-
                 Set<ComponentName> enabledComponents = new ArraySet<>();
-                final int installedServiceCount = services.size();
+                final int installedServiceCount = mEnabledServices.size();
                 for (int i = 0; i < installedServiceCount; i++) {
-                    ServiceInfo serviceInfo = services.get(i).getResolveInfo().serviceInfo;
+                    ServiceInfo serviceInfo = mEnabledServices.get(i).getResolveInfo().serviceInfo;
                     ComponentName componentName = new ComponentName(
                             serviceInfo.packageName, serviceInfo.name);
                     enabledComponents.add(componentName);
                 }
+                mAreEnabledServicesUpdated = false;
 
                 final int printerCount = printers.size();
                 for (int i = printerCount - 1; i >= 0; i--) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index 4b62f24..ad4823e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -22,11 +22,13 @@
 import android.app.DialogFragment;
 import android.app.Fragment;
 import android.app.FragmentTransaction;
+import android.app.LoaderManager;
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.Loader;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
@@ -51,10 +53,12 @@
 import android.print.PrintDocumentInfo;
 import android.print.PrintJobInfo;
 import android.print.PrintManager;
+import android.print.PrintServicesLoader;
 import android.print.PrinterCapabilitiesInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintService;
+import android.printservice.PrintServiceInfo;
 import android.provider.DocumentsContract;
 import android.text.Editable;
 import android.text.TextUtils;
@@ -94,7 +98,6 @@
 import com.android.printspooler.util.MediaSizeUtils;
 import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
 import com.android.printspooler.util.PageRangeUtils;
-import com.android.printspooler.util.PrintOptionUtils;
 import com.android.printspooler.widget.PrintContentView;
 import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
 import com.android.printspooler.widget.PrintContentView.OptionsStateController;
@@ -113,12 +116,14 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class PrintActivity extends Activity implements RemotePrintDocument.UpdateResultCallbacks,
         PrintErrorFragment.OnActionListener, PageAdapter.ContentCallbacks,
-        OptionsStateChangeListener, OptionsStateController {
+        OptionsStateChangeListener, OptionsStateController,
+        LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
     private static final String LOG_TAG = "PrintActivity";
 
     private static final boolean DEBUG = false;
@@ -129,6 +134,10 @@
 
     private static final String HAS_PRINTED_PREF = "has_printed";
 
+    private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 1;
+    private static final int LOADER_ID_PRINT_REGISTRY = 2;
+    private static final int LOADER_ID_PRINT_REGISTRY_INT = 3;
+
     private static final int ORIENTATION_PORTRAIT = 0;
     private static final int ORIENTATION_LANDSCAPE = 1;
 
@@ -139,7 +148,7 @@
     private static final int DEST_ADAPTER_MAX_ITEM_COUNT = 9;
 
     private static final int DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF = Integer.MAX_VALUE;
-    private static final int DEST_ADAPTER_ITEM_ID_ALL_PRINTERS = Integer.MAX_VALUE - 1;
+    private static final int DEST_ADAPTER_ITEM_ID_MORE = Integer.MAX_VALUE - 1;
 
     private static final int STATE_INITIALIZING = 0;
     private static final int STATE_CONFIGURING = 1;
@@ -236,6 +245,15 @@
 
     private int mUiState = UI_STATE_PREVIEW;
 
+    /** Observer for changes to the printers */
+    private PrintersObserver mPrintersObserver;
+
+    /** Advances options activity name for current printer */
+    private ComponentName mAdvancedPrintOptionsActivity;
+
+    /** Whether at least one print services is enabled or not */
+    private boolean mArePrintServicesEnabled;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -275,6 +293,8 @@
                 }
             }
         });
+
+        getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
     }
 
     private void onConnectedToPrintSpooler(final IBinder documentAdapter) {
@@ -289,7 +309,7 @@
             public void run() {
                 onPrinterRegistryReady(documentAdapter);
             }
-        });
+        }, LOADER_ID_PRINT_REGISTRY, LOADER_ID_PRINT_REGISTRY_INT);
     }
 
     private void onPrinterRegistryReady(IBinder documentAdapter) {
@@ -713,15 +733,12 @@
     }
 
     private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
-        ComponentName serviceName = printer.getId().getServiceName();
-
-        String activityName = PrintOptionUtils.getAdvancedOptionsActivityName(this, serviceName);
-        if (TextUtils.isEmpty(activityName)) {
+        if (mAdvancedPrintOptionsActivity == null) {
             return;
         }
 
         Intent intent = new Intent(Intent.ACTION_MAIN);
-        intent.setComponent(new ComponentName(serviceName.getPackageName(), activityName));
+        intent.setComponent(mAdvancedPrintOptionsActivity);
 
         List<ResolveInfo> resolvedActivities = getPackageManager()
                 .queryIntentActivities(intent, 0);
@@ -1191,7 +1208,8 @@
         mCopiesEditText.addTextChangedListener(new EditTextWatcher());
 
         // Destination.
-        mDestinationSpinnerAdapter.registerDataSetObserver(new PrintersObserver());
+        mPrintersObserver = new PrintersObserver();
+        mDestinationSpinnerAdapter.registerDataSetObserver(mPrintersObserver);
         mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
         mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
         mDestinationSpinner.setOnItemSelectedListener(itemSelectedListener);
@@ -1279,6 +1297,59 @@
         }
     }
 
+    @Override
+    public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+        return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+                PrintManager.ENABLED_SERVICES);
+    }
+
+    @Override
+    public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+            List<PrintServiceInfo> services) {
+        ComponentName newAdvancedPrintOptionsActivity = null;
+        if (mCurrentPrinter != null && services != null) {
+            final int numServices = services.size();
+            for (int i = 0; i < numServices; i++) {
+                PrintServiceInfo service = services.get(i);
+
+                if (service.getComponentName().equals(mCurrentPrinter.getId().getServiceName())) {
+                    String advancedOptionsActivityName = service.getAdvancedOptionsActivityName();
+
+                    if (!TextUtils.isEmpty(advancedOptionsActivityName)) {
+                        newAdvancedPrintOptionsActivity = new ComponentName(
+                                service.getComponentName().getPackageName(),
+                                advancedOptionsActivityName);
+
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (!Objects.equals(newAdvancedPrintOptionsActivity, mAdvancedPrintOptionsActivity)) {
+            mAdvancedPrintOptionsActivity = newAdvancedPrintOptionsActivity;
+            updateOptionsUi();
+        }
+
+        boolean newArePrintServicesEnabled = services != null && !services.isEmpty();
+        if (mArePrintServicesEnabled != newArePrintServicesEnabled) {
+            mArePrintServicesEnabled = newArePrintServicesEnabled;
+
+            // Reload mDestinationSpinnerAdapter as mArePrintServicesEnabled changed and the adapter
+            // reads that in DestinationAdapter#getMoreItemTitle
+            if (mDestinationSpinnerAdapter != null) {
+                mDestinationSpinnerAdapter.notifyDataSetChanged();
+            }
+        }
+    }
+
+    @Override
+    public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+        if (!isFinishing()) {
+            onLoadFinished(loader, null);
+        }
+    }
+
     /**
      * A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
      * dismissed if the same {@link PrintService} gets approved by another
@@ -1718,9 +1789,7 @@
         }
 
         // Advanced print options
-        ComponentName serviceName = mCurrentPrinter.getId().getServiceName();
-        if (!TextUtils.isEmpty(PrintOptionUtils.getAdvancedOptionsActivityName(
-                this, serviceName))) {
+        if (mAdvancedPrintOptionsActivity != null) {
             mMoreOptionsButton.setVisibility(View.VISIBLE);
             mMoreOptionsButton.setEnabled(true);
         } else {
@@ -1969,6 +2038,10 @@
             mPrinterRegistry.setTrackedPrinter(null);
         }
 
+        if (mPrintersObserver != null) {
+            mDestinationSpinnerAdapter.unregisterDataSetObserver(mPrintersObserver);
+        }
+
         if (mState != STATE_INITIALIZING) {
             mProgressMessageController.cancel();
             mSpoolerProvider.destroy();
@@ -2208,14 +2281,14 @@
                 if (position == 0) {
                     return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
                 } else if (position == 1) {
-                    return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+                    return DEST_ADAPTER_ITEM_ID_MORE;
                 }
             } else {
                 if (position == 1) {
                     return DEST_ADAPTER_ITEM_ID_SAVE_AS_PDF;
                 }
                 if (position == getCount() - 1) {
-                    return DEST_ADAPTER_ITEM_ID_ALL_PRINTERS;
+                    return DEST_ADAPTER_ITEM_ID_MORE;
                 }
             }
             return position;
@@ -2228,6 +2301,14 @@
             return view;
         }
 
+        private String getMoreItemTitle() {
+            if (mArePrintServicesEnabled) {
+                return getString(R.string.all_printers);
+            } else {
+                return getString(R.string.print_add_printer);
+            }
+        }
+
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
             if (mShowDestinationPrompt) {
@@ -2256,7 +2337,7 @@
                     title = printerHolder.printer.getName();
                     icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
                 } else if (position == 1) {
-                    title = getString(R.string.all_printers);
+                    title = getMoreItemTitle();
                 }
             } else {
                 if (position == 1 && getPdfPrinter() != null) {
@@ -2264,7 +2345,7 @@
                     title = printerHolder.printer.getName();
                     icon = getResources().getDrawable(R.drawable.ic_menu_savetopdf, null);
                 } else if (position == getCount() - 1) {
-                    title = getString(R.string.all_printers);
+                    title = getMoreItemTitle();
                 } else {
                     PrinterHolder printerHolder = (PrinterHolder) getItem(position);
                     PrinterInfo printInfo = printerHolder.printer;
@@ -2299,7 +2380,7 @@
                 }
                 iconView.setImageDrawable(icon);
             } else {
-                iconView.setVisibility(View.GONE);
+                iconView.setVisibility(View.INVISIBLE);
             }
 
             return convertView;
@@ -2344,6 +2425,7 @@
                 PrinterInfo updatedPrinter = newPrintersMap.remove(oldPrinterId);
                 if (updatedPrinter != null) {
                     printerHolder.printer = updatedPrinter;
+                    printerHolder.removed = false;
                 } else {
                     printerHolder.removed = true;
                 }
@@ -2449,6 +2531,7 @@
             PrinterCapabilitiesInfo oldCapab = oldPrinterState.getCapabilities();
             PrinterCapabilitiesInfo newCapab = newPrinterState.getCapabilities();
 
+            final boolean hadCabab = oldCapab != null;
             final boolean hasCapab = newCapab != null;
             final boolean gotCapab = oldCapab == null && newCapab != null;
             final boolean lostCapab = oldCapab != null && newCapab == null;
@@ -2467,23 +2550,31 @@
 
             mCurrentPrinter = newPrinterState;
 
-            if ((isActive && gotCapab) || (becameActive && hasCapab)) {
-                if (hasCapab && capabChanged) {
-                    updatePrintAttributesFromCapabilities(newCapab);
-                    updatePrintPreviewController(false);
-                }
-                onPrinterAvailable(newPrinterState);
-            } else if ((becameInactive && hasCapab) || (isActive && lostCapab)) {
-                onPrinterUnavailable(newPrinterState);
-            }
-
             final boolean updateNeeded = ((capabChanged && hasCapab && isActive)
                     || (becameActive && hasCapab) || (isActive && gotCapab));
 
+            if (capabChanged && hasCapab) {
+                updatePrintAttributesFromCapabilities(newCapab);
+            }
+
+            if (updateNeeded) {
+                updatePrintPreviewController(false);
+            }
+
+            if ((isActive && gotCapab) || (becameActive && hasCapab)) {
+                onPrinterAvailable(newPrinterState);
+            } else if ((becameInactive && hadCabab) || (isActive && lostCapab)) {
+                onPrinterUnavailable(newPrinterState);
+            }
+
             if (updateNeeded && canUpdateDocument()) {
                 updateDocument(false);
             }
 
+            // Force a reload of the enabled print services to update mAdvancedPrintOptionsActivity
+            // in onLoadFinished();
+            getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
+
             updateOptionsUi();
             updateSummary();
         }
@@ -2509,7 +2600,7 @@
                     return;
                 }
 
-                if (id == DEST_ADAPTER_ITEM_ID_ALL_PRINTERS) {
+                if (id == DEST_ADAPTER_ITEM_ID_MORE) {
                     startSelectPrinterActivity();
                     return;
                 }
@@ -2543,6 +2634,10 @@
                 }
 
                 mPrinterAvailabilityDetector.updatePrinter(currentPrinter);
+
+                // Force a reload of the enabled print services to update
+                // mAdvancedPrintOptionsActivity in onLoadFinished();
+                getLoaderManager().getLoader(LOADER_ID_ENABLED_PRINT_SERVICES).forceLoad();
             } else if (spinner == mMediaSizeSpinner) {
                 SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
                 PrintAttributes attributes = mPrintJob.getAttributes();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
index 6d60bb8..86366dd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrinterRegistry.java
@@ -33,7 +33,7 @@
 
 public class PrinterRegistry {
 
-    private static final int LOADER_ID_PRINTERS_LOADER = 1;
+    private final int mLoaderId;
 
     private final Activity mActivity;
 
@@ -52,12 +52,17 @@
         public void onPrintersInvalid();
     }
 
-    public PrinterRegistry(Activity activity, Runnable readyCallback) {
+    public PrinterRegistry(Activity activity, Runnable readyCallback, int loaderId,
+            int internalLoaderId) {
+        mLoaderId = loaderId;
         mActivity = activity;
         mReadyCallback = readyCallback;
         mHandler = new MyHandler(activity.getMainLooper());
-        activity.getLoaderManager().initLoader(LOADER_ID_PRINTERS_LOADER,
-                null, mLoaderCallbacks);
+
+        Bundle loaderData = new Bundle(1);
+        loaderData.putInt(null, internalLoaderId);
+
+        activity.getLoaderManager().initLoader(loaderId, loaderData, mLoaderCallbacks);
     }
 
     public void setOnPrintersChangeListener(OnPrintersChangeListener listener) {
@@ -106,7 +111,7 @@
     }
 
     private FusedPrintersProvider getPrinterProvider() {
-        Loader<?> loader = mActivity.getLoaderManager().getLoader(LOADER_ID_PRINTERS_LOADER);
+        Loader<?> loader = mActivity.getLoaderManager().getLoader(mLoaderId);
         return (FusedPrintersProvider) loader;
     }
 
@@ -114,38 +119,34 @@
             new LoaderCallbacks<List<PrinterInfo>>() {
         @Override
         public void onLoaderReset(Loader<List<PrinterInfo>> loader) {
-            if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
-                mPrinters.clear();
-                if (mOnPrintersChangeListener != null) {
-                    // Post a message as we are in onLoadFinished and certain operations
-                    // are not allowed in this callback, such as fragment transactions.
-                    // Clients should not handle this explicitly.
-                    mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
-                            mOnPrintersChangeListener).sendToTarget();
-                }
+            mPrinters.clear();
+            if (mOnPrintersChangeListener != null) {
+                // Post a message as we are in onLoadFinished and certain operations
+                // are not allowed in this callback, such as fragment transactions.
+                // Clients should not handle this explicitly.
+                mHandler.obtainMessage(MyHandler.MSG_PRINTERS_INVALID,
+                        mOnPrintersChangeListener).sendToTarget();
             }
         }
 
         // LoaderCallbacks#onLoadFinished
         @Override
         public void onLoadFinished(Loader<List<PrinterInfo>> loader, List<PrinterInfo> printers) {
-            if (loader.getId() == LOADER_ID_PRINTERS_LOADER) {
-                mPrinters.clear();
-                mPrinters.addAll(printers);
-                if (mOnPrintersChangeListener != null) {
-                    // Post a message as we are in onLoadFinished and certain operations
-                    // are not allowed in this callback, such as fragment transactions.
-                    // Clients should not handle this explicitly.
-                    SomeArgs args = SomeArgs.obtain();
-                    args.arg1 = mOnPrintersChangeListener;
-                    args.arg2 = printers;
-                    mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
-                }
-                if (!mReady) {
-                    mReady = true;
-                    if (mReadyCallback != null) {
-                        mReadyCallback.run();
-                    }
+            mPrinters.clear();
+            mPrinters.addAll(printers);
+            if (mOnPrintersChangeListener != null) {
+                // Post a message as we are in onLoadFinished and certain operations
+                // are not allowed in this callback, such as fragment transactions.
+                // Clients should not handle this explicitly.
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = mOnPrintersChangeListener;
+                args.arg2 = printers;
+                mHandler.obtainMessage(MyHandler.MSG_PRINTERS_CHANGED, args).sendToTarget();
+            }
+            if (!mReady) {
+                mReady = true;
+                if (mReadyCallback != null) {
+                    mReadyCallback.run();
                 }
             }
         }
@@ -153,10 +154,7 @@
         // LoaderCallbacks#onCreateLoader
         @Override
         public Loader<List<PrinterInfo>> onCreateLoader(int id, Bundle args) {
-            if (id == LOADER_ID_PRINTERS_LOADER) {
-                return new FusedPrintersProvider(mActivity);
-            }
-            return null;
+            return new FusedPrintersProvider(mActivity, args.getInt(null));
         }
     };
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index 4f7624a..e53a522 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -17,31 +17,18 @@
 package com.android.printspooler.ui;
 
 import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.DialogFragment;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
+import android.app.LoaderManager;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
-import android.content.pm.ActivityInfo;
+import android.content.Loader;
 import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.database.ContentObserver;
 import android.database.DataSetObserver;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
 import android.print.PrintManager;
+import android.print.PrintServicesLoader;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
 import android.printservice.PrintServiceInfo;
@@ -59,17 +46,16 @@
 import android.view.accessibility.AccessibilityManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.AdapterContextMenuInfo;
-import android.widget.ArrayAdapter;
 import android.widget.BaseAdapter;
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.SearchView;
 import android.widget.TextView;
 import android.widget.Toast;
 
-import com.android.internal.content.PackageMonitor;
 import com.android.printspooler.R;
 
 import java.util.ArrayList;
@@ -78,35 +64,33 @@
 /**
  * This is an activity for selecting a printer.
  */
-public final class SelectPrinterActivity extends Activity {
+public final class SelectPrinterActivity extends Activity implements
+        LoaderManager.LoaderCallbacks<List<PrintServiceInfo>> {
 
     private static final String LOG_TAG = "SelectPrinterFragment";
 
+    private static final int LOADER_ID_PRINT_REGISTRY = 1;
+    private static final int LOADER_ID_PRINT_REGISTRY_INT = 2;
+    private static final int LOADER_ID_ENABLED_PRINT_SERVICES = 3;
+
     public static final String INTENT_EXTRA_PRINTER_ID = "INTENT_EXTRA_PRINTER_ID";
 
-    private static final String FRAGMENT_TAG_ADD_PRINTER_DIALOG =
-            "FRAGMENT_TAG_ADD_PRINTER_DIALOG";
-
-    private static final String FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS =
-            "FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS";
-
     private static final String EXTRA_PRINTER_ID = "EXTRA_PRINTER_ID";
 
+    private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
+
     /** If there are any enabled print services */
     private boolean mHasEnabledPrintServices;
 
-    private final ArrayList<PrintServiceInfo> mAddPrinterServices =
-            new ArrayList<>();
-
     private PrinterRegistry mPrinterRegistry;
 
     private ListView mListView;
 
     private AnnounceFilterResult mAnnounceFilterResult;
 
-    /** Monitor if new print services get enabled or disabled */
-    private ContentObserver mPrintServicesDisabledObserver;
-    private PackageMonitor mPackageObserver;
+    private void startAddPrinterActivity() {
+        startActivity(new Intent(this, AddPrinterActivity.class));
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -115,7 +99,8 @@
 
         setContentView(R.layout.select_printer_activity);
 
-        mPrinterRegistry = new PrinterRegistry(this, null);
+        mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
+                LOADER_ID_PRINT_REGISTRY_INT);
 
         // Hook up the list view.
         mListView = (ListView) findViewById(android.R.id.list);
@@ -145,22 +130,67 @@
                 }
 
                 PrinterInfo printer = (PrinterInfo) mListView.getAdapter().getItem(position);
-                onPrinterSelected(printer.getId());
+
+                if (printer == null) {
+                    startAddPrinterActivity();
+                } else {
+                    onPrinterSelected(printer.getId());
+                }
+            }
+        });
+
+        findViewById(R.id.button).setOnClickListener(new OnClickListener() {
+            @Override public void onClick(View v) {
+                startAddPrinterActivity();
             }
         });
 
         registerForContextMenu(mListView);
 
-        // Display a notification about disabled services if there are disabled services
-        String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
-                Settings.Secure.DISABLED_PRINT_SERVICES);
-        if (!TextUtils.isEmpty(disabledServicesSetting)) {
-            Toast.makeText(this, getString(R.string.print_services_disabled_toast),
-                    Toast.LENGTH_LONG).show();
+        getLoaderManager().initLoader(LOADER_ID_ENABLED_PRINT_SERVICES, null, this);
+
+        // On first creation:
+        //
+        // If no services are installed, instantly open add printer dialog.
+        // If some are disabled and some are enabled show a toast to notify the user
+        if (savedInstanceState == null || !savedInstanceState.getBoolean(KEY_NOT_FIRST_CREATE)) {
+            List<PrintServiceInfo> allServices =
+                    ((PrintManager) getSystemService(Context.PRINT_SERVICE))
+                            .getPrintServices(PrintManager.ALL_SERVICES);
+            boolean hasEnabledServices = false;
+            boolean hasDisabledServices = false;
+
+            if (allServices != null) {
+                final int numServices = allServices.size();
+                for (int i = 0; i < numServices; i++) {
+                    if (allServices.get(i).isEnabled()) {
+                        hasEnabledServices = true;
+                    } else {
+                        hasDisabledServices = true;
+                    }
+                }
+            }
+
+            if (!hasEnabledServices) {
+                startAddPrinterActivity();
+            } else if (hasDisabledServices) {
+                String disabledServicesSetting = Settings.Secure.getString(getContentResolver(),
+                        Settings.Secure.DISABLED_PRINT_SERVICES);
+                if (!TextUtils.isEmpty(disabledServicesSetting)) {
+                    Toast.makeText(this, getString(R.string.print_services_disabled_toast),
+                            Toast.LENGTH_LONG).show();
+                }
+            }
         }
     }
 
     @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_NOT_FIRST_CREATE, true);
+    }
+
+    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         super.onCreateOptionsMenu(menu);
 
@@ -249,60 +279,13 @@
      * Adjust the UI if the enabled print services changed.
      */
     private synchronized void onPrintServicesUpdate() {
-        updateServicesWithAddPrinterActivity();
         updateEmptyView((DestinationAdapter)mListView.getAdapter());
         invalidateOptionsMenu();
     }
 
-    /**
-     * Register listener for changes to the enabled print services.
-     */
-    private void registerServiceMonitor() {
-        // Listen for services getting disabled
-        mPrintServicesDisabledObserver = new ContentObserver(new Handler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                onPrintServicesUpdate();
-            }
-        };
-
-        // Listen for services getting installed or uninstalled
-        mPackageObserver = new PackageMonitor() {
-            @Override
-            public void onPackageModified(String packageName) {
-                onPrintServicesUpdate();
-            }
-
-            @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                onPrintServicesUpdate();
-            }
-
-            @Override
-            public void onPackageAdded(String packageName, int uid) {
-                onPrintServicesUpdate();
-            }
-        };
-
-        getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.DISABLED_PRINT_SERVICES), false,
-                mPrintServicesDisabledObserver);
-
-        mPackageObserver.register(this, getMainLooper(), false);
-    }
-
-    /**
-     * Unregister the listeners for changes to the enabled print services.
-     */
-    private void unregisterServiceMonitorIfNeeded() {
-        getContentResolver().unregisterContentObserver(mPrintServicesDisabledObserver);
-        mPackageObserver.unregister();
-    }
-
     @Override
     public void onStart() {
         super.onStart();
-        registerServiceMonitor();
         onPrintServicesUpdate();
     }
 
@@ -316,19 +299,9 @@
 
     @Override
     public void onStop() {
-        unregisterServiceMonitorIfNeeded();
         super.onStop();
     }
 
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == R.id.action_add_printer) {
-            showAddPrinterSelectionDialog();
-            return true;
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
     private void onPrinterSelected(PrinterId printerId) {
         Intent intent = new Intent();
         intent.putExtra(INTENT_EXTRA_PRINTER_ID, printerId);
@@ -336,68 +309,6 @@
         finish();
     }
 
-    private void updateServicesWithAddPrinterActivity() {
-        mHasEnabledPrintServices = true;
-        mAddPrinterServices.clear();
-
-        // Get all enabled print services.
-        PrintManager printManager = (PrintManager) getSystemService(Context.PRINT_SERVICE);
-        List<PrintServiceInfo> enabledServices = printManager.getEnabledPrintServices();
-
-        // No enabled print services - done.
-        if (enabledServices.isEmpty()) {
-            mHasEnabledPrintServices = false;
-            return;
-        }
-
-        // Find the services with valid add printers activities.
-        final int enabledServiceCount = enabledServices.size();
-        for (int i = 0; i < enabledServiceCount; i++) {
-            PrintServiceInfo enabledService = enabledServices.get(i);
-
-            // No add printers activity declared - next.
-            if (TextUtils.isEmpty(enabledService.getAddPrintersActivityName())) {
-                continue;
-            }
-
-            ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
-            ComponentName addPrintersComponentName = new ComponentName(
-                    serviceInfo.packageName, enabledService.getAddPrintersActivityName());
-            Intent addPritnersIntent = new Intent()
-                .setComponent(addPrintersComponentName);
-
-            // The add printers activity is valid - add it.
-            PackageManager pm = getPackageManager();
-            List<ResolveInfo> resolvedActivities = pm.queryIntentActivities(addPritnersIntent, 0);
-            if (!resolvedActivities.isEmpty()) {
-                // The activity is a component name, therefore it is one or none.
-                ActivityInfo activityInfo = resolvedActivities.get(0).activityInfo;
-                if (activityInfo.exported
-                        && (activityInfo.permission == null
-                                || pm.checkPermission(activityInfo.permission, getPackageName())
-                                        == PackageManager.PERMISSION_GRANTED)) {
-                    mAddPrinterServices.add(enabledService);
-                }
-            }
-        }
-    }
-
-    private void showAddPrinterSelectionDialog() {
-        FragmentTransaction transaction = getFragmentManager().beginTransaction();
-        Fragment oldFragment = getFragmentManager().findFragmentByTag(
-                FRAGMENT_TAG_ADD_PRINTER_DIALOG);
-        if (oldFragment != null) {
-            transaction.remove(oldFragment);
-        }
-        AddPrinterAlertDialogFragment newFragment = new AddPrinterAlertDialogFragment();
-        Bundle arguments = new Bundle();
-        arguments.putParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS,
-                mAddPrinterServices);
-        newFragment.setArguments(arguments);
-        transaction.add(newFragment, FRAGMENT_TAG_ADD_PRINTER_DIALOG);
-        transaction.commit();
-    }
-
     public void updateEmptyView(DestinationAdapter adapter) {
         if (mListView.getEmptyView() == null) {
             View emptyView = findViewById(R.id.empty_print_state);
@@ -426,71 +337,28 @@
         }
     }
 
-    public static class AddPrinterAlertDialogFragment extends DialogFragment {
+    @Override
+    public Loader<List<PrintServiceInfo>> onCreateLoader(int id, Bundle args) {
+        return new PrintServicesLoader((PrintManager) getSystemService(Context.PRINT_SERVICE), this,
+                PrintManager.ENABLED_SERVICES);
+    }
 
-        private String mAddPrintServiceItem;
+    @Override
+    public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
+            List<PrintServiceInfo> data) {
+        if (data == null || data.isEmpty()) {
+            mHasEnabledPrintServices = false;
+        } else {
+            mHasEnabledPrintServices = true;
+        }
 
-        @Override
-        @SuppressWarnings("unchecked")
-        public Dialog onCreateDialog(Bundle savedInstanceState) {
-            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity())
-                    .setTitle(R.string.choose_print_service);
+        onPrintServicesUpdate();
+    }
 
-            final List<PrintServiceInfo> printServices = (List<PrintServiceInfo>) (List<?>)
-                    getArguments().getParcelableArrayList(FRAGMENT_ARGUMENT_PRINT_SERVICE_INFOS);
-
-            final ArrayAdapter<String> adapter = new ArrayAdapter<>(
-                    getActivity(), android.R.layout.simple_list_item_1);
-            final int printServiceCount = printServices.size();
-            for (int i = 0; i < printServiceCount; i++) {
-                PrintServiceInfo printService = printServices.get(i);
-                adapter.add(printService.getResolveInfo().loadLabel(
-                        getActivity().getPackageManager()).toString());
-            }
-
-            final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(),
-                    Settings.Secure.PRINT_SERVICE_SEARCH_URI);
-            final Intent viewIntent;
-            if (!TextUtils.isEmpty(searchUri)) {
-                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
-                if (getActivity().getPackageManager().resolveActivity(intent, 0) != null) {
-                    viewIntent = intent;
-                    mAddPrintServiceItem = getString(R.string.add_print_service_label);
-                    adapter.add(mAddPrintServiceItem);
-                } else {
-                    viewIntent = null;
-                }
-            } else {
-                viewIntent = null;
-            }
-
-            builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
-                @Override
-                public void onClick(DialogInterface dialog, int which) {
-                    String item = adapter.getItem(which);
-                    if (item.equals(mAddPrintServiceItem)) {
-                        try {
-                            startActivity(viewIntent);
-                        } catch (ActivityNotFoundException anfe) {
-                            Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
-                        }
-                    } else {
-                        PrintServiceInfo printService = printServices.get(which);
-                        ComponentName componentName = new ComponentName(
-                                printService.getResolveInfo().serviceInfo.packageName,
-                                printService.getAddPrintersActivityName());
-                        Intent intent = new Intent(Intent.ACTION_MAIN);
-                        intent.setComponent(componentName);
-                        try {
-                            startActivity(intent);
-                        } catch (ActivityNotFoundException anfe) {
-                            Log.w(LOG_TAG, "Couldn't start add printer activity", anfe);
-                        }
-                    }
-                }
-            });
-
-            return builder.create();
+    @Override
+    public void onLoaderReset(Loader<List<PrintServiceInfo>> loader) {
+        if (!isFinishing()) {
+            onLoadFinished(loader, null);
         }
     }
 
@@ -592,14 +460,40 @@
         @Override
         public int getCount() {
             synchronized (mLock) {
-                return mFilteredPrinters.size();
+                if (mFilteredPrinters.isEmpty()) {
+                    return 0;
+                } else {
+                    // Add "add printer" item to the end of the list. If the list is empty there is
+                    // a link on the empty view
+                    return mFilteredPrinters.size() + 1;
+                }
+            }
+        }
+
+        @Override
+        public int getViewTypeCount() {
+            return 2;
+        }
+
+        @Override
+        public int getItemViewType(int position) {
+            // Use separate view types for the "add printer" item an the items referring to printers
+            if (getItem(position) == null) {
+                return 0;
+            } else {
+                return 1;
             }
         }
 
         @Override
         public Object getItem(int position) {
             synchronized (mLock) {
-                return mFilteredPrinters.get(position);
+                if (position < mFilteredPrinters.size()) {
+                    return mFilteredPrinters.get(position);
+                } else {
+                    // Return null to mark this as the "add printer item"
+                    return null;
+                }
             }
         }
 
@@ -615,6 +509,18 @@
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
+            final PrinterInfo printer = (PrinterInfo) getItem(position);
+
+            // Handle "add printer item"
+            if (printer == null) {
+                if (convertView == null) {
+                    convertView = getLayoutInflater().inflate(R.layout.add_printer_list_item,
+                            parent, false);
+                }
+
+                return convertView;
+            }
+
             if (convertView == null) {
                 convertView = getLayoutInflater().inflate(
                         R.layout.printer_list_item, parent, false);
@@ -622,7 +528,6 @@
 
             convertView.setEnabled(isActionable(position));
 
-            final PrinterInfo printer = (PrinterInfo) getItem(position);
 
             CharSequence title = printer.getName();
             Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
@@ -661,7 +566,7 @@
                 subtitleView.setVisibility(View.GONE);
             }
 
-            ImageView moreInfoView = (ImageView) convertView.findViewById(R.id.more_info);
+            LinearLayout moreInfoView = (LinearLayout) convertView.findViewById(R.id.more_info);
             if (printer.getInfoIntent() != null) {
                 moreInfoView.setVisibility(View.VISIBLE);
                 moreInfoView.setOnClickListener(new OnClickListener() {
@@ -699,7 +604,12 @@
 
         public boolean isActionable(int position) {
             PrinterInfo printer =  (PrinterInfo) getItem(position);
-            return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+
+            if (printer == null) {
+                return true;
+            } else {
+                return printer.getStatus() != PrinterInfo.STATUS_UNAVAILABLE;
+            }
         }
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
deleted file mode 100644
index 446952d..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/util/PrintOptionUtils.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printspooler.util;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.ServiceInfo;
-import android.print.PrintManager;
-import android.printservice.PrintServiceInfo;
-
-import java.util.List;
-
-public class PrintOptionUtils {
-
-    private PrintOptionUtils() {
-        /* ignore - hide constructor */
-    }
-
-    /**
-     * Gets the advanced options activity name for a print service.
-     *
-     * @param context Context for accessing system resources.
-     * @param serviceName The print service name.
-     * @return The advanced options activity name or null.
-     */
-    public static String getAdvancedOptionsActivityName(Context context,
-            ComponentName serviceName) {
-        PrintManager printManager = (PrintManager) context.getSystemService(
-                Context.PRINT_SERVICE);
-        List<PrintServiceInfo> printServices = printManager.getEnabledPrintServices();
-        final int printServiceCount = printServices.size();
-        for (int i = 0; i < printServiceCount; i ++) {
-            PrintServiceInfo printServiceInfo = printServices.get(i);
-            ServiceInfo serviceInfo = printServiceInfo.getResolveInfo().serviceInfo;
-            if (serviceInfo.name.equals(serviceName.getClassName())
-                    && serviceInfo.packageName.equals(serviceName.getPackageName())) {
-                return printServiceInfo.getAdvancedOptionsActivityName();
-            }
-        }
-        return null;
-    }
-}
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index 9e5d029..67296a6 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -17,14 +17,15 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/drawer_layout"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:background="?android:attr/colorPrimaryDark">
     <!-- The main content view -->
     <LinearLayout
         android:id="@+id/content_parent"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
-        android:fitsSystemWindows="false" >
+        android:fitsSystemWindows="true" >
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -36,14 +37,14 @@
                 android:layout_height="wrap_content"
                 android:navigationContentDescription="@*android:string/action_bar_up_description"
                 android:theme="?android:attr/actionBarTheme"
-                android:paddingTop="@*android:dimen/status_bar_height"
                 style="?android:attr/toolbarStyle"
                 android:background="?android:attr/colorPrimary" />
         </FrameLayout>
         <FrameLayout
             android:id="@+id/content_frame"
             android:layout_width="match_parent"
-            android:layout_height="fill_parent" />
+            android:layout_height="fill_parent"
+            android:background="?android:attr/windowBackground" />
     </LinearLayout>
     <!-- The navigation drawer -->
     <ListView android:id="@+id/left_drawer"
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 94e376c8..a408c08 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Beheer deur administrateur"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Geaktiveer deur administrateur"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Gedeaktiveer deur administrateur"</string>
     <string name="home" msgid="8263346537524314127">"Tuis"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> gelede"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> oor"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index c605d2d..7ae97e2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል  እየሞላ አይደለም"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"በአስተዳዳሪ ቁጥጥር የተደረገበት"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"በአስተዳዳሪ የነቃ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"በአስተዳዳሪ የተሰናከለ"</string>
     <string name="home" msgid="8263346537524314127">"መነሻ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"ከ<xliff:g id="ID_1">%1$s</xliff:g> በፊት"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ቀርቷል"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 975051a..eb76e1f 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"إعدادات يتحكم فيها المشرف"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"تم التمكين بواسطة المشرف"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"تم التعطيل بواسطة المشرف"</string>
     <string name="home" msgid="8263346537524314127">"الشاشة الرئيسية"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"قبل <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"يتبقى <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 3f78fa2..9903397 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Admin tərəfindən nəzarət olunur"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Administrator tərəfindən aktiv edildi"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Administrator tərəfindən deaktiv edildi"</string>
     <string name="home" msgid="8263346537524314127">"Əsas səhifə"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> əvvəl"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qalıb"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index a38542d..f570d09 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontroliše administrator"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio je administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio je administrator"</string>
     <string name="home" msgid="8263346537524314127">"Početni"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Pre <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index e3c8d8a..72652c9 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролира се от администратор"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Активирано от администратора"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Деактивирано от администратора"</string>
     <string name="home" msgid="8263346537524314127">"Начало"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Преди <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Оставащо време: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index d3c9e6b..b863773 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"প্রশাসকের দ্বারা নিয়ন্ত্রিত"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"প্রশাসক সক্ষম করেছেন"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"প্রশাসক অক্ষম করেছেন"</string>
     <string name="home" msgid="8263346537524314127">"হোম"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> আগে"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> বাকী আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 673b08b..542c3ce 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pod kontrolom administratora"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio je administrator"</string>
     <string name="home" msgid="8263346537524314127">"Početna stranica"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Još otprilike <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index d4d2f6c..e2c4495 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlat per l\'administrador"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Opció activada per l\'administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Opció desactivada per l\'administrador"</string>
     <string name="home" msgid="8263346537524314127">"Inici"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Fa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Temps restant: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 00d4f47..f713ceb 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Spravováno administrátorem"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Povoleno administrátorem"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Zakázáno administrátorem"</string>
     <string name="home" msgid="8263346537524314127">"Plocha"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"před <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Zbývající čas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 398875a..d1d7b23 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolleret af administratoren"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Aktiveret af administratoren"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Deaktiveret af administratoren"</string>
     <string name="home" msgid="8263346537524314127">"Start"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> tilbage"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 2a56425..fbd1207 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Durch den Administrator verwaltet"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Vom Administrator aktiviert"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Vom Administrator deaktiviert"</string>
     <string name="home" msgid="8263346537524314127">"Startseite"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Vor <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Noch <xliff:g id="ID_1">%1$s</xliff:g> verbleibend"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index dd6bb4f..dce2cf7 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ελέγχονται από το διαχειριστή"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Ενεργοποιήθηκε από το διαχειριστή"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Απενεργοποιήθηκε από το διαχειριστή"</string>
     <string name="home" msgid="8263346537524314127">"Αρχική οθόνη"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Πριν από <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Απομένουν <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
     <string name="home" msgid="8263346537524314127">"Home"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
     <string name="home" msgid="8263346537524314127">"Home"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 1ed6dbd..4821a71 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlled by admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Enabled by administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Disabled by administrator"</string>
     <string name="home" msgid="8263346537524314127">"Home"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ago"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> left"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 3f2befd..2d0ce02 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se realiza la carga"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Cargado"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Habilitada por el administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Inhabilitada por el administrador"</string>
     <string name="home" msgid="8263346537524314127">"Página principal"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Falta <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index a01d2e9..8dcf6ab 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada por el administrador"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Habilitado por el administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Inhabilitado por el administrador"</string>
     <string name="home" msgid="8263346537524314127">"Inicio"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Hace <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Tiempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 27546ce..62dd7ab 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Juhib administraator"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Administraator on lubanud"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Administraator on keelanud"</string>
     <string name="home" msgid="8263346537524314127">"Avaekraan"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> tagasi"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> on jäänud"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index dd0e1cb..cb6f0b4 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administratzaileak kontrolatzen du"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Administratzaileak gaitu du"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Administratzaileak desgaitu du"</string>
     <string name="home" msgid="8263346537524314127">"Hasierako pantaila"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Duela <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> guztiz kargatu arte"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 0a2d111..e7cbd1e 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -274,11 +274,11 @@
     <string name="inactive_app_active_summary" msgid="4174921824958516106">"فعال. برای تغییر حالت ضربه بزنید."</string>
     <string name="runningservices_settings_title" msgid="8097287939865165213">"سرویس‌های در حال اجرا"</string>
     <string name="runningservices_settings_summary" msgid="854608995821032748">"مشاهده و کنترل سرویس‌های در حال اجرای فعلی"</string>
-    <string name="enable_webview_multiprocess" msgid="3405948012467585908">"فعال کردن وب‌نما چند پردازشی"</string>
+    <string name="enable_webview_multiprocess" msgid="3405948012467585908">"فعال کردن وب‌نمای چندپردازشی"</string>
     <string name="enable_webview_multiprocess_desc" msgid="852226124223847283">"اجرای تولیدکننده تصویر وب‌نما در یک پردازش مجزا."</string>
     <string name="select_webview_provider_title" msgid="4628592979751918907">"اجرای وب‌نما"</string>
     <string name="select_webview_provider_dialog_title" msgid="4370551378720004872">"تنظیم اجرای وب‌نما"</string>
-    <string name="select_webview_provider_toast_text" msgid="8168796505966635684">"اجرای وب‌نما انتخابی نامعتبر است چون فهرست گزینه‌های اجرای انتخابی قدیمی شده است. این فهرست اکنون باید به‌روزرسانی شود."</string>
+    <string name="select_webview_provider_toast_text" msgid="8168796505966635684">"اجرای وب‌نمای انتخابی نامعتبر است چون فهرست گزینه‌های اجرای انتخابی قدیمی شده است. این فهرست اکنون باید به‌روزرسانی شود."</string>
     <string name="convert_to_file_encryption" msgid="3060156730651061223">"تبدیل به رمزگذاری برحسب فایل"</string>
     <string name="convert_to_file_encryption_enabled" msgid="2861258671151428346">"تبدیل…"</string>
     <string name="convert_to_file_encryption_done" msgid="7859766358000523953">"از قبل به رمزگذاری بر حسب فایل تبدیل شده است"</string>
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمی‌شود"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"توسط سرپرست سیستم کنترل می‌شود"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"سرپرست آن را فعال کرده است"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"سرپرست آن را غیرفعال کرده است"</string>
     <string name="home" msgid="8263346537524314127">"صفحه اصلی"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی مانده است"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index bb27bcf..bb3ad97 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Järjestelmänvalvoja hallinnoi tätä asetusta."</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Järjestelmänvalvojan käyttöön ottama"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Järjestelmänvalvojan käytöstä poistama"</string>
     <string name="home" msgid="8263346537524314127">"Aloitusnäyttö"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> sitten"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> jäljellä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 7ca3f89..1f12f18 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"N\'est pas en charge"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Pleine"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Activé par l\'administrateur"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Désactivé par l\'administrateur"</string>
     <string name="home" msgid="8263346537524314127">"Accueil"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Durée restante :<xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index adfc7a3..3dc2a6a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Contrôlé par l\'administrateur"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Activé par l\'administrateur"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Désactivé par l\'administrateur"</string>
     <string name="home" msgid="8263346537524314127">"Accueil"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Il y a <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Il reste <xliff:g id="ID_1">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 867a29d..3715919 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Opción controlada polo administrador"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Activado polo administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Desactivado polo administrador"</string>
     <string name="home" msgid="8263346537524314127">"Inicio"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Hai <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Tempo restante: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 54e7288..c77aeb6 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"વ્યવસ્થાપક દ્વારા નિયંત્રિત"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"વ્યવસ્થાપક દ્વારા સક્ષમ કરેલ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
     <string name="home" msgid="8263346537524314127">"હોમ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> પહેલાં"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> બાકી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a7e838e..e8a47cb 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"व्यवस्थापक द्वारा नियंत्रित"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"व्यवस्थापक द्वारा सक्षम किया गया"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"व्यवस्थापक द्वारा अक्षम किया गया"</string>
     <string name="home" msgid="8263346537524314127">"होम"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहले"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शेष"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f315fe9..320ebc1 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolira administrator"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Omogućio administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogućio administrator"</string>
     <string name="home" msgid="8263346537524314127">"Početni zaslon"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Prije <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Još <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9b62eb1..4ea4528 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nem töltődik"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Feltöltve"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Rendszergazda által irányítva"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Engedélyezve a rendszergazda által"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Letiltva a rendszergazda által"</string>
     <string name="home" msgid="8263346537524314127">"Főoldal"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Ennyi ideje: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> van hátra"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 36a3813..046ea48 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Վերահսկվում է ադմինիստրատորի կողմից"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Միացված է ադմինիստրատորի կողմից"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Կասեցված է ադմինիստրատորի կողմից"</string>
     <string name="home" msgid="8263346537524314127">"Գլխավոր էջ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> առաջ"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Մնացել է <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 62372ee..02c747a 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikontrol oleh admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Diaktifkan oleh administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Dinonaktifkan oleh administrator"</string>
     <string name="home" msgid="8263346537524314127">"Layar Utama"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> lalu"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Tersisa <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index cd8734d..17a2f08 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Stjórnað af kerfisstjóra"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Virkjað af stjórnanda"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Stjórnandi gerði óvirkt"</string>
     <string name="home" msgid="8263346537524314127">"Heim"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Fyrir <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> eftir"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 44bffcb..9c75939 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Gestita dall\'amministratore"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Attivata dall\'amministratore"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Disattivata dall\'amministratore"</string>
     <string name="home" msgid="8263346537524314127">"Home page"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> fa"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> rimanenti"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index de7f7e3..429ce61 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"נמצא בשליטת מנהל מערכת"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"הופעל על ידי מנהל המערכת"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"הושבת על ידי מנהל המערכת"</string>
     <string name="home" msgid="8263346537524314127">"דף הבית"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"לפני <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"נשארו <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index f341d71..6c42110 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -313,6 +313,8 @@
     <!-- no translation found for battery_info_status_full (2824614753861462808) -->
     <skip />
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"管理者により管理されています"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"管理者によって有効にされています"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"管理者によって無効にされています"</string>
     <string name="home" msgid="8263346537524314127">"ホーム"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"あと <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 08e6327..6dba53b 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"იმართება ადმინისტრატორის მიერ"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"ჩართულია ადმინისტრატორის მიერ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"გათიშულია ადმინისტრატორის მიერ"</string>
     <string name="home" msgid="8263346537524314127">"მთავარი"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"გავიდა <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"დარჩენილია <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 921812a..36843fd 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Әкімші басқарады"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Әкімші қосқан"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Әкімші өшірген"</string>
     <string name="home" msgid="8263346537524314127">"Негізгі бет"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> бұрын"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> қалды"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index f64158c..9985a1e 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិន​បញ្ចូលថ្ម"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"បានបើកដំណើរការដោយអ្នកគ្រប់គ្រង"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រង"</string>
     <string name="home" msgid="8263346537524314127">"ដើម"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> មុន"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"នៅសល់ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 7e68655..8ca9807 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"ನಿರ್ವಾಹಕರಿಂದ ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"ನಿರ್ವಾಹಕರಿಂದ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="home" msgid="8263346537524314127">"ಮುಖಪುಟ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ಹಿಂದೆ"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index a7434d0..31e0378 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"관리자가 제어"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"관리자가 사용 설정함"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"관리자가 사용 중지함"</string>
     <string name="home" msgid="8263346537524314127">"홈"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> 전"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> 남음"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 178d47e..fd2d905 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Администратор тарабынан көзөмөлдөнөт"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Администратор иштетип койгон"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Администратор өчүрүп койгон"</string>
     <string name="home" msgid="8263346537524314127">"Башкы бет"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> мурун"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> калды"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index e9017d9..605f476 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງ"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"ຖືກເປີດໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"ຖືກປິດໄວ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບ"</string>
     <string name="home" msgid="8263346537524314127">"​ໜ້າຫຼັກ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ກ່ອນນີ້"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"ຍັງເຫຼືອ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 5c3f5be..253eeb3 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Valdo administratorius"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Įgalino administratorius"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Išjungė administratorius"</string>
     <string name="home" msgid="8263346537524314127">"Pagrindinis ekranas"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Prieš <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Liko <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 27eeaf5..3323cd9 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolē administrators"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Iespējojis administrators"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Atspējojis administrators"</string>
     <string name="home" msgid="8263346537524314127">"Sākums"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Pirms šāda laika: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Atlikušais laiks: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index ec09f21..41c8600 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролирано од администраторот"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Овозможено од администраторот"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Оневозможено од администраторот"</string>
     <string name="home" msgid="8263346537524314127">"Почетна страница"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Пред <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Преостанаа <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 24ad333..80cf43e 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"അഡ്‌മിൻ നിയന്ത്രിക്കുന്നത്"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"അഡ്‌മിനിസ്ട്രേറ്റർ പ്രവർത്തനക്ഷമമാക്കി"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"അഡ്‌മിനിസ്ട്രേറ്റർ പ്രവർത്തനരഹിതമാക്കി"</string>
     <string name="home" msgid="8263346537524314127">"ഹോം"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> മുമ്പ്"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 5466ff1..5e7ea15 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Админ удирдсан"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Админ идэвхтэй болгосон"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Админ идэвхгүй болгосон"</string>
     <string name="home" msgid="8263346537524314127">"Нүүр"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> өмнө"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> үлдсэн"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index ab55a3d..7a3d76d 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकाने नियंत्रित केलेले"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"प्रशासकाने सक्षम केलेले"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"प्रशासकाने अक्षम केलेले"</string>
     <string name="home" msgid="8263346537524314127">"मुख्यपृष्ठ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पूर्वी"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> शिल्लक"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 01b52c1..3f29687 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Dikawal oleh pentadbir"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Didayakan oleh pentadbir"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Dilumpuhkan oleh pentadbir"</string>
     <string name="home" msgid="8263346537524314127">"Skrin Utama"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> yang lalu"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> lagi"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index d3659b7..71862a3 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"စီမံခန့်ခွဲသူမှ ထိန်းချုပ်ပါသည်"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"စီမံခန့်ခွဲသူမှ ဖွင့်ထားသည်"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"စီမံခန့်ခွဲသူမှ ပိတ်ထားသည်"</string>
     <string name="home" msgid="8263346537524314127">"ပင်မ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"ပြီးခဲ့သည့် <xliff:g id="ID_1">%1$s</xliff:g> က"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ကျန်ပါသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 6865d50..2ad14dc 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrollert av administratoren"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Slått på av administratoren"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Avslått av administratoren"</string>
     <string name="home" msgid="8263346537524314127">"Startside"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> siden"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> gjenstår"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 7742b9d..6c8dc59 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"प्रशासकद्वारा नियन्त्रित"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"प्रशासकद्वारा सक्षम गरिएको छ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"प्रशासकद्वारा असक्षम गरिएको छ"</string>
     <string name="home" msgid="8263346537524314127">"गृह"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> पहिले"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> बाँकी"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index efee429..7f9df25 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ingesteld door beheerder"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Ingeschakeld door beheerder"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Uitgeschakeld door beheerder"</string>
     <string name="home" msgid="8263346537524314127">"Startpagina"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> geleden"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> resterend"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 15e6635..10d57f4 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਯੋਗ ਬਣਾਈ ਗਈ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
     <string name="home" msgid="8263346537524314127">"ਮੁੱਖ ਸਕ੍ਰੀਨ"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> ਪਹਿਲਾਂ"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> ਬਾਕੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 9d8d97a..3e89094 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolowane przez administratora"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Włączone przez administratora"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Wyłączone przez administratora"</string>
     <string name="home" msgid="8263346537524314127">"Ekran główny"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> temu"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Pozostało <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 844ae41..c772ded 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Ativada pelo administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Desativada pelo administrador"</string>
     <string name="home" msgid="8263346537524314127">"Início"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 115f951..2b3dcc7 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está a carregar"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Completo"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlado pelo administrador"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Ativado pelo administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Desativado pelo administrador"</string>
     <string name="home" msgid="8263346537524314127">"Página inicial"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Há <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Resta(m) <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 844ae41..c772ded 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlada pelo admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Ativada pelo administrador"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Desativada pelo administrador"</string>
     <string name="home" msgid="8263346537524314127">"Início"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> atrás"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> restante(s)"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0faf848..0322f55 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Controlată de administrator"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Activată de administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Dezactivată de administrator"</string>
     <string name="home" msgid="8263346537524314127">"Ecranul principal"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Acum <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Timp rămas: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 09f792e..7548ace 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролируется администратором"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Включено администратором"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Отключено администратором"</string>
     <string name="home" msgid="8263346537524314127">"Главная"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> назад"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Осталось <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 20cff85..f3d6dc3 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"පරිපාලක විසින් පාලනය කරන ලදී"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"පරිපාලක විසින් සබල කරන ලදී"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"පරිපාලක විසින් අබල කරන ලදී"</string>
     <string name="home" msgid="8263346537524314127">"මුල් පිටුව"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>කට පෙර"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g>ක් ඉතිරිය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 5640b13..712c348 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Ovládané správcom"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Povolené správcom"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Zakázané správcom"</string>
     <string name="home" msgid="8263346537524314127">"Domov"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"pred <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Zostáva <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index ffe794e..2f23144 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Nadzira skrbnik"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Omogočil skrbnik"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Onemogočil skrbnik"</string>
     <string name="home" msgid="8263346537524314127">"Začetni zaslon"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Pred toliko časa: <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Še <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index c84430c..853dd2f 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kontrolluar nga administratori"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Aktivizuar nga administratori"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Çaktivizuar nga administratori"</string>
     <string name="home" msgid="8263346537524314127">"Kreu"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> më parë"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> të mbetura"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index af3daba..094a405 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Контролише администратор"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Омогућио је администратор"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Онемогућио је администратор"</string>
     <string name="home" msgid="8263346537524314127">"Почетни"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Пре <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Још <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 1cd9b6f..e12d394 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Strys av administratören"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Har aktiverats av administratören"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Har inaktiverats av administratören"</string>
     <string name="home" msgid="8263346537524314127">"Startsida"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"för <xliff:g id="ID_1">%1$s</xliff:g> sedan"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kvar"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 24d626c..d5a9d1b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Imedhibitiwa na msimamizi"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Msimamizi amewasha mapendeleo ya mipangilio"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Msimamizi amezima mapendeleo ya mipangilio"</string>
     <string name="home" msgid="8263346537524314127">"Mwanzo"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"Zimepita <xliff:g id="ID_1">%1$s</xliff:g>"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Zimesalia <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index fac31df..5c7732f 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"நிர்வாகி கட்டுப்படுத்துகிறார்"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"நிர்வாகி இயக்கியுள்ளார்"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"நிர்வாகி முடக்கியுள்ளார்"</string>
     <string name="home" msgid="8263346537524314127">"முகப்பு"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> முன்"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> உள்ளது"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 4b4aabf..a74fc82 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"నిర్వాహకుని ద్వారా నియంత్రించబడింది"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"నిర్వాహకులు ప్రారంభించారు"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"నిర్వాహకులు నిలిపివేసారు"</string>
     <string name="home" msgid="8263346537524314127">"హోమ్"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> క్రితం"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> మిగిలి ఉంది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 6906671..53caea5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"ผู้ดูแลระบบเป็นผู้ควบคุม"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"เปิดใช้โดยผู้ดูแลระบบ"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"ปิดใช้โดยผู้ดูแลระบบ"</string>
     <string name="home" msgid="8263346537524314127">"หน้าแรก"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>ที่ผ่านมา"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"เหลือ <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a472075..0c1f167 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Pinapamahalaan ng admin"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Na-enable ng administrator"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Na-disable ng administrator"</string>
     <string name="home" msgid="8263346537524314127">"Home"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> na ang nakalipas"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> na lang"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 85b6080..230da22 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Yönetici tarafından denetleniyor"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Yönetici tarafından etkinleştirildi"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Yönetici tarafından devre dışı bırakıldı"</string>
     <string name="home" msgid="8263346537524314127">"Ana Ekran"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> önce"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> kaldı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 32cd586..01cedc6 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Керується адміністратором"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Увімкнено адміністратором"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Вимкнено адміністратором"</string>
     <string name="home" msgid="8263346537524314127">"Головний екран"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> тому"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Залишилося <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 0eaead8..878ff0c 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"کنٹرول کردہ بذریعہ منتظم"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"منتظم نے فعال کر دیا"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"منتظم نے غیر فعال کر دیا"</string>
     <string name="home" msgid="8263346537524314127">"ہوم"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> قبل"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> باقی ہیں"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index 30a8576..d81a041 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Administrator tomonidan boshqariladi"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Administrator tomonidan yoqilgan"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Administrator tomonidan o‘chirilgan"</string>
     <string name="home" msgid="8263346537524314127">"Bosh ekran"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> oldin"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> qoldi"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 38ddcc0..2b4bddd 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hiện không sạc"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Đầy"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Do quản trị viên kiểm soát"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Được bật bởi quản trị viên"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Bị tắt bởi quản trị viên"</string>
     <string name="home" msgid="8263346537524314127">"Màn hình chính"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> trước"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"Còn <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4001d2c..48f03b7 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"由管理员控制"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"已被管理员启用"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"已被管理员禁用"</string>
     <string name="home" msgid="8263346537524314127">"主屏幕"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"还剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 02be160..38656e5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"已由管理員啟用"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"已由管理員停用"</string>
     <string name="home" msgid="8263346537524314127">"主畫面"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"尚餘 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 7407e28..af83365 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"已由管理員停用"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"已由管理員啟用"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"已由管理員停用"</string>
     <string name="home" msgid="8263346537524314127">"主畫面"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g>前"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"還剩 <xliff:g id="ID_1">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e563bff..16841d3 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -311,6 +311,8 @@
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
     <string name="disabled_by_admin_summary_text" msgid="6750513964908334617">"Kulawulwa umqondisi"</string>
+    <string name="enabled_by_admin" msgid="2386503803463071894">"Kunikwe amandla umqondisi"</string>
+    <string name="disabled_by_admin" msgid="3669999613095206948">"Ikhutshazwe umlawuli"</string>
     <string name="home" msgid="8263346537524314127">"Ekhaya"</string>
     <string name="charge_length_format" msgid="8978516217024434156">"<xliff:g id="ID_1">%1$s</xliff:g> edlule"</string>
     <string name="remaining_length_format" msgid="7886337596669190587">"<xliff:g id="ID_1">%1$s</xliff:g> osele"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 99bd4ad..9268994 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -22,6 +22,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -40,12 +41,21 @@
             new TextUtils.SimpleStringSplitter(ENABLED_ACCESSIBILITY_SERVICES_SEPARATOR);
 
     /**
-     * @return the set of enabled accessibility services. If there are not services
-     * it returned the unmodifiable {@link Collections#emptySet()}.
+     * @return the set of enabled accessibility services. If there are no services,
+     * it returns the unmodifiable {@link Collections#emptySet()}.
      */
     public static Set<ComponentName> getEnabledServicesFromSettings(Context context) {
-        final String enabledServicesSetting = Settings.Secure.getString(
-                context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+        return getEnabledServicesFromSettings(context, UserHandle.myUserId());
+    }
+
+    /**
+     * @return the set of enabled accessibility services for {@param userId}. If there are no
+     * services, it returns the unmodifiable {@link Collections#emptySet()}.
+     */
+    public static Set<ComponentName> getEnabledServicesFromSettings(Context context, int userId) {
+        final String enabledServicesSetting = Settings.Secure.getStringForUser(
+                context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                userId);
         if (enabledServicesSetting == null) {
             return Collections.emptySet();
         }
@@ -77,11 +87,22 @@
         return langContext.getText(resId);
     }
 
+    /**
+     * Changes an accessibility component's state.
+     */
     public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
             boolean enabled) {
+        setAccessibilityServiceState(context, toggledService, enabled, UserHandle.myUserId());
+    }
+
+    /**
+     * Changes an accessibility component's state for {@param userId}.
+     */
+    public static void setAccessibilityServiceState(Context context, ComponentName toggledService,
+            boolean enabled, int userId) {
         // Parse the enabled services.
         Set<ComponentName> enabledServices = AccessibilityUtils.getEnabledServicesFromSettings(
-                context);
+                context, userId);
 
         if (enabledServices.isEmpty()) {
             enabledServices = new ArraySet<>(1);
@@ -121,13 +142,14 @@
         if (enabledServicesBuilderLength > 0) {
             enabledServicesBuilder.deleteCharAt(enabledServicesBuilderLength - 1);
         }
-        Settings.Secure.putString(context.getContentResolver(),
+        Settings.Secure.putStringForUser(context.getContentResolver(),
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                enabledServicesBuilder.toString());
+                enabledServicesBuilder.toString(), userId);
 
         // Update accessibility enabled.
-        Settings.Secure.putInt(context.getContentResolver(),
-                Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0);
+        Settings.Secure.putIntForUser(context.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_ENABLED, accessibilityEnabled ? 1 : 0,
+                userId);
     }
 
     private static Set<ComponentName> getInstalledServices(Context context) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index d89abf42..b79ce80 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -62,8 +62,9 @@
      */
     private static final ArraySet<String> sBroadcastOnRestore;
     static {
-        sBroadcastOnRestore = new ArraySet<String>(3);
+        sBroadcastOnRestore = new ArraySet<String>(4);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+        sBroadcastOnRestore.add(Settings.Secure.ENABLED_VR_LISTENERS);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
         sBroadcastOnRestore.add(Settings.Secure.ENABLED_INPUT_METHODS);
     }
diff --git a/packages/Shell/res/values-ky-rKG/strings.xml b/packages/Shell/res/values-ky-rKG/strings.xml
index 1903109..c701b7e 100644
--- a/packages/Shell/res/values-ky-rKG/strings.xml
+++ b/packages/Shell/res/values-ky-rKG/strings.xml
@@ -35,6 +35,6 @@
     <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"Мүчүлүштүк тууралуу билдирүүнүн <xliff:g id="ID">#%d</xliff:g> чоо-жайы"</string>
     <string name="bugreport_info_name" msgid="4414036021935139527">"Файлдын аталышы"</string>
     <string name="bugreport_info_title" msgid="2306030793918239804">"Мүчүлүштүктүн аталышы"</string>
-    <string name="bugreport_info_description" msgid="5072835127481627722">"Мүчүлүштүктүн корутундусу"</string>
+    <string name="bugreport_info_description" msgid="5072835127481627722">"Мүчүлүштүк корутундусу"</string>
     <string name="save" msgid="4781509040564835759">"Сактоо"</string>
 </resources>
diff --git a/packages/Shell/res/values-ta-rIN/strings.xml b/packages/Shell/res/values-ta-rIN/strings.xml
index 8238d25..c14da56 100644
--- a/packages/Shell/res/values-ta-rIN/strings.xml
+++ b/packages/Shell/res/values-ta-rIN/strings.xml
@@ -34,7 +34,7 @@
     <string name="bugreport_screenshot_failed" msgid="5853049140806834601">"ஸ்கிரீன் ஷாட்டை எடுக்க முடியவில்லை."</string>
     <string name="bugreport_info_dialog_title" msgid="1355948594292983332">"பிழை அறிக்கை <xliff:g id="ID">#%d</xliff:g> இன் விவரங்கள்"</string>
     <string name="bugreport_info_name" msgid="4414036021935139527">"கோப்புப்பெயர்"</string>
-    <string name="bugreport_info_title" msgid="2306030793918239804">"பிழைத் தலைப்பு"</string>
+    <string name="bugreport_info_title" msgid="2306030793918239804">"பிழை தலைப்பு"</string>
     <string name="bugreport_info_description" msgid="5072835127481627722">"பிழை குறித்த சுருக்க விவரம்"</string>
     <string name="save" msgid="4781509040564835759">"சேமி"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index 5666c5f..3a7c6cd 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -456,11 +456,11 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"Želiti li uključiti Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"Da povežete tastaturu sa tabletom, prvo morate uključiti Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Uključi"</string>
-    <string name="show_silently" msgid="6841966539811264192">"Nečujno pokaži obavijesti"</string>
+    <string name="show_silently" msgid="6841966539811264192">"Nečujno prikaži obavijesti"</string>
     <string name="block" msgid="2734508760962682611">"Blokiraj sva obavještenja"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"Nemoj utišati"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"Nemoj utišati ili blokirati"</string>
-    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Pokaži kompletne postavke za određivanje značaja"</string>
+    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Prikaži kompletne postavke za određivanje značaja"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Blokirano"</string>
     <string name="min_importance" msgid="1901894910809414782">"Minimalni značaj"</string>
     <string name="low_importance" msgid="4109929986107147930">"Mali značaj"</string>
@@ -468,11 +468,11 @@
     <string name="high_importance" msgid="1527066195614050263">"Visok značaj"</string>
     <string name="max_importance" msgid="5089005872719563894">"Hitan značaj"</string>
     <string name="notification_importance_blocked" msgid="2397192642657872872">"Nikada ne prikazuj ova obavještenja"</string>
-    <string name="notification_importance_min" msgid="1938190340516905748">"Nečujno pokaži na dnu spiska obavještenja"</string>
+    <string name="notification_importance_min" msgid="1938190340516905748">"Nečujno prikaži na dnu spiska obavještenja"</string>
     <string name="notification_importance_low" msgid="3657252049508213048">"Nečujno prikaži ova obavještenja"</string>
     <string name="notification_importance_default" msgid="4466466472622442175">"Dozvolite zvuk na ovim obavještenjima"</string>
-    <string name="notification_importance_high" msgid="2135428926525093825">"Kratko prikaži na ekranu i dozvoli zvuk i dozvoli zvuk"</string>
-    <string name="notification_importance_max" msgid="5806278962376556491">"Pokaži na vrhu liste obaveštenja, kratko prikaži na ekranu i dozvoli zvuk"</string>
+    <string name="notification_importance_high" msgid="2135428926525093825">"Kratko prikaži na ekranu i dozvoli zvuk"</string>
+    <string name="notification_importance_max" msgid="5806278962376556491">"Prikaži na vrhu liste obavještenja, kratko prikaži na ekranu i dozvoli zvuk"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Više postavki"</string>
     <string name="notification_done" msgid="5279426047273930175">"Gotovo"</string>
     <string name="color_and_appearance" msgid="1254323855964993144">"Boja i izgled"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3747be0..f8224ac 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -459,7 +459,7 @@
     <string name="block" msgid="2734508760962682611">"Bloqueja totes les notificacions"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"No silenciïs"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"No silenciïs ni bloquegis"</string>
-    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra la configuració de la importància completa"</string>
+    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra la configuració completa per a la importància"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Bloquejades"</string>
     <string name="min_importance" msgid="1901894910809414782">"Importància mínima"</string>
     <string name="low_importance" msgid="4109929986107147930">"Importància baixa"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index b3eda73..4092994 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -303,8 +303,7 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"fijación de pantalla"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"buscar"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"No se ha podido iniciar <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
-    <skip />
+    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"La aplicación <xliff:g id="APP">%s</xliff:g> se ha inhabilitado en modo seguro."</string>
     <string name="recents_history_button_label" msgid="5153358867807604821">"Historial"</string>
     <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Borrar"</string>
     <string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Esta aplicación no admite el modo multiventana."</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 3f8e844..29e3da8 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -459,7 +459,7 @@
     <string name="block" msgid="2734508760962682611">"Blokeatu jakinarazpen guztiak"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"Ez isilarazi"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"Ez isilarazi edo blokeatu"</string>
-    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Erakutsi garrantzia handiko jakinarazpenen ezarpenak"</string>
+    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Erakutsi garrantzi handiko jakinarazpenen ezarpenak"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Blokeatuta"</string>
     <string name="min_importance" msgid="1901894910809414782">"Gutxieneko garrantzia"</string>
     <string name="low_importance" msgid="4109929986107147930">"Garrantzi txikia"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index d6622e3..aa6dd28 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -455,23 +455,23 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"Attivare il Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"Per connettere la tastiera al tablet, devi prima attivare il Bluetooth."</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Attiva"</string>
-    <string name="show_silently" msgid="6841966539811264192">"Mostra silenziosamente le notifiche"</string>
+    <string name="show_silently" msgid="6841966539811264192">"Mostra le notifiche silenziosamente"</string>
     <string name="block" msgid="2734508760962682611">"Blocca tutte le notifiche"</string>
-    <string name="do_not_silence" msgid="6878060322594892441">"Non disattivare i suoni"</string>
-    <string name="do_not_silence_block" msgid="4070647971382232311">"Non disattivare i suoni e non bloccare"</string>
-    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra impostazioni importanza complete"</string>
+    <string name="do_not_silence" msgid="6878060322594892441">"Non silenziare"</string>
+    <string name="do_not_silence_block" msgid="4070647971382232311">"Non silenziare e non bloccare"</string>
+    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Mostra impostazioni di importanza complete"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Bloccata"</string>
-    <string name="min_importance" msgid="1901894910809414782">"Importanza min"</string>
+    <string name="min_importance" msgid="1901894910809414782">"Importanza minima"</string>
     <string name="low_importance" msgid="4109929986107147930">"Importanza scarsa"</string>
     <string name="default_importance" msgid="8192107689995742653">"Importanza normale"</string>
     <string name="high_importance" msgid="1527066195614050263">"Importanza elevata"</string>
     <string name="max_importance" msgid="5089005872719563894">"Importanza urgente"</string>
     <string name="notification_importance_blocked" msgid="2397192642657872872">"Non mostrare mai queste notifiche"</string>
-    <string name="notification_importance_min" msgid="1938190340516905748">"Mostra silenziosamente nella parte inferiore dell\'elenco delle notifiche"</string>
-    <string name="notification_importance_low" msgid="3657252049508213048">"Mostra silenziosamente queste notifiche"</string>
-    <string name="notification_importance_default" msgid="4466466472622442175">"Attiva i suoni per queste notifiche"</string>
-    <string name="notification_importance_high" msgid="2135428926525093825">"Apri sullo schermo e attiva l\'audio"</string>
-    <string name="notification_importance_max" msgid="5806278962376556491">"Mostra in cima all\'elenco delle notifiche, apri sullo schermo e attiva l\'audio"</string>
+    <string name="notification_importance_min" msgid="1938190340516905748">"Mostra silenziosamente in fondo all\'elenco delle notifiche"</string>
+    <string name="notification_importance_low" msgid="3657252049508213048">"Mostra queste notifiche silenziosamente"</string>
+    <string name="notification_importance_default" msgid="4466466472622442175">"Attiva l\'audio per queste notifiche"</string>
+    <string name="notification_importance_high" msgid="2135428926525093825">"Visualizza sullo schermo e attiva l\'audio"</string>
+    <string name="notification_importance_max" msgid="5806278962376556491">"Mostra in cima all\'elenco delle notifiche, visualizza sullo schermo e attiva l\'audio"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Altre impostazioni"</string>
     <string name="notification_done" msgid="5279426047273930175">"Fine"</string>
     <string name="color_and_appearance" msgid="1254323855964993144">"Colore e aspetto"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index f8c54b6..720caec 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -303,8 +303,7 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"экранды бекіту"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"іздеу"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> іске қосу мүмкін болмады."</string>
-    <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
-    <skip />
+    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> қауіпсіз режимде өшіріледі."</string>
     <string name="recents_history_button_label" msgid="5153358867807604821">"Тарих"</string>
     <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Тазалау"</string>
     <string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Бұл қолданба көп терезені қолдамайды"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index 66a34f0..96ffa96 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -457,8 +457,8 @@
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"Күйгүзүү"</string>
     <string name="show_silently" msgid="6841966539811264192">"Эскертмелер үнсүз көрсөтүлсүн"</string>
     <string name="block" msgid="2734508760962682611">"Бардык эскертмелерди бөгөттөө"</string>
-    <string name="do_not_silence" msgid="6878060322594892441">"Үнсүз кылынбасын"</string>
-    <string name="do_not_silence_block" msgid="4070647971382232311">"Үнсүз кылынып же бөгөттөлбөсүн"</string>
+    <string name="do_not_silence" msgid="6878060322594892441">"Үнү менен көрсөтүлсүн"</string>
+    <string name="do_not_silence_block" msgid="4070647971382232311">"Үнү менен көрсөтүлүп бөгөттөлбөсүн"</string>
     <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Маанилүүлүк жөндөөлөрү толук көрсөтүлсүн"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Бөгөттөлгөн"</string>
     <string name="min_importance" msgid="1901894910809414782">"Маанилүүлүгү эң төмөн"</string>
@@ -469,7 +469,7 @@
     <string name="notification_importance_blocked" msgid="2397192642657872872">"Бул эскертмелер эч качан көрсөтүлбөсүн"</string>
     <string name="notification_importance_min" msgid="1938190340516905748">"Эскертмелер тизмесинин соңунда үнсүз көрсөтүлсүн"</string>
     <string name="notification_importance_low" msgid="3657252049508213048">"Бул эскертмелер үнсүз көрсөтүлсүн"</string>
-    <string name="notification_importance_default" msgid="4466466472622442175">"Бул эскертмелер добуш чыгарышына уруксат берилсин"</string>
+    <string name="notification_importance_default" msgid="4466466472622442175">"Бул эскертмелер үнү менен көрсөтүлсүн"</string>
     <string name="notification_importance_high" msgid="2135428926525093825">"Үн менен коштолуп, экранга чыгарылсын"</string>
     <string name="notification_importance_max" msgid="5806278962376556491">"Эскертмелер тизмесинин эң башында көрсөтүлүп, үн менен коштолуп, экранга чыгарылсын"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"Дагы жөндөөлөр"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 4b78552..2008553 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -303,8 +303,7 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"स्क्रिन पिन गर्दै"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"खोजी गर्नुहोस्"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"सुरु गर्न सकिएन <xliff:g id="APP">%s</xliff:g>।"</string>
-    <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
-    <skip />
+    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> लाई सुरक्षित-मोडमा असक्षम गरिएको छ।"</string>
     <string name="recents_history_button_label" msgid="5153358867807604821">"इतिहास"</string>
     <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"मेटाउनुहोस्"</string>
     <string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"यस अनुप्रयोगले बहु-विन्डोलाई समर्थन गर्दैन"</string>
@@ -456,11 +455,11 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"ब्लुटुथ सक्रिय पार्ने हो?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"सक्रिय पार्नुहोस्"</string>
-    <string name="show_silently" msgid="6841966539811264192">"सूचनाहरूलाई बिना आवाज देखाउनुहोस्"</string>
+    <string name="show_silently" msgid="6841966539811264192">"सूचनाहरूलाई बिना आवाज देखाउने"</string>
     <string name="block" msgid="2734508760962682611">"सबै सूचनाहरूलाई रोक्नुहोस्"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"मौन नगर्नुहोस्"</string>
     <string name="do_not_silence_block" msgid="4070647971382232311">"मौन नगर्नुहोस् वा नरोक्नुहोस्"</string>
-    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"पूर्ण महत्त्व सेटिङहरू देखाउनुहोस्"</string>
+    <string name="tuner_full_importance_settings" msgid="8103289238676424226">"पूर्ण महत्त्व सेटिङहरू देखाउने"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"रोकियो"</string>
     <string name="min_importance" msgid="1901894910809414782">"न्यूनतम महत्त्व"</string>
     <string name="low_importance" msgid="4109929986107147930">"न्यून महत्त्व"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 4978d94..ddc1c0a 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -461,7 +461,7 @@
     <string name="do_not_silence_block" msgid="4070647971382232311">"Usinyamazishe wala kuzuia"</string>
     <string name="tuner_full_importance_settings" msgid="8103289238676424226">"Onyesha mipangilio kamili ya umuhimu"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"Amezuiwa"</string>
-    <string name="min_importance" msgid="1901894910809414782">"Umuhimu wa kiwango cha chini zaidi"</string>
+    <string name="min_importance" msgid="1901894910809414782">"Umuhimu wa kiwango cha chini"</string>
     <string name="low_importance" msgid="4109929986107147930">"Umuhimu kiwango cha chini"</string>
     <string name="default_importance" msgid="8192107689995742653">"Umuhimu wa kiwango cha kawaida"</string>
     <string name="high_importance" msgid="1527066195614050263">"Umuhimu wa kiwango cha juu"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index e8babe5..074a8a0 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -303,8 +303,7 @@
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"khóa màn hình"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"tìm kiếm"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"Không thể khởi động <xliff:g id="APP">%s</xliff:g>."</string>
-    <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
-    <skip />
+    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> bị tắt ở chế độ an toàn."</string>
     <string name="recents_history_button_label" msgid="5153358867807604821">"Lịch sử"</string>
     <string name="recents_history_clear_all_button_label" msgid="5905258334958006953">"Xóa"</string>
     <string name="recents_drag_non_dockable_task_message" msgid="2935843902795166158">"Ứng dụng này không hỗ trợ chế độ nhiều cửa sổ"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f828306..dd5ea1f 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -458,10 +458,10 @@
     <string name="show_silently" msgid="6841966539811264192">"显示通知,但不发出提示音"</string>
     <string name="block" msgid="2734508760962682611">"屏蔽所有通知"</string>
     <string name="do_not_silence" msgid="6878060322594892441">"不静音"</string>
-    <string name="do_not_silence_block" msgid="4070647971382232311">"不静音或不屏蔽"</string>
+    <string name="do_not_silence_block" msgid="4070647971382232311">"不静音也不屏蔽"</string>
     <string name="tuner_full_importance_settings" msgid="8103289238676424226">"显示完整的重要性设置"</string>
     <string name="blocked_importance" msgid="5198578988978234161">"屏蔽"</string>
-    <string name="min_importance" msgid="1901894910809414782">"最低重要性"</string>
+    <string name="min_importance" msgid="1901894910809414782">"重要性最低"</string>
     <string name="low_importance" msgid="4109929986107147930">"重要性:低"</string>
     <string name="default_importance" msgid="8192107689995742653">"重要性:一般"</string>
     <string name="high_importance" msgid="1527066195614050263">"重要性:高"</string>
@@ -470,8 +470,8 @@
     <string name="notification_importance_min" msgid="1938190340516905748">"在通知列表底部显示,但不发出提示音"</string>
     <string name="notification_importance_low" msgid="3657252049508213048">"显示这些通知,但不发出提示音"</string>
     <string name="notification_importance_default" msgid="4466466472622442175">"允许这些通知发出提示音"</string>
-    <string name="notification_importance_high" msgid="2135428926525093825">"在屏幕上短暂显示,并发出提示音"</string>
-    <string name="notification_importance_max" msgid="5806278962376556491">"在通知列表顶部显示,同时在屏幕上短暂显示,并发出提示音"</string>
+    <string name="notification_importance_high" msgid="2135428926525093825">"在屏幕上短暂显示,并允许发出提示音"</string>
+    <string name="notification_importance_max" msgid="5806278962376556491">"在通知列表顶部显示,同时在屏幕上短暂显示,并允许发出提示音"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"更多设置"</string>
     <string name="notification_done" msgid="5279426047273930175">"完成"</string>
     <string name="color_and_appearance" msgid="1254323855964993144">"颜色和外观"</string>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index b9eee2e..9697ea6 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -48,6 +48,7 @@
     <item type="id" name="transformation_start_y_tag"/>
     <item type="id" name="transformation_start_scale_x_tag"/>
     <item type="id" name="transformation_start_scale_y_tag"/>
+    <item type="id" name="custom_background_color"/>
 
     <!-- Whether the icon is from a notification for which targetSdk < L -->
     <item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 913b2b3..2e94bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -23,7 +23,6 @@
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.shortcut.ShortcutKeyDispatcher;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -72,8 +71,8 @@
     }
 
     public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
-            View headsUpScrim, boolean scrimSrcEnabled) {
-        return new ScrimController(scrimBehind, scrimInFront, headsUpScrim, scrimSrcEnabled);
+            View headsUpScrim) {
+        return new ScrimController(scrimBehind, scrimInFront, headsUpScrim);
     }
 
     public <T> T createInstance(Class<T> classType) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 54bf68b..efac0fb3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -35,9 +35,11 @@
 import android.util.Log;
 import android.view.Display;
 import android.view.View;
+import android.widget.Toast;
 
 import com.android.systemui.EventLogConstants;
 import com.android.systemui.EventLogTags;
+import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.events.EventBus;
@@ -393,28 +395,35 @@
         boolean screenPinningActive = ssp.isScreenPinningActive();
         boolean isTopTaskHome = topTask != null && SystemServicesProxy.isHomeStack(topTask.stackId);
         if (topTask != null && !isTopTaskHome && !screenPinningActive) {
-            if (sSystemServicesProxy.isSystemUser(currentUser)) {
-                mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
-            } else {
-                if (mSystemToUserCallbacks != null) {
-                    IRecentsNonSystemUserCallbacks callbacks =
-                            mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
-                    if (callbacks != null) {
-                        try {
-                            callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
-                                    initialBounds);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Callback failed", e);
+            if (topTask.isDockable) {
+                if (sSystemServicesProxy.isSystemUser(currentUser)) {
+                    mImpl.dockTopTask(topTask.id, dragMode, stackCreateMode, initialBounds);
+                } else {
+                    if (mSystemToUserCallbacks != null) {
+                        IRecentsNonSystemUserCallbacks callbacks =
+                                mSystemToUserCallbacks.getNonSystemUserRecentsForUser(currentUser);
+                        if (callbacks != null) {
+                            try {
+                                callbacks.dockTopTask(topTask.id, dragMode, stackCreateMode,
+                                        initialBounds);
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Callback failed", e);
+                            }
+                        } else {
+                            Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                         }
-                    } else {
-                        Log.e(TAG, "No SystemUI callbacks found for user: " + currentUser);
                     }
                 }
+                mDraggingInRecentsCurrentUser = currentUser;
+                return true;
+            } else {
+                Toast.makeText(mContext, R.string.recents_drag_non_dockable_task_message,
+                        Toast.LENGTH_SHORT).show();
+                return false;
             }
-            mDraggingInRecentsCurrentUser = currentUser;
-            return true;
+        } else {
+            return false;
         }
-        return false;
     }
 
     @Override
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 699b85e..a5ed32a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -916,11 +916,11 @@
         float peekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
         float slope = ((1f - peekHeightPct) - cpoint1Y) / (0.5f - cpoint1X);
         float b = 1f - slope * cpoint1X;
-        float cpoint2X = 0.75f;
+        float cpoint2X = 0.65f;
         float cpoint2Y = slope * cpoint2X + b;
         Path p = new Path();
         p.moveTo(0f, 1f);
-        p.cubicTo(0f, 1f, cpoint1X, 1f, 0.5f, 1f - peekHeightPct);
+        p.cubicTo(0f, 1f, cpoint1X, cpoint1Y, 0.5f, 1f - peekHeightPct);
         p.cubicTo(0.5f, 1f - peekHeightPct, cpoint2X, cpoint2Y, 1f, 0f);
         return p;
     }
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 6abb826..9da8fee 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -307,7 +307,16 @@
             // measure/layout pass
             updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
             updateToInitialState();
-            relayoutTaskViewsOnNextFrame(AnimationProps.IMMEDIATE);
+            relayoutTaskViews(AnimationProps.IMMEDIATE);
+
+            // Rebind all the task views.  This will not trigger new resources to be loaded unless
+            // they have actually changed
+            List<TaskView> taskViews = getTaskViews();
+            int taskViewCount = taskViews.size();
+            for (int i = 0; i < taskViewCount; i++) {
+                TaskView tv = taskViews.get(i);
+                bindTaskView(tv, tv.getTask());
+            }
         }
     }
 
@@ -640,9 +649,9 @@
         List<TaskView> taskViews = getTaskViews();
         int taskViewCount = taskViews.size();
         for (int i = 0; i < taskViewCount; i++) {
-            final TaskView tv = taskViews.get(i);
-            final int taskIndex = mStack.indexOfStackTask(tv.getTask());
-            final TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
+            TaskView tv = taskViews.get(i);
+            int taskIndex = mStack.indexOfStackTask(tv.getTask());
+            TaskViewTransform transform = mCurrentTaskTransforms.get(taskIndex);
 
             if (ignoreTasksSet.contains(tv.getTask().key)) {
                 continue;
@@ -1430,8 +1439,8 @@
     public void onReturnViewToPool(TaskView tv) {
         final Task task = tv.getTask();
 
-        // Report that this task's data is no longer being used
-        Recents.getTaskLoader().unloadTaskData(task);
+        // Unbind the task from the task view
+        unbindTaskView(tv, task);
 
         // Reset the view properties and view state
         tv.resetViewProperties();
@@ -1476,11 +1485,8 @@
         // Update the task views list after adding the new task view
         updateTaskViewsList();
 
-        // Rebind the task and request that this task's data be filled into the TaskView
-        tv.onTaskBound(task);
-
-        // Load the task data
-        Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+        // Bind the task view to the new task
+        bindTaskView(tv, task);
 
         // If the doze trigger has already fired, then update the state for this task view
         if (mUIDozeTrigger.isAsleep()) {
@@ -1512,6 +1518,19 @@
         return (tv.getTask() == preferredData);
     }
 
+    private void bindTaskView(TaskView tv, Task task) {
+        // Rebind the task and request that this task's data be filled into the TaskView
+        tv.onTaskBound(task);
+
+        // Load the task data
+        Recents.getTaskLoader().loadTaskData(task, true /* fetchAndInvalidateThumbnails */);
+    }
+
+    private void unbindTaskView(TaskView tv, Task task) {
+        // Report that this task's data is no longer being used
+        Recents.getTaskLoader().unloadTaskData(task);
+    }
+
     /**** TaskViewCallbacks Implementation ****/
 
     @Override
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 52f8fc8..8635911 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -20,6 +20,7 @@
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Path;
 import android.graphics.Rect;
 import android.util.ArrayMap;
 import android.util.MutableBoolean;
@@ -44,6 +45,7 @@
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
 import com.android.systemui.recents.events.ui.StackViewScrolledEvent;
 import com.android.systemui.recents.events.ui.TaskViewDismissedEvent;
+import com.android.systemui.recents.misc.FreePathInterpolator;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 import com.android.systemui.recents.model.Task;
@@ -60,6 +62,16 @@
     private static final int INACTIVE_POINTER_ID = -1;
     private static final Interpolator STACK_TRANSFORM_INTERPOLATOR =
             new PathInterpolator(0.73f, 0.33f, 0.42f, 0.85f);
+    // The min overscroll is the amount of task progress overscroll we want / the max overscroll
+    // curve value below
+    private static final float MAX_OVERSCROLL = 0.7f / 0.3f;
+    private static final Interpolator OVERSCROLL_INTERP;
+    static {
+        Path OVERSCROLL_PATH = new Path();
+        OVERSCROLL_PATH.moveTo(0, 0);
+        OVERSCROLL_PATH.cubicTo(0.2f, 0.175f, 0.25f, 0.3f, 1f, 0.3f);
+        OVERSCROLL_INTERP = new FreePathInterpolator(OVERSCROLL_PATH);
+    }
 
     Context mContext;
     TaskStackView mSv;
@@ -245,6 +257,18 @@
                     // of the curve, so just move the scroll proportional to that
                     float deltaP = layoutAlgorithm.getDeltaPForY(mDownY, y);
                     float curScrollP = mDownScrollP + deltaP;
+
+                    // Modulate the overscroll to prevent users from pulling the stack too far
+                    float minScrollP = layoutAlgorithm.mMinScrollP;
+                    float maxScrollP = layoutAlgorithm.mMaxScrollP;
+                    if (curScrollP < minScrollP || curScrollP > maxScrollP) {
+                        float clampedScrollP = Utilities.clamp(curScrollP, minScrollP, maxScrollP);
+                        float overscrollP = (curScrollP - clampedScrollP);
+                        float overscrollX = Math.abs(overscrollP) / MAX_OVERSCROLL;
+                        curScrollP = clampedScrollP + (Math.signum(overscrollP) *
+                                (OVERSCROLL_INTERP.getInterpolation(overscrollX) * MAX_OVERSCROLL));
+                    }
+
                     mScroller.setStackScroll(curScrollP);
                     mStackViewScrolledEvent.updateY(y - mLastY);
                     EventBus.getDefault().send(mStackViewScrolledEvent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index effe581..5652c7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -36,7 +36,9 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.notification.FakeShadowView;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 
 /**
  * Base class for both {@link ExpandableNotificationRow} and {@link NotificationOverflowContainer}
@@ -121,6 +123,7 @@
     private float mAnimationTranslationY;
     private boolean mDrawingAppearAnimation;
     private ValueAnimator mAppearAnimator;
+    private ValueAnimator mBackgroundColorAnimator;
     private float mAppearAnimationFraction = -1.0f;
     private float mAppearAnimationTranslation;
     private boolean mShowingLegacyBackground;
@@ -157,6 +160,9 @@
     };
     private float mShadowAlpha = 1.0f;
     private FakeShadowView mFakeShadow;
+    private int mCurrentBackgroundTint;
+    private int mTargetTint;
+    private int mStartTint;
 
     public ActivatableNotificationView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -457,21 +463,63 @@
      * Sets the tint color of the background
      */
     public void setTintColor(int color) {
+        setTintColor(color, false);
+    }
+
+    /**
+     * Sets the tint color of the background
+     */
+    public void setTintColor(int color, boolean animated) {
         mBgTint = color;
-        updateBackgroundTint();
+        updateBackgroundTint(animated);
     }
 
     protected void updateBackgroundTint() {
-        int color = getBgColor();
+        updateBackgroundTint(false /* animated */);
+    }
+
+    private void updateBackgroundTint(boolean animated) {
+        if (mBackgroundColorAnimator != null) {
+            mBackgroundColorAnimator.cancel();
+        }
         int rippleColor = getRippleColor();
+        mBackgroundDimmed.setRippleColor(rippleColor);
+        mBackgroundNormal.setRippleColor(rippleColor);
+        int color = calculateBgColor();
+        if (!animated) {
+            setBackgroundTintColor(color);
+        } else if (color != mCurrentBackgroundTint) {
+            mStartTint = mCurrentBackgroundTint;
+            mTargetTint = color;
+            mBackgroundColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
+            mBackgroundColorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                @Override
+                public void onAnimationUpdate(ValueAnimator animation) {
+                    int newColor = NotificationUtils.interpolateColors(mStartTint, mTargetTint,
+                            animation.getAnimatedFraction());
+                    setBackgroundTintColor(newColor);
+                }
+            });
+            mBackgroundColorAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            mBackgroundColorAnimator.setInterpolator(Interpolators.LINEAR);
+            mBackgroundColorAnimator.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    mBackgroundColorAnimator = null;
+                }
+            });
+            mBackgroundColorAnimator.start();
+        }
+    }
+
+    private void setBackgroundTintColor(int color) {
+        mCurrentBackgroundTint = color;
         if (color == mNormalColor) {
             // We don't need to tint a normal notification
             color = 0;
         }
         mBackgroundDimmed.setTint(color);
         mBackgroundNormal.setTint(color);
-        mBackgroundDimmed.setRippleColor(rippleColor);
-        mBackgroundNormal.setRippleColor(rippleColor);
     }
 
     /**
@@ -773,8 +821,12 @@
 
     protected abstract View getContentView();
 
-    public int getBgColor() {
-        if (mBgTint != 0) {
+    public int calculateBgColor() {
+        return calculateBgColor(true /* withTint */);
+    }
+
+    private int calculateBgColor(boolean withTint) {
+        if (withTint && mBgTint != 0) {
             return mBgTint;
         } else if (mShowingLegacyBackground) {
             return mLegacyColor;
@@ -839,7 +891,7 @@
     }
 
     public boolean hasSameBgColor(ActivatableNotificationView otherView) {
-        return getBgColor() == otherView.getBgColor();
+        return calculateBgColor() == otherView.calculateBgColor();
     }
 
     @Override
@@ -863,6 +915,10 @@
                 outlineTranslation);
     }
 
+    public int getBackgroundColorWithoutTint() {
+        return calculateBgColor(false /* withTint */);
+    }
+
     public interface OnActivatedListener {
         void onActivated(ActivatableNotificationView view);
         void onActivationReset(ActivatableNotificationView view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 12a83fd..c2e1f7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -411,8 +411,7 @@
     }
 
     public ExpandableNotificationRow getViewAtPosition(float y) {
-        if (!mIsSummaryWithChildren || !mChildrenExpanded
-                || (getNotificationChildren().size() == 1 && isClearable())) {
+        if (!mIsSummaryWithChildren || !mChildrenExpanded) {
             return this;
         } else {
             ExpandableNotificationRow view = mChildrenContainer.getViewAtPosition(y);
@@ -569,6 +568,13 @@
         mPublicLayout.reInflateViews();
     }
 
+    public void setContentBackground(int customBackgroundColor, boolean animate,
+            NotificationContentView notificationContentView) {
+        if (getShowingLayout() == notificationContentView) {
+            setTintColor(customBackgroundColor, animate);
+        }
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
@@ -1011,7 +1017,6 @@
         }
         mPrivateLayout.updateExpandButtons(isExpandable());
         updateChildrenHeaderAppearance();
-        updateHeaderChildCount();
         updateChildrenVisibility();
     }
 
@@ -1024,7 +1029,11 @@
      * @return whether the view state is currently expanded.
      */
     public boolean isExpanded() {
-        return !mOnKeyguard
+        return isExpanded(false /* allowOnKeyguard */);
+    }
+
+    public boolean isExpanded(boolean allowOnKeyguard) {
+        return (!mOnKeyguard || allowOnKeyguard)
                 && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded())
                 || isUserExpanded());
     }
@@ -1104,7 +1113,8 @@
         } else {
             animateShowingPublic(delay, duration);
         }
-
+        NotificationContentView showingLayout = getShowingLayout();
+        showingLayout.updateBackgroundColor(animated);
         mPrivateLayout.updateExpandButtons(isExpandable());
         updateClearability();
         mShowingPublicInitialized = true;
@@ -1162,13 +1172,6 @@
         }
     }
 
-    public void updateHeaderChildCount() {
-        if (mIsSummaryWithChildren) {
-            mNotificationHeader.setChildCount(
-                    mChildrenContainer.getNotificationChildren().size());
-        }
-    }
-
     public static void applyTint(View v, int color) {
         int alpha;
         if (color != 0) {
@@ -1280,15 +1283,7 @@
             header.reapply(getContext(), mNotificationHeader);
             mNotificationHeaderWrapper.notifyContentUpdated(mEntry.notification);
         }
-        updateHeaderExpandButton();
         updateChildrenHeaderAppearance();
-        updateHeaderChildCount();
-    }
-
-    private void updateHeaderExpandButton() {
-        if (mIsSummaryWithChildren) {
-            mNotificationHeader.setIsGroupHeader(true /* isGroupHeader*/);
-        }
     }
 
     public void updateChildrenHeaderAppearance() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index b94c15b..3b87577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -33,6 +33,7 @@
 import com.android.systemui.statusbar.notification.HybridNotificationView;
 import com.android.systemui.statusbar.notification.HybridNotificationViewManager;
 import com.android.systemui.statusbar.notification.NotificationCustomViewWrapper;
+import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.NotificationViewWrapper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.RemoteInputView;
@@ -48,7 +49,7 @@
     private static final int VISIBLE_TYPE_EXPANDED = 1;
     private static final int VISIBLE_TYPE_HEADSUP = 2;
     private static final int VISIBLE_TYPE_SINGLELINE = 3;
-    private static final int UNDEFINED = -1;
+    public static final int UNDEFINED = -1;
 
     private final Rect mClipBounds = new Rect();
     private final int mMinContractedHeight;
@@ -367,6 +368,7 @@
             getViewForVisibleType(visibleType).setVisibility(View.VISIBLE);
             hiddenView.transformTo(shownView, 0.0f);
             mVisibleType = visibleType;
+            updateBackgroundColor(true /* animate */);
         }
         if (mTransformationStartVisibleType != UNDEFINED
                 && mVisibleType != mTransformationStartVisibleType) {
@@ -376,11 +378,29 @@
             float transformationAmount = calculateTransformationAmount();
             shownView.transformFrom(hiddenView, transformationAmount);
             hiddenView.transformTo(shownView, transformationAmount);
+            updateBackgroundTransformation(transformationAmount);
         } else {
             updateViewVisibilities(visibleType);
+            updateBackgroundColor(false);
         }
     }
 
+    private void updateBackgroundTransformation(float transformationAmount) {
+        int endColor = getBackgroundColor(mVisibleType);
+        int startColor = getBackgroundColor(mTransformationStartVisibleType);
+        if (endColor != startColor) {
+            if (startColor == 0) {
+                startColor = mContainingNotification.getBackgroundColorWithoutTint();
+            }
+            if (endColor == 0) {
+                endColor = mContainingNotification.getBackgroundColorWithoutTint();
+            }
+            endColor = NotificationUtils.interpolateColors(startColor, endColor,
+                    transformationAmount);
+        }
+        mContainingNotification.setContentBackground(endColor, false, this);
+    }
+
     private float calculateTransformationAmount() {
         int startHeight = getViewForVisibleType(mTransformationStartVisibleType).getHeight();
         int endHeight = getViewForVisibleType(mVisibleType).getHeight();
@@ -457,9 +477,24 @@
                 updateViewVisibilities(visibleType);
             }
             mVisibleType = visibleType;
+            updateBackgroundColor(animate);
         }
     }
 
+    public void updateBackgroundColor(boolean animate) {
+        int customBackgroundColor = getBackgroundColor(mVisibleType);
+        mContainingNotification.setContentBackground(customBackgroundColor, animate, this);
+    }
+
+    private int getBackgroundColor(int visibleType) {
+        NotificationViewWrapper currentVisibleWrapper = getVisibleWrapper(visibleType);
+        int customBackgroundColor = 0;
+        if (currentVisibleWrapper != null) {
+            customBackgroundColor = currentVisibleWrapper.getCustomBackgroundColor();
+        }
+        return customBackgroundColor;
+    }
+
     private void updateViewVisibilities(int visibleType) {
         boolean contractedVisible = visibleType == VISIBLE_TYPE_CONTRACTED;
         mContractedWrapper.setVisible(contractedVisible);
@@ -530,8 +565,8 @@
         }
     }
 
-    private NotificationViewWrapper getCurrentVisibleWrapper() {
-        switch (mVisibleType) {
+    private NotificationViewWrapper getVisibleWrapper(int visibleType) {
+        switch (visibleType) {
             case VISIBLE_TYPE_EXPANDED:
                 return mExpandedWrapper;
             case VISIBLE_TYPE_HEADSUP:
@@ -549,7 +584,7 @@
     private int calculateVisibleType() {
         if (mUserExpanding) {
             int height = !mIsChildInGroup || isGroupExpanded()
-                    || mContainingNotification.isExpanded()
+                    || mContainingNotification.isExpanded(true /* allowOnKeyguard */)
                     ? mContainingNotification.getMaxContentHeight()
                     : mContainingNotification.getShowingLayout().getMinHeight();
             if (height == 0) {
@@ -588,7 +623,8 @@
             }
         } else {
             if (noExpandedChild || (viewHeight <= mContractedChild.getHeight()
-                    && (!mIsChildInGroup || !mContainingNotification.isExpanded()))) {
+                    && (!mIsChildInGroup
+                            || !mContainingNotification.isExpanded(true /* allowOnKeyguard */)))) {
                 return VISIBLE_TYPE_CONTRACTED;
             } else {
                 return VISIBLE_TYPE_EXPANDED;
@@ -605,7 +641,6 @@
             return;
         }
         mDark = dark;
-        dark = dark && !mShowingLegacyBackground;
         if (mVisibleType == VISIBLE_TYPE_CONTRACTED || !dark) {
             mContractedWrapper.setDark(dark, fade, delay);
         }
@@ -636,6 +671,19 @@
 
     public void setShowingLegacyBackground(boolean showing) {
         mShowingLegacyBackground = showing;
+        updateShowingLegacyBackground();
+    }
+
+    private void updateShowingLegacyBackground() {
+        if (mContractedChild != null) {
+            mContractedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+        }
+        if (mExpandedChild != null) {
+            mExpandedWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+        }
+        if (mHeadsUpChild != null) {
+            mHeadsUpWrapper.setShowingLegacyBackground(mShowingLegacyBackground);
+        }
     }
 
     public void setIsChildInGroup(boolean isChildInGroup) {
@@ -657,6 +705,7 @@
         if (mHeadsUpChild != null) {
             mHeadsUpWrapper.notifyContentUpdated(entry.notification);
         }
+        updateShowingLegacyBackground();
         selectLayout(false /* animate */, true /* force */);
         setDark(mDark, false /* animate */, 0 /* delay */);
     }
@@ -778,7 +827,7 @@
     }
 
     public NotificationHeaderView getVisibleNotificationHeader() {
-        NotificationViewWrapper wrapper = getCurrentVisibleWrapper();
+        NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
         return wrapper == null ? null : wrapper.getNotificationHeader();
     }
 
@@ -806,6 +855,7 @@
             mTransformationStartVisibleType = UNDEFINED;
             mVisibleType = calculateVisibleType();
             updateViewVisibilities(mVisibleType);
+            updateBackgroundColor(false);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 7f8f20f..49e4ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -16,8 +16,19 @@
 
 package com.android.systemui.statusbar.notification;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.Color;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.service.notification.StatusBarNotification;
+import android.support.v4.graphics.ColorUtils;
 import android.view.View;
 
+import com.android.systemui.R;
 import com.android.systemui.ViewInvertHelper;
 import com.android.systemui.statusbar.phone.NotificationPanelView;
 
@@ -27,6 +38,11 @@
 public class NotificationCustomViewWrapper extends NotificationViewWrapper {
 
     private final ViewInvertHelper mInvertHelper;
+    private final Paint mGreyPaint = new Paint();
+    private int mBackgroundColor = 0;
+    private static final int CUSTOM_BACKGROUND_TAG = R.id.custom_background_color;
+    private boolean mShouldInvertDark;
+    private boolean mShowingLegacyBackground;
 
     protected NotificationCustomViewWrapper(View view) {
         super(view);
@@ -39,10 +55,46 @@
             return;
         }
         super.setDark(dark, fade, delay);
-        if (fade) {
-            mInvertHelper.fade(dark, delay);
+        if (!mShowingLegacyBackground && mShouldInvertDark) {
+            if (fade) {
+                mInvertHelper.fade(dark, delay);
+            } else {
+                mInvertHelper.update(dark);
+            }
         } else {
-            mInvertHelper.update(dark);
+            mView.setLayerType(dark ? View.LAYER_TYPE_HARDWARE : View.LAYER_TYPE_NONE, null);
+            if (fade) {
+                fadeGrayscale(dark, delay);
+            } else {
+                updateGrayscale(dark);
+            }
+        }
+    }
+
+    protected void fadeGrayscale(final boolean dark, long delay) {
+        startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+            @Override
+            public void onAnimationUpdate(ValueAnimator animation) {
+                updateGrayscaleMatrix((float) animation.getAnimatedValue());
+                mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+                mView.setLayerPaint(mGreyPaint);
+            }
+        }, dark, delay, new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (!dark) {
+                    mView.setLayerType(View.LAYER_TYPE_NONE, null);
+                }
+            }
+        });
+    }
+
+    protected void updateGrayscale(boolean dark) {
+        if (dark) {
+            updateGrayscaleMatrix(1f);
+            mGreyPaint.setColorFilter(
+                    new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+            mView.setLayerPaint(mGreyPaint);
         }
     }
 
@@ -51,4 +103,35 @@
         super.setVisible(visible);
         mView.setAlpha(visible ? 1.0f : 0.0f);
     }
+
+    @Override
+    public void notifyContentUpdated(StatusBarNotification notification) {
+        super.notifyContentUpdated(notification);
+        Drawable background = mView.getBackground();
+        mBackgroundColor = 0;
+        if (background instanceof ColorDrawable) {
+            mBackgroundColor = ((ColorDrawable) background).getColor();
+            mView.setBackground(null);
+            mView.setTag(CUSTOM_BACKGROUND_TAG, mBackgroundColor);
+        } else if (mView.getTag(CUSTOM_BACKGROUND_TAG) != null) {
+            mBackgroundColor = (int) mView.getTag(CUSTOM_BACKGROUND_TAG);
+        }
+        mShouldInvertDark = mBackgroundColor == 0 || isColorLight(mBackgroundColor);
+    }
+
+    private boolean isColorLight(int backgroundColor) {
+        return Color.alpha(backgroundColor) == 0
+                || ColorUtils.calculateLuminance(backgroundColor) > 0.5;
+    }
+
+    @Override
+    public int getCustomBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    @Override
+    public void setShowingLegacyBackground(boolean showing) {
+        super.setShowingLegacyBackground(showing);
+        mShowingLegacyBackground = showing;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 842bd22c..b201d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -47,7 +47,6 @@
  */
 public class NotificationHeaderViewWrapper extends NotificationViewWrapper {
 
-    private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
     private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
             0, PorterDuff.Mode.SRC_ATOP);
     private final int mIconDarkAlpha;
@@ -178,21 +177,6 @@
         }
     }
 
-    protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
-            boolean dark, long delay, Animator.AnimatorListener listener) {
-        float startIntensity = dark ? 0f : 1f;
-        float endIntensity = dark ? 1f : 0f;
-        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
-        animator.addUpdateListener(updateListener);
-        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
-        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        animator.setStartDelay(delay);
-        if (listener != null) {
-            animator.addListener(listener);
-        }
-        animator.start();
-    }
-
     private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
         startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
             @Override
@@ -264,10 +248,6 @@
         mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
     }
 
-    private void updateGrayscaleMatrix(float intensity) {
-        mGrayscaleColorMatrix.setSaturation(1 - intensity);
-    }
-
     private static int interpolateColor(int source, int target, float t) {
         int aSource = Color.alpha(source);
         int rSource = Color.red(source);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
index 4738657..6ef61ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification;
 
+import android.graphics.Color;
 import android.widget.ImageView;
 
 import com.android.internal.util.NotificationColorUtil;
@@ -38,4 +39,12 @@
     public static float interpolate(float start, float end, float amount) {
         return start * (1.0f - amount) + end * amount;
     }
+
+    public static int interpolateColors(int startColor, int endColor, float amount) {
+        return Color.argb(
+                (int) interpolate(Color.alpha(startColor), Color.alpha(endColor), amount),
+                (int) interpolate(Color.red(startColor), Color.red(endColor), amount),
+                (int) interpolate(Color.green(startColor), Color.green(endColor), amount),
+                (int) interpolate(Color.blue(startColor), Color.blue(endColor), amount));
+    }
 }
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 0df0d26..ebff69d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -16,13 +16,19 @@
 
 package com.android.systemui.statusbar.notification;
 
+import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.graphics.ColorMatrix;
 import android.service.notification.StatusBarNotification;
 import android.view.NotificationHeaderView;
 import android.view.View;
 
+import com.android.systemui.Interpolators;
 import com.android.systemui.statusbar.CrossFadeHelper;
+import com.android.systemui.statusbar.NotificationContentView;
 import com.android.systemui.statusbar.TransformableView;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
 
 /**
  * Wraps the actual notification content view; used to implement behaviors which are different for
@@ -30,6 +36,7 @@
  */
 public abstract class NotificationViewWrapper implements TransformableView {
 
+    protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
     protected final View mView;
     protected boolean mDark;
     protected boolean mDarkInitialized = false;
@@ -75,6 +82,26 @@
         mDarkInitialized = false;
     };
 
+
+    protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+            boolean dark, long delay, Animator.AnimatorListener listener) {
+        float startIntensity = dark ? 0f : 1f;
+        float endIntensity = dark ? 1f : 0f;
+        ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+        animator.addUpdateListener(updateListener);
+        animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+        animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+        animator.setStartDelay(delay);
+        if (listener != null) {
+            animator.addListener(listener);
+        }
+        animator.start();
+    }
+
+    protected void updateGrayscaleMatrix(float intensity) {
+        mGrayscaleColorMatrix.setSaturation(1 - intensity);
+    }
+
     /**
      * Update the appearance of the expand button.
      *
@@ -122,4 +149,11 @@
         mView.animate().cancel();
         mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
     }
+
+    public int getCustomBackgroundColor() {
+        return 0;
+    }
+
+    public void setShowingLegacyBackground(boolean showing) {
+    }
 }
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 073a848..7856f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -772,10 +772,21 @@
         ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front);
         View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim);
         mScrimController = SystemUIFactory.getInstance().createScrimController(
-                scrimBehind, scrimInFront, headsUpScrim, mScrimSrcModeEnabled);
+                scrimBehind, scrimInFront, headsUpScrim);
+        if (mScrimSrcModeEnabled) {
+            Runnable runnable = new Runnable() {
+                @Override
+                public void run() {
+                    boolean asSrc = mBackdrop.getVisibility() != View.VISIBLE;
+                    mScrimController.setDrawBehindAsSrc(asSrc);
+                    mStackScroller.setDrawBackgroundAsSrc(asSrc);
+                }
+            };
+            mBackdrop.setOnVisibilityChangedRunnable(runnable);
+            runnable.run();
+        }
         mHeadsUpManager.addListener(mScrimController);
         mStackScroller.setScrimController(mScrimController);
-        mScrimController.setBackDropView(mBackdrop);
         mStatusBarView.setScrimController(mScrimController);
         mDozeScrimController = new DozeScrimController(mScrimController, context);
 
@@ -1466,6 +1477,9 @@
             mStackScroller.removeView(remove);
             mStackScroller.setChildTransferInProgress(false);
         }
+
+        removeNotificationChildren();
+
         for (int i=0; i<toShow.size(); i++) {
             View v = toShow.get(i);
             if (v.getParent() == null) {
@@ -1473,6 +1487,8 @@
             }
         }
 
+        addNotificationChildrenAndSort();
+
         // So after all this work notifications still aren't sorted correctly.
         // Let's do that now by advancing through toShow and mStackScroller in
         // lock-step, making sure mStackScroller matches what we see in toShow.
@@ -1494,9 +1510,6 @@
 
         }
 
-        // lets handle the child notifications now
-        updateNotificationShadeForChildren();
-
         // clear the map again for the next usage
         mTmpChildOrderMap.clear();
 
@@ -1522,34 +1535,7 @@
                 && !ONLY_CORE_APPS);
     }
 
-    private void updateNotificationShadeForChildren() {
-        // First let's remove all children which don't belong in the parents
-        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
-        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
-            View view = mStackScroller.getChildAt(i);
-            if (!(view instanceof ExpandableNotificationRow)) {
-                // We don't care about non-notification views.
-                continue;
-            }
-
-            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
-            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
-            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
-
-            if (children != null) {
-                toRemove.clear();
-                for (ExpandableNotificationRow childRow : children) {
-                    if (orderedChildren == null || !orderedChildren.contains(childRow)) {
-                        toRemove.add(childRow);
-                    }
-                }
-                for (ExpandableNotificationRow remove : toRemove) {
-                    parent.removeChildNotification(remove);
-                    mStackScroller.notifyGroupChildRemoved(remove);
-                }
-            }
-        }
-
+    private void addNotificationChildrenAndSort() {
         // Let's now add all notification children which are missing
         boolean orderChanged = false;
         for (int i = 0; i < mStackScroller.getChildCount(); i++) {
@@ -1580,6 +1566,39 @@
         }
     }
 
+    private void removeNotificationChildren() {
+        // First let's remove all children which don't belong in the parents
+        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+        for (int i = 0; i < mStackScroller.getChildCount(); i++) {
+            View view = mStackScroller.getChildAt(i);
+            if (!(view instanceof ExpandableNotificationRow)) {
+                // We don't care about non-notification views.
+                continue;
+            }
+
+            ExpandableNotificationRow parent = (ExpandableNotificationRow) view;
+            List<ExpandableNotificationRow> children = parent.getNotificationChildren();
+            List<ExpandableNotificationRow> orderedChildren = mTmpChildOrderMap.get(parent);
+
+            if (children != null) {
+                toRemove.clear();
+                for (ExpandableNotificationRow childRow : children) {
+                    if (orderedChildren == null || !orderedChildren.contains(childRow)) {
+                        toRemove.add(childRow);
+                    }
+                }
+                for (ExpandableNotificationRow remove : toRemove) {
+                    parent.removeChildNotification(remove);
+                    if (mNotificationData.get(remove.getStatusBarNotification().getKey()) == null) {
+                        // We only want to add an animation if the view is completely removed
+                        // otherwise it's just a transfer
+                        mStackScroller.notifyGroupChildRemoved(remove);
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     public void addQsTile(ComponentName tile) {
         mQSPanel.getHost().addTile(tile);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index fe76ae7..1a557e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -30,7 +30,6 @@
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.ScrimView;
@@ -75,8 +74,6 @@
     private long mAnimationDelay;
     private Runnable mOnAnimationFinished;
     private final Interpolator mInterpolator = new DecelerateInterpolator();
-    private BackDropView mBackDropView;
-    private boolean mScrimSrcEnabled;
     private boolean mDozing;
     private float mDozeInFrontAlpha;
     private float mDozeBehindAlpha;
@@ -90,14 +87,12 @@
     private boolean mSkipFirstFrame;
     private boolean mDontAnimateBouncerChanges;
 
-    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
-            boolean scrimSrcEnabled) {
+    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
         mHeadsUpScrim = headsUpScrim;
         final Context context = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
-        mScrimSrcEnabled = scrimSrcEnabled;
         updateHeadsUpScrim(false);
     }
 
@@ -378,19 +373,7 @@
         return scrim.getTag(TAG_KEY_ANIM) != null;
     }
 
-    public void setBackDropView(BackDropView backDropView) {
-        mBackDropView = backDropView;
-        mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
-            @Override
-            public void run() {
-                updateScrimBehindDrawingMode();
-            }
-        });
-        updateScrimBehindDrawingMode();
-    }
-
-    private void updateScrimBehindDrawingMode() {
-        boolean asSrc = mBackDropView.getVisibility() != View.VISIBLE && mScrimSrcEnabled;
+    public void setDrawBehindAsSrc(boolean asSrc) {
         mScrimBehind.setDrawAsSrc(asSrc);
     }
 
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 7955733..9d50ab4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -511,7 +511,7 @@
                 break;
             }
             ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight = child.isExpanded()
+            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
                     ? child.getMaxExpandHeight()
                     : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
             maxContentHeight += childHeight;
@@ -532,7 +532,7 @@
         int childCount = mChildren.size();
         for (int i = 0; i < childCount; i++) {
             ExpandableNotificationRow child = mChildren.get(i);
-            float childHeight = child.isExpanded()
+            float childHeight = child.isExpanded(true /* allowOnKeyguard */)
                     ? child.getMaxExpandHeight()
                     : child.getShowingLayout().getMinHeight(true /* likeGroupExpanded */);
             float singleLineHeight = child.getShowingLayout().getMinHeight(
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 aa444f5..cca3746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -18,7 +18,6 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeAnimator;
@@ -324,6 +323,7 @@
             }
         }
     };
+    private PorterDuffXfermode mSrcMode = new PorterDuffXfermode(PorterDuff.Mode.SRC);
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -359,7 +359,6 @@
             mDebugPaint.setStyle(Paint.Style.STROKE);
         }
         mFalsingManager = FalsingManager.getInstance(context);
-        mBackgroundPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
     }
 
     @Override
@@ -427,6 +426,11 @@
                 R.dimen.min_top_overscroll_to_qs);
     }
 
+    public void setDrawBackgroundAsSrc(boolean asSrc) {
+        mBackgroundPaint.setXfermode(asSrc ? mSrcMode : null);
+        invalidate();
+    }
+
     private void notifyHeightChangeListener(ExpandableView view) {
         if (mOnHeightChangedListener != null) {
             mOnHeightChangedListener.onHeightChanged(view, false /* needsAnimation */);
@@ -774,12 +778,15 @@
         if (child instanceof ExpandableNotificationRow) {
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             ExpandableNotificationRow parent = row.getNotificationParent();
-            if (mGearExposedView != null && parent != null
-                    && parent.areChildrenExpanded() && mGearExposedView == parent) {
+            if (parent != null && parent.areChildrenExpanded()
+                    && (mGearExposedView == parent
+                        || (parent.getNotificationChildren().size() == 1
+                                && parent.isClearable()))) {
                 // In this case the group is expanded and showing the gear for the
                 // group, further interaction should apply to the group, not any
-                // child notifications so we use the parent of the child.
-                child = row.getNotificationParent();
+                // child notifications so we use the parent of the child. We also do the same
+                // if we only have a single child.
+                child = parent;
             }
         }
         return child;
@@ -857,7 +864,7 @@
     public boolean canChildBeExpanded(View v) {
         return v instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) v).isExpandable()
-                && !((ExpandableNotificationRow) v).isHeadsUp();
+                && (mIsExpanded || !((ExpandableNotificationRow) v).isPinned());
     }
 
     public void setUserExpandedChild(View v, boolean userExpanded) {
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index f3140d2..65654a8 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -1953,8 +1953,10 @@
     // a notification.
     ACTION_TOUCH_GEAR = 333;
 
+    // Logs that the user has edited the enabled VR listeners.
+    VR_MANAGE_LISTENERS = 334;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
-
   }
 }
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 3b6c62b..a93b4d8 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -39,6 +39,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
@@ -47,7 +48,6 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.SynchronousUserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -465,7 +465,7 @@
             super(handler);
         }
 
-        public void registerContentObserverLocked(int userId) {
+        public void registerContentObserverLocked(@UserIdInt int userId) {
             if (mRegistered && mUserId == userId) {
                 return;
             }
@@ -774,7 +774,16 @@
         }
 
         @Override
+        public void onSwitchUser(@UserIdInt int userHandle) {
+            // Called on the system server's main looper thread.
+            // TODO: Dispatch this to a worker thread as needed.
+            mService.onSwitchUser(userHandle);
+        }
+
+        @Override
         public void onBootPhase(int phase) {
+            // Called on the system server's main looper thread.
+            // TODO: Dispatch this to a worker thread as needed.
             if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                 StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
                         .getService(Context.STATUS_BAR_SERVICE);
@@ -783,12 +792,14 @@
         }
 
         @Override
-        public void onUnlockUser(int userHandle) {
+        public void onUnlockUser(@UserIdInt int userHandle) {
+            // Called on the system server's main looper thread.
+            // TODO: Dispatch this to a worker thread as needed.
             mService.onUnlockUser(userHandle);
         }
     }
 
-    public void onUnlockUser(int userId) {
+    void onUnlockUser(@UserIdInt int userId) {
         synchronized(mMethodMap) {
             final int currentUserId = mSettings.getCurrentUserId();
             if (DEBUG) {
@@ -804,6 +815,12 @@
         }
     }
 
+    void onSwitchUser(@UserIdInt int userId) {
+        synchronized (mMethodMap) {
+            switchUserLocked(userId);
+        }
+    }
+
     public InputMethodManagerService(Context context) {
         mIPackageManager = AppGlobals.getPackageManager();
         mContext = context;
@@ -852,25 +869,6 @@
         mNotificationShown = false;
         int userId = 0;
         try {
-            ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new SynchronousUserSwitchObserver() {
-                        @Override
-                        public void onUserSwitching(int newUserId)
-                                throws RemoteException {
-                            synchronized(mMethodMap) {
-                                switchUserLocked(newUserId);
-                            }
-                        }
-
-                        @Override
-                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        }
-
-                        @Override
-                        public void onForegroundProfileSwitch(int newProfileId) {
-                            // Ignore.
-                        }
-                    });
             userId = ActivityManagerNative.getDefault().getCurrentUser().id;
         } catch (RemoteException e) {
             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 0f9a25b..3f453dc 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -27,9 +27,9 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.annotation.UserIdInt;
 import android.app.ActivityManagerNative;
 import android.app.AppGlobals;
-import android.app.SynchronousUserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -81,9 +81,47 @@
     private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = new HashMap<>();
     private final TextServicesSettings mSettings;
 
-    public void systemRunning() {
-        if (!mSystemReady) {
-            mSystemReady = true;
+    public static final class Lifecycle extends SystemService {
+        private TextServicesManagerService mService;
+
+        public Lifecycle(Context context) {
+            super(context);
+            mService = new TextServicesManagerService(context);
+        }
+
+        @Override
+        public void onStart() {
+            publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
+        }
+
+        @Override
+        public void onSwitchUser(@UserIdInt int userHandle) {
+            // Called on the system server's main looper thread.
+            // TODO: Dispatch this to a worker thread as needed.
+            mService.onSwitchUser(userHandle);
+        }
+
+        @Override
+        public void onBootPhase(int phase) {
+            // Called on the system server's main looper thread.
+            // TODO: Dispatch this to a worker thread as needed.
+            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+                mService.systemRunning();
+            }
+        }
+    }
+
+    void systemRunning() {
+        synchronized (mSpellCheckerMap) {
+            if (!mSystemReady) {
+                mSystemReady = true;
+            }
+        }
+    }
+
+    void onSwitchUser(@UserIdInt int userId) {
+        synchronized (mSpellCheckerMap) {
+            switchUserLocked(userId);
         }
     }
 
@@ -98,24 +136,6 @@
 
         int userId = UserHandle.USER_SYSTEM;
         try {
-            ActivityManagerNative.getDefault().registerUserSwitchObserver(
-                    new SynchronousUserSwitchObserver() {
-                        @Override
-                        public void onUserSwitching(int newUserId) throws RemoteException {
-                            synchronized(mSpellCheckerMap) {
-                                switchUserLocked(newUserId);
-                            }
-                        }
-
-                        @Override
-                        public void onUserSwitchComplete(int newUserId) throws RemoteException {
-                        }
-
-                        @Override
-                        public void onForegroundProfileSwitch(int newProfileId) {
-                            // Ignore.
-                        }
-                    });
             userId = ActivityManagerNative.getDefault().getCurrentUser().id;
         } catch (RemoteException e) {
             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
@@ -128,7 +148,7 @@
         switchUserLocked(userId);
     }
 
-    private void switchUserLocked(int userId) {
+    private void switchUserLocked(@UserIdInt int userId) {
         mSettings.setCurrentUserId(userId);
         updateCurrentProfileIds();
         unbindServiceLocked();
@@ -1011,17 +1031,18 @@
 
     private static class TextServicesSettings {
         private final ContentResolver mResolver;
+        @UserIdInt
         private int mCurrentUserId;
         @GuardedBy("mLock")
         private int[] mCurrentProfileIds = new int[0];
         private Object mLock = new Object();
 
-        public TextServicesSettings(ContentResolver resolver, int userId) {
+        public TextServicesSettings(ContentResolver resolver, @UserIdInt int userId) {
             mResolver = resolver;
             mCurrentUserId = userId;
         }
 
-        public void setCurrentUserId(int userId) {
+        public void setCurrentUserId(@UserIdInt int userId) {
             if (DBG) {
                 Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to "
                         + userId + ", new ime = " + getSelectedSpellChecker());
@@ -1030,13 +1051,37 @@
             mCurrentUserId = userId;
         }
 
+        private void putString(final String key, final String str) {
+            Settings.Secure.putStringForUser(mResolver, key, str, mCurrentUserId);
+        }
+
+        private String getString(final String key) {
+            return Settings.Secure.getStringForUser(mResolver, key, mCurrentUserId);
+        }
+
+        private void putInt(final String key, final int value) {
+            Settings.Secure.putIntForUser(mResolver, key, value, mCurrentUserId);
+        }
+
+        private int getInt(final String key, final int defaultValue) {
+            return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mCurrentUserId);
+        }
+
+        private void putBoolean(final String key, final boolean value) {
+            putInt(key, value ? 1 : 0);
+        }
+
+        private boolean getBoolean(final String key, final boolean defaultValue) {
+            return getInt(key, defaultValue ? 1 : 0) == 1;
+        }
+
         public void setCurrentProfileIds(int[] currentProfileIds) {
             synchronized (mLock) {
                 mCurrentProfileIds = currentProfileIds;
             }
         }
 
-        public boolean isCurrentProfile(int userId) {
+        public boolean isCurrentProfile(@UserIdInt int userId) {
             synchronized (mLock) {
                 if (userId == mCurrentUserId) return true;
                 for (int i = 0; i < mCurrentProfileIds.length; i++) {
@@ -1046,39 +1091,33 @@
             }
         }
 
+        @UserIdInt
         public int getCurrentUserId() {
             return mCurrentUserId;
         }
 
         public void putSelectedSpellChecker(String sciId) {
-            Settings.Secure.putStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER, sciId, mCurrentUserId);
+            putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
         }
 
         public void putSelectedSpellCheckerSubtype(int hashCode) {
-            Settings.Secure.putStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode),
-                    mCurrentUserId);
+            putString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
         }
 
         public void setSpellCheckerEnabled(boolean enabled) {
-            Settings.Secure.putIntForUser(mResolver,
-                    Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0, mCurrentUserId);
+            putBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, enabled);
         }
 
         public String getSelectedSpellChecker() {
-            return Settings.Secure.getStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER, mCurrentUserId);
+            return getString(Settings.Secure.SELECTED_SPELL_CHECKER);
         }
 
         public String getSelectedSpellCheckerSubtype() {
-            return Settings.Secure.getStringForUser(mResolver,
-                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, mCurrentUserId);
+            return getString(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
         }
 
         public boolean isSpellCheckerEnabled() {
-            return Settings.Secure.getIntForUser(mResolver,
-                    Settings.Secure.SPELL_CHECKER_ENABLED, 1, mCurrentUserId) == 1;
+            return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8df5b2c..0317641 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2156,15 +2156,20 @@
             } break;
             case VR_MODE_CHANGE_MSG: {
                 VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-                final boolean vrMode = msg.arg1 != 0;
-                vrService.setVrMode(vrMode);
-
-                if (mInVrMode != vrMode) {
-                    synchronized (ActivityManagerService.this) {
+                final ActivityRecord r = (ActivityRecord) msg.obj;
+                boolean vrMode;
+                ComponentName requestedPackage;
+                int userId;
+                synchronized (ActivityManagerService.this) {
+                    vrMode = r.requestedVrComponent != null;
+                    requestedPackage = r.requestedVrComponent;
+                    userId = r.userId;
+                    if (mInVrMode != vrMode) {
                         mInVrMode = vrMode;
                         mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode);
                     }
                 }
+                vrService.setVrMode(vrMode, requestedPackage, userId);
             } break;
             }
         }
@@ -2937,7 +2942,7 @@
 
     final void applyUpdateVrModeLocked(ActivityRecord r) {
         mHandler.sendMessage(
-                mHandler.obtainMessage(VR_MODE_CHANGE_MSG, (r.isVrActivity) ? 1 : 0, 0));
+                mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
@@ -12030,25 +12035,51 @@
     }
 
     @Override
-    public void setVrMode(IBinder token, boolean enabled) {
+    public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) {
         if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
             throw new UnsupportedOperationException("VR mode not supported on this device!");
         }
 
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+        ActivityRecord r;
+        synchronized (this) {
+            r = ActivityRecord.isInStackLocked(token);
+        }
+
+        if (r == null) {
+            throw new IllegalArgumentException();
+        }
+
+        int err;
+        if ((err = vrService.hasVrPackage(packageName, r.userId)) !=
+                VrManagerInternal.NO_ERROR) {
+            return err;
+        }
+
         synchronized(this) {
-            final ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            if (r == null) {
-                throw new IllegalArgumentException();
-            }
-            r.isVrActivity = enabled;
+            r.requestedVrComponent = (enabled) ? packageName : null;
 
             // Update associated state if this activity is currently focused
             if (r == mFocusedActivity) {
                 applyUpdateVrModeLocked(r);
             }
+            return 0;
         }
     }
 
+    @Override
+    public boolean isVrModePackageEnabled(ComponentName packageName) {
+        if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) {
+            throw new UnsupportedOperationException("VR mode not supported on this device!");
+        }
+
+        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
+
+        return vrService.hasVrPackage(packageName, UserHandle.getCallingUserId()) ==
+                VrManagerInternal.NO_ERROR;
+    }
+
     public boolean isTopActivityImmersive() {
         enforceNotIsolatedCaller("startActivity");
         synchronized (this) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e430dad..2ea5d15 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -123,7 +123,7 @@
     final boolean stateNotNeeded; // As per ActivityInfo.flags
     boolean fullscreen; // covers the full screen?
     final boolean noDisplay;  // activity is not displayed?
-    final boolean componentSpecified;  // did caller specifiy an explicit component?
+    final boolean componentSpecified;  // did caller specify an explicit component?
     final boolean rootVoiceInteraction;  // was this the root activity of a voice interaction?
 
     static final int APPLICATION_ACTIVITY_TYPE = 0;
@@ -190,7 +190,7 @@
     boolean forceNewConfig; // force re-create with new config next time
     int launchCount;        // count of launches since last state
     long lastLaunchTime;    // time of last launch of this activity
-    boolean isVrActivity;   // is the activity running in VR mode?
+    ComponentName requestedVrComponent; // the requested component for handling VR mode.
     ArrayList<ActivityContainer> mChildContainers = new ArrayList<>();
 
     String stringName;      // for caching of toString().
@@ -354,7 +354,11 @@
                 pw.print(" forceNewConfig="); pw.println(forceNewConfig);
         pw.print(prefix); pw.print("mActivityType=");
                 pw.println(activityTypeToString(mActivityType));
-        pw.print(prefix); pw.print("vrMode="); pw.println(isVrActivity);
+        if (requestedVrComponent != null) {
+            pw.print(prefix);
+            pw.print("requestedVrComponent=");
+            pw.println(requestedVrComponent);
+        }
         if (displayStartTime != 0 || startTime != 0) {
             pw.print(prefix); pw.print("displayStartTime=");
                     if (displayStartTime == 0) pw.print("0");
@@ -718,7 +722,6 @@
             }
 
             immersive = (aInfo.flags & ActivityInfo.FLAG_IMMERSIVE) != 0;
-            isVrActivity = (aInfo.flags & ActivityInfo.FLAG_ENABLE_VR_MODE) != 0;
         } else {
             realActivity = null;
             taskAffinity = null;
@@ -730,7 +733,6 @@
             noDisplay = false;
             mActivityType = APPLICATION_ACTIVITY_TYPE;
             immersive = false;
-            isVrActivity = false;
         }
     }
 
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f2a9c2c..d919737 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -58,7 +58,7 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.AudioPort;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioRoutesInfo;
 import android.media.IAudioFocusDispatcher;
 import android.media.IAudioRoutesObserver;
@@ -6287,8 +6287,8 @@
         mRecordMonitor.unregisterRecordingCallback(rcdb);
     }
 
-    public AudioRecordConfiguration[] getActiveRecordConfigurations() {
-        return mRecordMonitor.getActiveRecordConfigurations();
+    public AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
+        return mRecordMonitor.getActiveRecordingConfigurations();
     }
 
     //======================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 86dcd0f..7a085e1 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -18,7 +18,7 @@
 
 import android.media.AudioFormat;
 import android.media.AudioManager;
-import android.media.AudioRecordConfiguration;
+import android.media.AudioRecordingConfiguration;
 import android.media.AudioSystem;
 import android.media.IRecordingConfigDispatcher;
 import android.media.MediaRecorder;
@@ -39,8 +39,8 @@
 
     private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
 
-    private HashMap<Integer, AudioRecordConfiguration> mRecordConfigs =
-            new HashMap<Integer, AudioRecordConfiguration>();
+    private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs =
+            new HashMap<Integer, AudioRecordingConfiguration>();
 
     RecordingActivityMonitor() {
         RecMonitorClient.sMonitor = this;
@@ -54,7 +54,7 @@
         if (MediaRecorder.isSystemOnlyAudioSource(source)) {
             return;
         }
-        final AudioRecordConfiguration[] configs =
+        final AudioRecordingConfiguration[] configs =
                 updateSnapshot(event, session, source, recordingInfo);
         if (configs != null){
             synchronized(mClients) {
@@ -104,9 +104,9 @@
         }
     }
 
-    AudioRecordConfiguration[] getActiveRecordConfigurations() {
+    AudioRecordingConfiguration[] getActiveRecordingConfigurations() {
         synchronized(mRecordConfigs) {
-            return mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+            return mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
         }
     }
 
@@ -121,10 +121,10 @@
      * @return null if the list of active recording sessions has not been modified, an array
      *     with the current active configurations otherwise.
      */
-    private AudioRecordConfiguration[] updateSnapshot(int event, int session, int source,
+    private AudioRecordingConfiguration[] updateSnapshot(int event, int session, int source,
             int[] recordingInfo) {
         final boolean configChanged;
-        final AudioRecordConfiguration[] configs;
+        final AudioRecordingConfiguration[] configs;
         synchronized(mRecordConfigs) {
             switch (event) {
             case AudioManager.RECORD_CONFIG_EVENT_STOP:
@@ -147,8 +147,8 @@
                 final int patchHandle = recordingInfo[6];
                 final Integer sessionKey = new Integer(session);
                 if (mRecordConfigs.containsKey(sessionKey)) {
-                    final AudioRecordConfiguration updatedConfig =
-                            new AudioRecordConfiguration(session, source,
+                    final AudioRecordingConfiguration updatedConfig =
+                            new AudioRecordingConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle);
                     if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
                         configChanged = false;
@@ -160,7 +160,7 @@
                     }
                 } else {
                     mRecordConfigs.put(sessionKey,
-                            new AudioRecordConfiguration(session, source,
+                            new AudioRecordingConfiguration(session, source,
                                     clientFormat, deviceFormat, patchHandle));
                     configChanged = true;
                 }
@@ -171,7 +171,7 @@
                 configChanged = false;
             }
             if (configChanged) {
-                configs = mRecordConfigs.values().toArray(new AudioRecordConfiguration[0]);
+                configs = mRecordConfigs.values().toArray(new AudioRecordingConfiguration[0]);
             } else {
                 configs = null;
             }
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 0d6e3e5..17313b6 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -301,7 +302,9 @@
      * */
     public void registerGuestService(ManagedServiceInfo guest) {
         checkNotNull(guest.service);
-        checkType(guest.service);
+        if (!checkType(guest.service)) {
+            throw new IllegalArgumentException();
+        }
         if (registerServiceImpl(guest) != null) {
             onServiceAdded(guest);
         }
@@ -920,9 +923,9 @@
 
     public static class UserProfiles {
         // Profiles of the current user.
-        private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<UserInfo>();
+        private final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
 
-        public void updateCache(Context context) {
+        public void updateCache(@NonNull Context context) {
             UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
             if (userManager != null) {
                 int currentUserId = ActivityManager.getCurrentUser();
@@ -954,12 +957,12 @@
         }
     }
 
-    protected static class Config {
-        String caption;
-        String serviceInterface;
-        String secureSettingName;
-        String bindPermission;
-        String settingsAction;
-        int clientLabel;
+    public static class Config {
+        public String caption;
+        public String serviceInterface;
+        public String secureSettingName;
+        public String bindPermission;
+        public String settingsAction;
+        public int clientLabel;
     }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 3855579..24ffe1f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,21 +16,21 @@
 
 package com.android.server.notification;
 
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CLICK;
-import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_ERROR;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_OPTIMIZATION;
-import static android.service.notification.NotificationAssistantService.REASON_GROUP_SUMMARY_CANCELED;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL;
-import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
-import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_SUSPENDED;
-import static android.service.notification.NotificationAssistantService.REASON_PROFILE_TURNED_OFF;
-import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_APP_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_CLICK;
+import static android.service.notification.NotificationRankerService.REASON_DELEGATE_ERROR;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_OPTIMIZATION;
+import static android.service.notification.NotificationRankerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL;
+import static android.service.notification.NotificationRankerService.REASON_LISTENER_CANCEL_ALL;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_BANNED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationRankerService.REASON_PACKAGE_SUSPENDED;
+import static android.service.notification.NotificationRankerService.REASON_PROFILE_TURNED_OFF;
+import static android.service.notification.NotificationRankerService.REASON_USER_STOPPED;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
@@ -100,7 +100,7 @@
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
 import android.service.notification.IStatusBarNotificationHolder;
-import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationRankerService;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.StatusBarNotification;
@@ -128,10 +128,9 @@
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.ManagedServices.ManagedServiceInfo;
-import com.android.server.notification.ManagedServices.UserProfiles;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.vr.VrManagerInternal;
-import com.android.server.vr.VrStateListener;
+import com.android.server.notification.ManagedServices.UserProfiles;
 
 import libcore.io.IoUtils;
 
@@ -220,14 +219,11 @@
     StatusBarManagerInternal mStatusBar;
     Vibrator mVibrator;
     private VrManagerInternal mVrManagerInternal;
-    private final NotificationVrListener mVrListener = new NotificationVrListener();
 
     final IBinder mForegroundToken = new Binder();
     private WorkerHandler mHandler;
     private final HandlerThread mRankingThread = new HandlerThread("ranker",
             Process.THREAD_PRIORITY_BACKGROUND);
-    private final HandlerThread mAssistantThread = new HandlerThread("assistant",
-            Process.THREAD_PRIORITY_BACKGROUND);
 
     private Light mNotificationLight;
     Light mAttentionLight;
@@ -293,14 +289,13 @@
 
     private final UserProfiles mUserProfiles = new UserProfiles();
     private NotificationListeners mListeners;
-    private NotificationAssistant mAssistant;
+    private NotificationRanker mRankerServices;
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
 
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
     private RankingHandler mRankingHandler;
-    private Handler mAssistantHandler;
 
     private static class Archive {
         final int mBufferSize;
@@ -758,7 +753,7 @@
                     }
                 }
                 mListeners.onPackagesChanged(queryReplace, pkgList);
-                mAssistant.onPackagesChanged(queryReplace, pkgList);
+                mRankerServices.onPackagesChanged(queryReplace, pkgList);
                 mConditionProviders.onPackagesChanged(queryReplace, pkgList);
                 mRankingHelper.onPackagesChanged(queryReplace, pkgList);
             }
@@ -807,7 +802,7 @@
                 // Refresh managed services
                 mConditionProviders.onUserSwitched(user);
                 mListeners.onUserSwitched(user);
-                mAssistant.onUserSwitched(user);
+                mRankerServices.onUserSwitched(user);
                 mZenModeHelper.onUserSwitched(user);
             } else if (action.equals(Intent.ACTION_USER_ADDED)) {
                 mUserProfiles.updateCache(context);
@@ -818,7 +813,7 @@
                 final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                 mConditionProviders.onUserUnlocked(user);
                 mListeners.onUserUnlocked(user);
-                mAssistant.onUserUnlocked(user);
+                mRankerServices.onUserUnlocked(user);
                 mZenModeHelper.onUserUnlocked(user);
             }
         }
@@ -856,14 +851,6 @@
         }
     }
 
-    private final class NotificationVrListener extends VrStateListener {
-        @Override
-        public void onVrStateChanged(final boolean enabled) {
-            mListeners.setCategoryState(NotificationListenerService.CATEGORY_VR_NOTIFICATIONS,
-                enabled);
-        }
-    }
-
     private SettingsObserver mSettingsObserver;
     private ZenModeHelper mZenModeHelper;
 
@@ -902,7 +889,6 @@
 
         mHandler = new WorkerHandler();
         mRankingThread.start();
-        mAssistantThread.start();
         String[] extractorNames;
         try {
             extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -911,7 +897,6 @@
         }
         mUsageStats = new NotificationUsageStats(getContext());
         mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
-        mAssistantHandler = new Handler(mAssistantThread.getLooper());
         mRankingHelper = new RankingHelper(getContext(),
                 mRankingHandler,
                 mUsageStats,
@@ -947,7 +932,7 @@
         importOldBlockDb();
 
         mListeners = new NotificationListeners();
-        mAssistant = new NotificationAssistant();
+        mRankerServices = new NotificationRanker();
         mStatusBar = getLocalService(StatusBarManagerInternal.class);
         mStatusBar.setNotificationDelegate(mNotificationDelegate);
 
@@ -1064,14 +1049,13 @@
             mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
             mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
             mVrManagerInternal = getLocalService(VrManagerInternal.class);
-            mVrManagerInternal.registerListener(mVrListener);
             mZenModeHelper.onSystemReady();
         } else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
             // This observer will force an update when observe is called, causing us to
             // bind to listener services.
             mSettingsObserver.observe();
             mListeners.onBootPhaseAppsCanStart();
-            mAssistant.onBootPhaseAppsCanStart();
+            mRankerServices.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
         }
     }
@@ -1426,17 +1410,30 @@
          */
         @Override
         public void registerListener(final INotificationListener listener,
-                final ComponentName component, final int userid) {
+                final ComponentName component, final int userid, boolean asRanker) {
             enforceSystemOrSystemUI("INotificationManager.registerListener");
-            mListeners.registerService(listener, component, userid);
+            if (asRanker) {
+                mRankerServices.registerService(listener, component, userid);
+            } else {
+                mListeners.registerService(listener, component, userid);
+            }
         }
 
         /**
          * Remove a listener binder directly
          */
         @Override
-        public void unregisterListener(INotificationListener listener, int userid) {
-            mListeners.unregisterService(listener, userid);
+        public void unregisterListener(INotificationListener token, int userid) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                if(mRankerServices.checkServiceTokenLocked(token) != null) {
+                    mRankerServices.unregisterService(token, userid);
+                } else {
+                    mListeners.unregisterService(token, userid);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
 
         /**
@@ -1489,8 +1486,8 @@
             checkCallerIsSystemOrSameApp(component.getPackageName());
             long identity = Binder.clearCallingIdentity();
             try {
-                ManagedServices manager = mAssistant.isComponentEnabledForCurrentProfiles(component)
-                        ? mAssistant
+                ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
+                        ? mRankerServices
                         : mListeners;
                 manager.setComponentState(component, true);
             } finally {
@@ -1983,7 +1980,7 @@
         }
 
         @Override
-        public void setImportanceFromAssistant(INotificationListener token, String key,
+        public void setImportanceFromRankerService(INotificationListener token, String key,
                 int importance, CharSequence explanation) throws RemoteException {
             if (importance == IMPORTANCE_NONE) {
                 throw new IllegalArgumentException("blocking not allowed: key=" + key);
@@ -1991,7 +1988,7 @@
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
-                    mAssistant.checkServiceTokenLocked(token);
+                    mRankerServices.checkServiceTokenLocked(token);
                     NotificationRecord n = mNotificationsByKey.get(key);
                     n.setImportance(importance, explanation);
                     mRankingHandler.requestSort();
@@ -2141,8 +2138,8 @@
                     pw.print(listener.component);
                 }
                 pw.println(')');
-                pw.println("\n  Notification assistant:");
-                mAssistant.dump(pw, filter);
+                pw.println("\n  Notification ranker services:");
+                mRankerServices.dump(pw, filter);
             }
             pw.println("\n  Policy access:");
             pw.print("    mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2369,9 +2366,9 @@
                     }
                 }
 
-                // tell the assistant about the notification
-                if (mAssistant.isEnabled()) {
-                    mAssistant.onNotificationEnqueued(r);
+                // tell the ranker service about the notification
+                if (mRankerServices.isEnabled()) {
+                    mRankerServices.onNotificationEnqueued(r);
                     // TODO delay the code below here for 100ms or until there is an answer
                 }
 
@@ -3488,21 +3485,21 @@
         }
     }
 
-    public class NotificationAssistant extends ManagedServices {
+    public class NotificationRanker extends ManagedServices {
 
-        public NotificationAssistant() {
+        public NotificationRanker() {
             super(getContext(), mHandler, mNotificationList, mUserProfiles);
         }
 
         @Override
         protected Config getConfig() {
             Config c = new Config();
-            c.caption = "notification assistant";
-            c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
-            c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
-            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+            c.caption = "notification ranker service";
+            c.serviceInterface = NotificationRankerService.SERVICE_INTERFACE;
+            c.secureSettingName = null;
+            c.bindPermission = Manifest.permission.BIND_NOTIFICATION_RANKER_SERVICE;
             c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
-            c.clientLabel = R.string.notification_assistant_binding_label;
+            c.clientLabel = R.string.notification_ranker_binding_label;
             return c;
         }
 
@@ -3530,10 +3527,10 @@
             final StatusBarNotification sbn = r.sbn;
             TrimCache trimCache = new TrimCache(sbn);
 
-            // mServices is the list inside ManagedServices of all the assistants,
+            // mServices is the list inside ManagedServices of all the rankers,
             // There should be only one, but it's a list, so while we enforce
             // singularity elsewhere, we keep it general here, to avoid surprises.
-            for (final ManagedServiceInfo info : NotificationAssistant.this.mServices) {
+            for (final ManagedServiceInfo info : NotificationRanker.this.mServices) {
                 boolean sbnVisible = isVisibleToListener(sbn, info);
                 if (!sbnVisible) {
                     continue;
@@ -3542,7 +3539,7 @@
                 final int importance = r.getImportance();
                 final boolean fromUser = r.isImportanceFromUser();
                 final StatusBarNotification sbnToPost =  trimCache.ForListener(info);
-                mAssistantHandler.post(new Runnable() {
+                mHandler.post(new Runnable() {
                     @Override
                     public void run() {
                         notifyEnqueued(info, sbnToPost, importance, fromUser);
@@ -3553,12 +3550,12 @@
 
         private void notifyEnqueued(final ManagedServiceInfo info,
                 final StatusBarNotification sbn, int importance, boolean fromUser) {
-            final INotificationListener assistant = (INotificationListener) info.service;
+            final INotificationListener ranker = (INotificationListener) info.service;
             StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);
             try {
-                assistant.onNotificationEnqueued(sbnHolder, importance, fromUser);
+                ranker.onNotificationEnqueued(sbnHolder, importance, fromUser);
             } catch (RemoteException ex) {
-                Log.e(TAG, "unable to notify assistant (enqueued): " + assistant, ex);
+                Log.e(TAG, "unable to notify ranker (enqueued): " + ranker, ex);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 9ec6da0..27c8293 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -31,6 +31,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.UserHandle;
+import android.print.PrintManager;
 import android.provider.CalendarContract;
 import android.provider.ContactsContract;
 import android.provider.MediaStore;
@@ -579,7 +580,7 @@
 
             // Print Spooler
             PackageParser.Package printSpoolerPackage = getSystemPackageLPr(
-                    "com.android.printspooler");
+                    PrintManager.PRINT_SPOOLER_PACKAGE_NAME);
             if (printSpoolerPackage != null
                     && doesPackageSupportRuntimePermissions(printSpoolerPackage)) {
                 grantRuntimePermissionsLPw(printSpoolerPackage, LOCATION_PERMISSIONS, true, userId);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 93dcc72..206a143 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -166,6 +166,10 @@
         mInstaller.execute("rmpackagedir", packageDir);
     }
 
+    public void rmProfiles(String pkgName) throws InstallerException {
+        mInstaller.execute("rmprofiles", pkgName);
+    }
+
     public void createUserConfig(int userid) throws InstallerException {
         mInstaller.execute("mkuserconfig", userid);
     }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index c9613b4..561682c 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -191,7 +191,6 @@
                         throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
                 }
 
-
                 Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                         + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                         + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d4c9e5..e180e050 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4443,6 +4443,13 @@
     }
 
     @Override
+    public List<String> getAllPackages() {
+        synchronized (mPackages) {
+            return new ArrayList<String>(mPackages.keySet());
+        }
+    }
+
+    @Override
     public String[] getPackagesForUid(int uid) {
         uid = UserHandle.getAppId(uid);
         // reader
@@ -15147,6 +15154,16 @@
     }
 
     @Override
+    public void clearApplicationProfileData(String packageName) {
+        enforceSystemOrRoot("Only the system can clear all profile data");
+        try {
+            mInstaller.rmProfiles(packageName);
+        } catch (InstallerException ex) {
+            Log.e(TAG, "Could not clear profile data of package " + packageName);
+        }
+    }
+
+    @Override
     public void clearApplicationUserData(final String packageName,
             final IPackageDataObserver observer, final int userId) {
         mContext.enforceCallingOrSelfPermission(
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index ccbd823..28d34a8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -233,17 +233,30 @@
         boolean useJitProfiles = false;
         boolean extractOnly = false;
         boolean forceCompilation = false;
+        boolean allPackages = false;
+        boolean clearProfileData = false;
         String compilationMode = "default";
 
         String opt;
         while ((opt = getNextOption()) != null) {
             switch (opt) {
-                case "-m":
-                    compilationMode = getNextArgRequired();
+                case "-a":
+                    allPackages = true;
+                    break;
+                case "-c":
+                    clearProfileData = true;
                     break;
                 case "-f":
                     forceCompilation = true;
                     break;
+                case "-m":
+                    compilationMode = getNextArgRequired();
+                    break;
+                case "--reset":
+                    forceCompilation = true;
+                    clearProfileData = true;
+                    compilationMode = "extract";
+                    break;
                 default:
                     pw.println("Error: Unknown option: " + opt);
                     return 1;
@@ -255,7 +268,7 @@
                 useJitProfiles = SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false);
                 extractOnly = false;
                 break;
-            case "all":
+            case "full":
                 useJitProfiles = false;
                 extractOnly = false;
                 break;
@@ -272,19 +285,49 @@
                 return 1;
         }
 
-        String packageName = getNextArg();
-        if (packageName == null) {
-            pw.println("Error: package name not specified");
-            return 1;
+        List<String> packageNames = null;
+        if (allPackages) {
+            packageNames = mInterface.getAllPackages();
+        } else {
+            String packageName = getNextArg();
+            if (packageName == null) {
+                pw.println("Error: package name not specified");
+                return 1;
+            }
+            packageNames = Collections.singletonList(packageName);
         }
 
-        boolean success = mInterface.performDexOpt(packageName, null /* instructionSet */,
-                useJitProfiles, extractOnly, forceCompilation);
-        if (success) {
+        List<String> failedPackages = new ArrayList<>();
+        for (String packageName : packageNames) {
+            if (clearProfileData) {
+                mInterface.clearApplicationProfileData(packageName);
+            }
+
+            boolean result = mInterface.performDexOpt(packageName, null /* instructionSet */,
+                        useJitProfiles, extractOnly, forceCompilation);
+            if (!result) {
+                failedPackages.add(packageName);
+            }
+        }
+
+        if (failedPackages.isEmpty()) {
             pw.println("Success");
             return 0;
+        } else if (failedPackages.size() == 1) {
+            pw.println("Failure: package " + failedPackages.get(0) + " could not be compiled");
+            return 1;
         } else {
-            pw.println("Failure: package " + packageName + " could not be compiled");
+            pw.print("Failure: the following packages could not be compiled: ");
+            boolean is_first = true;
+            for (String packageName : failedPackages) {
+                if (is_first) {
+                    is_first = false;
+                } else {
+                    pw.print(", ");
+                }
+                pw.print(packageName);
+            }
+            pw.println();
             return 1;
         }
     }
@@ -1135,12 +1178,17 @@
         pw.println("  help");
         pw.println("    Print this help text.");
         pw.println("");
-        pw.println("  compile [-m MODE] [-f] TARGET-PACKAGE");
-        pw.println("    Trigger compilation of TARGET-PACKAGE.");
+        pw.println("  compile [-m MODE] [-f] [-c] [--reset] (-a | TARGET-PACKAGE)");
+        pw.println("    Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
         pw.println("    Options:");
-        pw.println("      -m: select compilation mode");
-        pw.println("          MODE can be one of \"default\", \"all\", \"profile\", and \"extract\"");
+        pw.println("      -a: compile all packages");
+        pw.println("      -c: clear profile data before compiling");
         pw.println("      -f: force compilation even if not needed");
+        pw.println("      -m: select compilation mode");
+        pw.println("          MODE can be one of \"default\", \"full\", \"profile\"," +
+                   " and \"extract\"");
+        pw.println("      --reset: restore package to its post-install state");
+        pw.println("          shorthand for \"-c -f -m extract\"");
         pw.println("  list features");
         pw.println("    Prints all features of the system.");
         pw.println("  list instrumentation [-f] [TARGET-PACKAGE]");
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 6218c4e..91d8671 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -50,6 +50,7 @@
 import android.os.WorkSource;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
+import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
 import android.util.EventLog;
 import android.util.Slog;
@@ -57,6 +58,7 @@
 import android.util.TimeUtils;
 import android.view.Display;
 import android.view.WindowManagerPolicy;
+
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
@@ -537,6 +539,8 @@
                     }
                 }
                 mBootCompletedRunnables = null;
+
+                incrementBootCount();
             }
         }
     }
@@ -772,7 +776,7 @@
         }
     }
 
-    void updateLowPowerModeLocked() {
+    private void updateLowPowerModeLocked() {
         if (mIsPowered && mLowPowerModeSetting) {
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateLowPowerModeLocked: powered, turning setting off");
@@ -2912,6 +2916,20 @@
         return suspendBlocker;
     }
 
+    private void incrementBootCount() {
+        synchronized (mLock) {
+            int count;
+            try {
+                count = Settings.Global.getInt(
+                        getContext().getContentResolver(), Settings.Global.BOOT_COUNT);
+            } catch (SettingNotFoundException e) {
+                count = 0;
+            }
+            Settings.Global.putInt(
+                    getContext().getContentResolver(), Settings.Global.BOOT_COUNT, count + 1);
+        }
+    }
+
     private static WorkSource copyWorkSource(WorkSource workSource) {
         return workSource != null ? new WorkSource(workSource) : null;
     }
diff --git a/services/core/java/com/android/server/utils/ManagedApplicationService.java b/services/core/java/com/android/server/utils/ManagedApplicationService.java
new file mode 100644
index 0000000..a645701
--- /dev/null
+++ b/services/core/java/com/android/server/utils/ManagedApplicationService.java
@@ -0,0 +1,220 @@
+/**
+ * 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.utils;
+
+import android.annotation.NonNull;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IInterface;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Manages the lifecycle of an application-provided service bound from system server.
+ *
+ * @hide
+ */
+public class ManagedApplicationService {
+    private final String TAG = getClass().getSimpleName();
+
+    private final Context mContext;
+    private final int mUserId;
+    private final ComponentName mComponent;
+    private final int mClientLabel;
+    private final String mSettingsAction;
+    private final BinderChecker mChecker;
+
+    private final DeathRecipient mDeathRecipient = new DeathRecipient() {
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mBoundInterface = null;
+            }
+        }
+    };
+
+    private final Object mLock = new Object();
+
+    // State protected by mLock
+    private ServiceConnection mPendingConnection;
+    private ServiceConnection mConnection;
+    private IInterface mBoundInterface;
+
+
+    private ManagedApplicationService(final Context context, final ComponentName component,
+            final int userId, int clientLabel, String settingsAction,
+            BinderChecker binderChecker) {
+        mContext = context;
+        mComponent = component;
+        mUserId = userId;
+        mClientLabel = clientLabel;
+        mSettingsAction = settingsAction;
+        mChecker = binderChecker;
+    }
+
+    /**
+     * Implement to validate returned IBinder instance.
+     */
+    public interface BinderChecker {
+        IInterface asInterface(IBinder binder);
+        boolean checkType(IInterface service);
+    }
+
+    /**
+     * Create a new ManagedApplicationService object but do not yet bind to the user service.
+     *
+     * @param context a Context to use for binding the application service.
+     * @param component the {@link ComponentName} of the application service to bind.
+     * @param userId the user ID of user to bind the application service as.
+     * @param clientLabel the resource ID of a label displayed to the user indicating the
+     *      binding service.
+     * @param settingsAction an action that can be used to open the Settings UI to enable/disable
+     *      binding to these services.
+     * @param binderChecker an interface used to validate the returned binder object.
+     * @return a ManagedApplicationService instance.
+     */
+    public static ManagedApplicationService build(@NonNull final Context context,
+        @NonNull final ComponentName component, final int userId, @NonNull int clientLabel,
+        @NonNull String settingsAction, @NonNull BinderChecker binderChecker) {
+        return new ManagedApplicationService(context, component, userId, clientLabel,
+            settingsAction, binderChecker);
+    }
+
+    /**
+     * @return the user ID of the user that owns the bound service.
+     */
+    public int getUserId() {
+        return mUserId;
+    }
+
+    /**
+     * @return the component of the bound service.
+     */
+    public ComponentName getComponent() {
+        return mComponent;
+    }
+
+    /**
+     * Asynchronously unbind from the application service if the bound service component and user
+     * does not match the given signature.
+     *
+     * @param componentName the component that must match.
+     * @param userId the user ID that must match.
+     * @return {@code true} if not matching.
+     */
+    public boolean disconnectIfNotMatching(final ComponentName componentName, final int userId) {
+        if (matches(componentName, userId)) {
+            return false;
+        }
+        disconnect();
+        return true;
+    }
+
+    /**
+     * Asynchronously unbind from the application service if bound.
+     */
+    public void disconnect() {
+        synchronized (mLock) {
+            // Wipe out pending connections
+            mPendingConnection = null;
+
+            // Unbind existing connection, if it exists
+            if (mConnection != null) {
+                mContext.unbindService(mConnection);
+                mConnection = null;
+            }
+
+            mBoundInterface = null;
+        }
+    }
+
+    /**
+     * Asynchronously bind to the application service if not bound.
+     */
+    public void connect() {
+        synchronized (mLock) {
+            if (mConnection != null || mPendingConnection != null) {
+                // We're already connected or are trying to connect
+                return;
+            }
+
+            final PendingIntent pendingIntent = PendingIntent.getActivity(
+                    mContext, 0, new Intent(mSettingsAction), 0);
+            final Intent intent = new Intent().setComponent(mComponent).
+                    putExtra(Intent.EXTRA_CLIENT_LABEL, mClientLabel).
+                    putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent);
+
+            final ServiceConnection serviceConnection = new ServiceConnection() {
+                @Override
+                public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+                    synchronized (mLock) {
+                        if (mPendingConnection == this) {
+                            // No longer pending, remove from pending connection
+                            mPendingConnection = null;
+                            mConnection = this;
+                        } else {
+                            // Service connection wasn't pending, must have been disconnected
+                            mContext.unbindService(this);
+                        }
+
+                        try {
+                            iBinder.linkToDeath(mDeathRecipient, 0);
+                            mBoundInterface = mChecker.asInterface(iBinder);
+                            if (!mChecker.checkType(mBoundInterface)) {
+                                // Received an invalid binder, disconnect
+                                mContext.unbindService(this);
+                                mBoundInterface = null;
+                            }
+                        } catch (RemoteException e) {
+                            // DOA
+                            Slog.w(TAG, "Unable to bind service: " + intent, e);
+                            mBoundInterface = null;
+                        }
+                    }
+                }
+
+                @Override
+                public void onServiceDisconnected(ComponentName componentName) {
+                    Slog.w(TAG, "Service disconnected: " + intent);
+                }
+            };
+
+            mPendingConnection = serviceConnection;
+
+            try {
+                if (!mContext.bindServiceAsUser(intent, serviceConnection,
+                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+                        new UserHandle(mUserId))) {
+                    Slog.w(TAG, "Unable to bind service: " + intent);
+                }
+            } catch (SecurityException e) {
+                Slog.w(TAG, "Unable to bind service: " + intent, e);
+            }
+        }
+    }
+
+    private boolean matches(final ComponentName component, final int userId) {
+        return Objects.equals(mComponent, component) && mUserId == userId;
+    }
+}
diff --git a/services/core/java/com/android/server/vr/EnabledComponentsObserver.java b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
new file mode 100644
index 0000000..1363fb9
--- /dev/null
+++ b/services/core/java/com/android/server/vr/EnabledComponentsObserver.java
@@ -0,0 +1,282 @@
+/**
+ * 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.vr;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.server.vr.SettingsObserver.SettingChangeListener;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Detects changes in packages, settings, and current users that may affect whether components
+ * implementing a given service can be run.
+ *
+ * @hide
+ */
+public class EnabledComponentsObserver implements SettingChangeListener {
+
+    private static final String TAG = EnabledComponentsObserver.class.getSimpleName();
+    private static final String ENABLED_SERVICES_SEPARATOR = ":";
+
+    public static final int NO_ERROR = 0;
+    public static final int DISABLED = -1;
+    public static final int NOT_INSTALLED = -2;
+
+    private final Object mLock;
+    private final Context mContext;
+    private final String mSettingName;
+    private final String mServiceName;
+    private final String mServicePermission;
+    private final SparseArray<ArraySet<ComponentName>> mInstalledSet = new SparseArray<>();
+    private final SparseArray<ArraySet<ComponentName>> mEnabledSet = new SparseArray<>();
+    private final Set<EnabledComponentChangeListener> mEnabledComponentListeners = new ArraySet<>();
+
+    /**
+     * Implement this to receive callbacks when relevant changes to the allowed components occur.
+     */
+    public interface EnabledComponentChangeListener {
+
+        /**
+         * Called when a change in the allowed components occurs.
+         */
+        void onEnabledComponentChanged();
+    }
+
+    private EnabledComponentsObserver(@NonNull Context context, @NonNull String settingName,
+            @NonNull String servicePermission, @NonNull String serviceName, @NonNull Object lock,
+            @NonNull Collection<EnabledComponentChangeListener> listeners) {
+        mLock = lock;
+        mContext = context;
+        mSettingName = settingName;
+        mServiceName = serviceName;
+        mServicePermission = servicePermission;
+        mEnabledComponentListeners.addAll(listeners);
+    }
+
+    /**
+     * Create a EnabledComponentObserver instance.
+     *
+     * @param context the context to query for changes.
+     * @param handler a handler to receive lifecycle events from system services on.
+     * @param settingName the name of a setting to monitor for a list of enabled components.
+     * @param looper a {@link Looper} to use for receiving package callbacks.
+     * @param servicePermission the permission required by the components to be bound.
+     * @param serviceName the intent action implemented by the tracked components.
+     * @param lock a lock object used to guard instance state in all callbacks and method calls.
+     * @return an EnableComponentObserver instance.
+     */
+    public static EnabledComponentsObserver build(@NonNull Context context,
+            @NonNull Handler handler, @NonNull String settingName, @NonNull Looper looper,
+            @NonNull String servicePermission, @NonNull String serviceName,
+            @NonNull final Object lock,
+            @NonNull Collection<EnabledComponentChangeListener> listeners) {
+
+        SettingsObserver s = SettingsObserver.build(context, handler, settingName);
+
+        final EnabledComponentsObserver o = new EnabledComponentsObserver(context, settingName,
+                servicePermission, serviceName, lock, listeners);
+
+        PackageMonitor packageMonitor = new PackageMonitor() {
+            @Override
+            public void onSomePackagesChanged() {
+                o.onPackagesChanged();
+
+            }
+
+            @Override
+            public void onPackageDisappeared(String packageName, int reason) {
+                o.onPackagesChanged();
+
+            }
+
+            @Override
+            public void onPackageModified(String packageName) {
+                o.onPackagesChanged();
+
+            }
+
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages, int uid,
+                    boolean doit) {
+                o.onPackagesChanged();
+
+                return super.onHandleForceStop(intent, packages, uid, doit);
+            }
+        };
+
+        packageMonitor.register(context, looper, UserHandle.ALL, true);
+
+        s.addListener(o);
+
+        return o;
+
+    }
+
+    public void onPackagesChanged() {
+        rebuildAll();
+    }
+
+    @Override
+    public void onSettingChanged() {
+        rebuildAll();
+    }
+
+    @Override
+    public void onSettingRestored(String prevValue, String newValue, int userId) {
+        rebuildAll();
+    }
+
+    public void onUsersChanged() {
+        rebuildAll();
+    }
+
+    /**
+     * Rebuild the sets of allowed components for each current user profile.
+     */
+    public void rebuildAll() {
+        synchronized (mLock) {
+            mInstalledSet.clear();
+            mEnabledSet.clear();
+            final int[] userIds = getCurrentProfileIds();
+            for (int i : userIds) {
+                ArraySet<ComponentName> implementingPackages = loadComponentNamesForUser(i);
+                ArraySet<ComponentName> packagesFromSettings =
+                        loadComponentNamesFromSetting(mSettingName, i);
+                packagesFromSettings.retainAll(implementingPackages);
+
+                mInstalledSet.put(i, implementingPackages);
+                mEnabledSet.put(i, packagesFromSettings);
+
+            }
+        }
+        sendSettingChanged();
+    }
+
+    /**
+     * Check whether a given component is present and enabled for the given user.
+     *
+     * @param component the component to check.
+     * @param userId the user ID for the component to check.
+     * @return {@code true} if present and enabled.
+     */
+    public int isValid(ComponentName component, int userId) {
+        synchronized (mLock) {
+            ArraySet<ComponentName> installedComponents = mInstalledSet.get(userId);
+            if (installedComponents == null || !installedComponents.contains(component)) {
+                return NOT_INSTALLED;
+            }
+            ArraySet<ComponentName> validComponents = mEnabledSet.get(userId);
+            if (validComponents == null || !validComponents.contains(component)) {
+                return DISABLED;
+            }
+            return NO_ERROR;
+        }
+    }
+
+    private int[] getCurrentProfileIds() {
+        UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        if (userManager == null) {
+            return null;
+        }
+        int currentUserId = ActivityManager.getCurrentUser();
+        List<UserInfo> profiles = userManager.getProfiles(currentUserId);
+        if (profiles == null) {
+            return null;
+        }
+        final int s = profiles.size();
+        int[] userIds = new int[s];
+        int ctr = 0;
+        for (UserInfo info : profiles) {
+            userIds[ctr++] = info.id;
+        }
+        return userIds;
+    }
+
+    private ArraySet<ComponentName> loadComponentNamesForUser(int userId) {
+        ArraySet<ComponentName> installed = new ArraySet<>();
+        PackageManager pm = mContext.getPackageManager();
+        Intent queryIntent = new Intent(mServiceName);
+        List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser(
+                queryIntent,
+                PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
+                userId);
+        if (installedServices != null) {
+            for (int i = 0, count = installedServices.size(); i < count; i++) {
+                ResolveInfo resolveInfo = installedServices.get(i);
+                ServiceInfo info = resolveInfo.serviceInfo;
+
+                ComponentName component = new ComponentName(info.packageName, info.name);
+                if (!mServicePermission.equals(info.permission)) {
+                    Slog.w(TAG, "Skipping service " + info.packageName + "/" + info.name
+                            + ": it does not require the permission "
+                            + mServicePermission);
+                    continue;
+                }
+                installed.add(component);
+            }
+        }
+        return installed;
+    }
+
+    private ArraySet<ComponentName> loadComponentNamesFromSetting(String settingName,
+            int userId) {
+        final ContentResolver cr = mContext.getContentResolver();
+        String settingValue = Settings.Secure.getStringForUser(
+                cr,
+                settingName,
+                userId);
+        if (TextUtils.isEmpty(settingValue))
+            return new ArraySet<>();
+        String[] restored = settingValue.split(ENABLED_SERVICES_SEPARATOR);
+        ArraySet<ComponentName> result = new ArraySet<>(restored.length);
+        for (int i = 0; i < restored.length; i++) {
+            ComponentName value = ComponentName.unflattenFromString(restored[i]);
+            if (null != value) {
+                result.add(value);
+            }
+        }
+        return result;
+    }
+
+    private void sendSettingChanged() {
+        for (EnabledComponentChangeListener l : mEnabledComponentListeners) {
+            l.onEnabledComponentChanged();
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/vr/SettingsObserver.java b/services/core/java/com/android/server/vr/SettingsObserver.java
new file mode 100644
index 0000000..ce76863
--- /dev/null
+++ b/services/core/java/com/android/server/vr/SettingsObserver.java
@@ -0,0 +1,145 @@
+/**
+ * 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.vr;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArraySet;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Detects changes in a given setting.
+ *
+ * @hide
+ */
+public class SettingsObserver {
+
+    private final String mSecureSettingName;
+    private final BroadcastReceiver mSettingRestorReceiver;
+    private final ContentObserver mContentObserver;
+    private final Set<SettingChangeListener> mSettingsListeners = new ArraySet<>();
+
+    /**
+     * Implement this to receive callbacks when the setting tracked by this observer changes.
+     */
+    public interface SettingChangeListener {
+
+        /**
+         * Called when the tracked setting has changed.
+         */
+        void onSettingChanged();
+
+
+        /**
+         * Called when the tracked setting has been restored for a particular user.
+         *
+         * @param prevValue the previous value of the setting.
+         * @param newValue the new value of the setting.
+         * @param userId the user ID for which this setting has been restored.
+         */
+        void onSettingRestored(String prevValue, String newValue, int userId);
+    }
+
+    private SettingsObserver(@NonNull final Context context, @NonNull final Handler handler,
+            @NonNull final Uri settingUri, @NonNull final String secureSettingName) {
+
+        mSecureSettingName = secureSettingName;
+        mSettingRestorReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (Intent.ACTION_SETTING_RESTORED.equals(intent.getAction())) {
+                    String element = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
+                    if (Objects.equals(element, secureSettingName)) {
+                        String prevValue = intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE);
+                        String newValue = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE);
+                        sendSettingRestored(prevValue, newValue, getSendingUserId());
+                    }
+                }
+            }
+        };
+
+        mContentObserver = new ContentObserver(handler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                if (uri == null || settingUri.equals(uri)) {
+                    sendSettingChanged();
+                }
+            }
+        };
+
+        ContentResolver resolver = context.getContentResolver();
+        resolver.registerContentObserver(settingUri, false, mContentObserver,
+                UserHandle.USER_ALL);
+    }
+
+    /**
+     * Create a SettingsObserver instance.
+     *
+     * @param context the context to query for settings changes.
+     * @param handler the handler to use for a settings ContentObserver.
+     * @param settingName the setting to track.
+     * @return a SettingsObserver instance.
+     */
+    public static SettingsObserver build(@NonNull Context context, @NonNull Handler handler,
+            @NonNull String settingName) {
+        Uri settingUri = Settings.Secure.getUriFor(settingName);
+
+        return new SettingsObserver(context, handler, settingUri, settingName);
+    }
+
+    /**
+     * Add a listener for setting changes.
+     *
+     * @param listener a {@link SettingChangeListener} instance.
+     */
+    public void addListener(@NonNull SettingChangeListener listener) {
+        mSettingsListeners.add(listener);
+
+    }
+
+    /**
+     * Remove a listener for setting changes.
+     *
+     * @param listener a {@link SettingChangeListener} instance.
+     */
+    public void removeListener(@NonNull SettingChangeListener listener) {
+        mSettingsListeners.remove(listener);
+
+    }
+
+    private void sendSettingChanged() {
+        for (SettingChangeListener l : mSettingsListeners) {
+            l.onSettingChanged();
+        }
+    }
+
+    private void sendSettingRestored(final String prevValue, final String newValue, final int userId) {
+        for (SettingChangeListener l : mSettingsListeners) {
+            l.onSettingRestored(prevValue, newValue, userId);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 42db364..6b5523f 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,14 +15,22 @@
  */
 package com.android.server.vr;
 
+import android.annotation.NonNull;
+import android.content.ComponentName;
+
 /**
- * VR mode local system service interface.
+ * Service for accessing the VR mode manager.
  *
  * @hide Only for use within system server.
  */
 public abstract class VrManagerInternal {
 
     /**
+     * The error code returned on success.
+     */
+    public static final int NO_ERROR = 0;
+
+    /**
      * Return current VR mode state.
      *
      * @return {@code true} if VR mode is enabled.
@@ -33,8 +41,11 @@
      * Set the current VR mode state.
      *
      * @param enabled {@code true} to enable VR mode.
+     * @param packageName The package name of the requested VrListenerService to bind.
+     * @param userId the user requesting the VrListenerService component.
      */
-    public abstract void setVrMode(boolean enabled);
+    public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
+            int userId);
 
     /**
      * Add a listener for VR mode state changes.
@@ -43,13 +54,23 @@
      * </p>
      * @param listener the listener instance to add.
      */
-    public abstract void registerListener(VrStateListener listener);
+    public abstract void registerListener(@NonNull VrStateListener listener);
 
     /**
      * Remove the listener from the current set of listeners.
      *
      * @param listener the listener to remove.
      */
-    public abstract void unregisterListener(VrStateListener listener);
+    public abstract void unregisterListener(@NonNull VrStateListener listener);
+
+   /**
+    * Return NO_ERROR if the given package is installed on the device and enabled as a
+    * VrListenerService for the given current user, or a negative error code indicating a failure.
+    *
+    * @param packageName the name of the package to check, or null to select the default package.
+    * @return NO_ERROR if the given package is installed and is enabled, or a negative error code
+    *       given in {@link android.service.vr.VrModeException} on failure.
+    */
+    public abstract int hasVrPackage(@NonNull ComponentName packageName, int userId);
 
 }
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 7611527..f5914faf 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,26 +16,53 @@
 package com.android.server.vr;
 
 import android.app.AppOpsManager;
+import android.annotation.NonNull;
 import android.content.Context;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
+import android.os.Looper;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.vr.IVrListener;
+import android.service.vr.VrListenerService;
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.server.SystemService;
+import com.android.server.vr.EnabledComponentsObserver.EnabledComponentChangeListener;
+import com.android.server.utils.ManagedApplicationService;
+import com.android.server.utils.ManagedApplicationService.BinderChecker;
 
 import java.util.ArrayList;
+import java.util.Set;
 
 /**
- * Service tracking whether VR mode is active, and notifying listening system services of state
- * changes.
+ * Service tracking whether VR mode is active, and notifying listening services of state changes.
+ * <p/>
+ * Services running in system server may modify the state of VrManagerService via the interface in
+ * VrManagerInternal, and may register to receive callbacks when the system VR mode changes via the
+ * interface given in VrStateListener.
+ * <p/>
+ * Device vendors may choose to receive VR state changes by implementing the VR mode HAL, e.g.:
+ *  hardware/libhardware/modules/vr
+ * <p/>
+ * In general applications may enable or disable VR mode by calling
+ * {@link android.app.Activity#setVrMode)}.  An application may also implement a service to be run
+ * while in VR mode by implementing {@link android.service.vr.VrListenerService}.
  *
- * {@hide}
+ * @see {@link android.service.vr.VrListenerService}
+ * @see {@link com.android.server.vr.VrManagerInternal}
+ * @see {@link com.android.server.vr.VrStateListener}
+ *
+ * @hide
  */
-public class VrManagerService extends SystemService {
+public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
 
     public static final String TAG = "VrManagerService";
 
@@ -46,9 +73,45 @@
 
     private final IBinder mOverlayToken = new Binder();
 
+    // State protected by mLock
     private boolean mVrModeEnabled = false;
-    private ArraySet<VrStateListener> mListeners = new ArraySet<>();
+    private final Set<VrStateListener> mListeners = new ArraySet<>();
+    private EnabledComponentsObserver mComponentObserver;
+    private ManagedApplicationService mCurrentVrService;
+    private Context mContext;
 
+    private static final BinderChecker sBinderChecker = new BinderChecker() {
+        @Override
+        public IInterface asInterface(IBinder binder) {
+            return IVrListener.Stub.asInterface(binder);
+        }
+
+        @Override
+        public boolean checkType(IInterface service) {
+            return service instanceof IVrListener;
+        }
+    };
+
+    /**
+     * Called when a user, package, or setting changes that could affect whether or not the
+     * currently bound VrListenerService is changed.
+     */
+    @Override
+    public void onEnabledComponentChanged() {
+        synchronized (mLock) {
+            if (mCurrentVrService == null) {
+                return; // No active services
+            }
+
+            // There is an active service, update it if needed
+            updateCurrentVrServiceLocked(mVrModeEnabled, mCurrentVrService.getComponent(),
+                    mCurrentVrService.getUserId());
+        }
+    }
+
+    /**
+     * Implementation of VrManagerInternal.  Callable only from system services.
+     */
     private final class LocalService extends VrManagerInternal {
         @Override
         public boolean isInVrMode() {
@@ -56,8 +119,8 @@
         }
 
         @Override
-        public void setVrMode(boolean enabled) {
-            VrManagerService.this.setVrMode(enabled);
+        public void setVrMode(boolean enabled, ComponentName packageName, int userId) {
+            VrManagerService.this.setVrMode(enabled, packageName, userId);
         }
 
         @Override
@@ -69,6 +132,11 @@
         public void unregisterListener(VrStateListener listener) {
             VrManagerService.this.removeListener(listener);
         }
+
+        @Override
+        public int hasVrPackage(ComponentName packageName, int userId) {
+            return VrManagerService.this.hasVrPackage(packageName, userId);
+        }
     }
 
     public VrManagerService(Context context) {
@@ -79,36 +147,60 @@
     public void onStart() {
         synchronized(mLock) {
             initializeNative();
+            mContext = getContext();
         }
 
         publishLocalService(VrManagerInternal.class, new LocalService());
     }
 
-    private void addListener(VrStateListener listener) {
-        synchronized (mLock) {
-            mListeners.add(listener);
-        }
-    }
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+            synchronized (mLock) {
+                Looper looper = Looper.getMainLooper();
+                Handler handler = new Handler(looper);
+                ArrayList<EnabledComponentChangeListener> listeners = new ArrayList<>();
+                listeners.add(this);
+                mComponentObserver = EnabledComponentsObserver.build(mContext, handler,
+                        Settings.Secure.ENABLED_VR_LISTENERS, looper,
+                        android.Manifest.permission.BIND_VR_LISTENER_SERVICE,
+                        VrListenerService.SERVICE_INTERFACE, mLock, listeners);
 
-    private void removeListener(VrStateListener listener) {
-        synchronized (mLock) {
-            mListeners.remove(listener);
-        }
-    }
-
-    private void setVrMode(boolean enabled) {
-        synchronized (mLock) {
-            if (mVrModeEnabled != enabled) {
-                mVrModeEnabled = enabled;
-                // Log mode change event.
-                Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
-                setVrModeNative(mVrModeEnabled);
-                updateOverlayStateLocked();
-                onVrModeChangedLocked();
+                mComponentObserver.rebuildAll();
             }
         }
     }
 
+    @Override
+    public void onStartUser(int userHandle) {
+        synchronized (mLock) {
+            mComponentObserver.onUsersChanged();
+        }
+    }
+
+    @Override
+    public void onSwitchUser(int userHandle) {
+        synchronized (mLock) {
+            mComponentObserver.onUsersChanged();
+        }
+
+    }
+
+    @Override
+    public void onStopUser(int userHandle) {
+        synchronized (mLock) {
+            mComponentObserver.onUsersChanged();
+        }
+
+    }
+
+    @Override
+    public void onCleanupUser(int userHandle) {
+        synchronized (mLock) {
+            mComponentObserver.onUsersChanged();
+        }
+    }
+
     private void updateOverlayStateLocked() {
         final long identity = Binder.clearCallingIdentity();
         try {
@@ -122,18 +214,134 @@
         }
     }
 
+    /**
+     * Send VR mode changes (if the mode state has changed), and update the bound/unbound state of
+     * the currently selected VR listener service.  If the component selected for the VR listener
+     * service has changed, unbind the previous listener and bind the new listener (if enabled).
+     * <p/>
+     * Note: Must be called while holding {@code mLock}.
+     *
+     * @param enabled new state for VR mode.
+     * @param component new component to be bound as a VR listener.
+     * @param userId user owning the component to be bound.
+     *
+     * @return {@code true} if the component/user combination specified is valid.
+     */
+    private boolean updateCurrentVrServiceLocked(boolean enabled,
+            @NonNull ComponentName component, int userId) {
+
+        // Always send mode change events.
+        changeVrModeLocked(enabled);
+
+        boolean validUserComponent = (mComponentObserver.isValid(component, userId) ==
+                EnabledComponentsObserver.NO_ERROR);
+
+        if (!enabled || !validUserComponent) {
+            // Unbind whatever is running
+            if (mCurrentVrService != null) {
+                Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
+                        mCurrentVrService.getUserId());
+                mCurrentVrService.disconnect();
+                mCurrentVrService = null;
+            }
+            return validUserComponent;
+        }
+
+        if (mCurrentVrService != null) {
+            // Unbind any running service that doesn't match the component/user selection
+            if (mCurrentVrService.disconnectIfNotMatching(component, userId)) {
+                Slog.i(TAG, "Disconnecting " + mCurrentVrService.getComponent() + " for user " +
+                        mCurrentVrService.getUserId());
+                mCurrentVrService = VrManagerService.create(mContext, component, userId);
+                mCurrentVrService.connect();
+                Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
+                        mCurrentVrService.getUserId());
+            }
+            // The service with the correct component/user is bound
+        } else {
+            // Nothing was previously running, bind a new service
+            mCurrentVrService = VrManagerService.create(mContext, component, userId);
+            mCurrentVrService.connect();
+            Slog.i(TAG, "Connecting " + mCurrentVrService.getComponent() + " for user " +
+                    mCurrentVrService.getUserId());
+        }
+
+        return validUserComponent;
+    }
+
+    /**
+     * Send VR mode change callbacks to HAL and system services if mode has actually changed.
+     * <p/>
+     * Note: Must be called while holding {@code mLock}.
+     *
+     * @param enabled new state of the VR mode.
+     */
+    private void changeVrModeLocked(boolean enabled) {
+        if (mVrModeEnabled != enabled) {
+            mVrModeEnabled = enabled;
+
+            // Log mode change event.
+            Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled"));
+            setVrModeNative(mVrModeEnabled);
+
+            updateOverlayStateLocked();
+            onVrModeChangedLocked();
+        }
+    }
+
+    /**
+     * Notify system services of VR mode change.
+     * <p/>
+     * Note: Must be called while holding {@code mLock}.
+     */
+    private void onVrModeChangedLocked() {
+        for (VrStateListener l : mListeners) {
+            l.onVrStateChanged(mVrModeEnabled);
+        }
+    }
+
+    /**
+     * Helper function for making ManagedApplicationService instances.
+     */
+    private static ManagedApplicationService create(@NonNull Context context,
+            @NonNull ComponentName component, int userId) {
+        return ManagedApplicationService.build(context, component, userId,
+                R.string.vr_listener_binding_label, Settings.ACTION_VR_LISTENER_SETTINGS,
+                sBinderChecker);
+    }
+
+    /*
+     * Implementation of VrManagerInternal calls.  These are callable from system services.
+     */
+
+    private boolean setVrMode(boolean enabled, @NonNull ComponentName targetPackageName,
+            int userId) {
+        synchronized (mLock) {
+            return updateCurrentVrServiceLocked(enabled, targetPackageName, userId);
+        }
+    }
+
     private boolean getVrMode() {
         synchronized (mLock) {
             return mVrModeEnabled;
         }
     }
 
-    /**
-     * Notify system services of VR mode change.
-     */
-    private void onVrModeChangedLocked() {
-        for (VrStateListener l : mListeners) {
-            l.onVrStateChanged(mVrModeEnabled);
+    private void addListener(VrStateListener listener) {
+        synchronized (mLock) {
+            mListeners.add(listener);
+        }
+    }
+
+    private void removeListener(VrStateListener listener) {
+        synchronized (mLock) {
+            mListeners.remove(listener);
+        }
+    }
+
+    private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
+        synchronized (mLock) {
+            return mComponentObserver.isValid(targetPackageName, userId);
         }
     }
 }
diff --git a/services/core/java/com/android/server/vr/VrStateListener.java b/services/core/java/com/android/server/vr/VrStateListener.java
index b8af4b2..b0603c80 100644
--- a/services/core/java/com/android/server/vr/VrStateListener.java
+++ b/services/core/java/com/android/server/vr/VrStateListener.java
@@ -1,4 +1,4 @@
-/*
+/**
  * Copyright (C) 2015 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,7 +16,9 @@
 package com.android.server.vr;
 
 /**
- * Listener for state changes in VrManagerService,
+ * Listener for state changes in VrManagerService.
+ *
+ * @hide Only for use within system server.
  */
 public abstract class VrStateListener {
 
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index a81fba0..55b3c7b 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -190,7 +190,8 @@
 
     public void clearThumbnail() {
         if (thumbnail != null) {
-            thumbnail.destroy();
+            thumbnail.hide();
+            mService.mWindowPlacerLocked.destroyAfterTransaction(thumbnail);
             thumbnail = null;
         }
         deferThumbnailDestruction = false;
diff --git a/services/core/java/com/android/server/wm/DimLayer.java b/services/core/java/com/android/server/wm/DimLayer.java
index fc5d8ce..95be233 100644
--- a/services/core/java/com/android/server/wm/DimLayer.java
+++ b/services/core/java/com/android/server/wm/DimLayer.java
@@ -84,10 +84,13 @@
     /** The user of this dim layer. */
     private final DimLayerUser mUser;
 
-    DimLayer(WindowManagerService service, DimLayerUser user, int displayId) {
+    private final String mName;
+
+    DimLayer(WindowManagerService service, DimLayerUser user, int displayId, String name) {
         mUser = user;
         mDisplayId = displayId;
         mService = service;
+        mName = name;
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "Ctor: displayId=" + displayId);
     }
 
@@ -100,7 +103,7 @@
                     16, 16, PixelFormat.OPAQUE,
                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
             } else {
-                mDimSurface = new SurfaceControl(service.mFxSession, TAG,
+                mDimSurface = new SurfaceControl(service.mFxSession, mName,
                     16, 16, PixelFormat.OPAQUE,
                     SurfaceControl.FX_SURFACE_DIM | SurfaceControl.HIDDEN);
             }
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 6d1cec4..97d0ae0 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -10,6 +10,8 @@
 import android.util.Slog;
 import android.util.TypedValue;
 
+import com.android.server.wm.DimLayer.DimLayerUser;
+
 import java.io.PrintWriter;
 
 /**
@@ -18,7 +20,8 @@
  * as well as other use cases (such as dimming above a dead window).
  */
 class DimLayerController {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "DimLayerController" : TAG_WM;
+    private static final String TAG_LOCAL = "DimLayerController";
+    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
 
     /** Amount of time in milliseconds to animate the dim surface from one value to another,
      * when no window animation is driving it. */
@@ -63,7 +66,8 @@
                     newDimLayer = state.dimLayer;
                 } else {
                     // Create new full screen dim layer.
-                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId);
+                    newDimLayer = new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+                            getDimLayerTag(dimLayerUser));
                 }
                 dimLayerUser.getDimBounds(mTmpBounds);
                 newDimLayer.setBounds(mTmpBounds);
@@ -73,7 +77,8 @@
             }
         } else {
             newDimLayer = (state.dimLayer == null || previousFullscreen)
-                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId)
+                    ? new DimLayer(mDisplayContent.mService, dimLayerUser, displayId,
+                            getDimLayerTag(dimLayerUser))
                     : state.dimLayer;
             dimLayerUser.getDimBounds(mTmpBounds);
             newDimLayer.setBounds(mTmpBounds);
@@ -81,6 +86,10 @@
         state.dimLayer = newDimLayer;
     }
 
+    private static String getDimLayerTag(DimLayerUser dimLayerUser) {
+        return TAG_LOCAL + "/" + dimLayerUser.toShortString();
+    }
+
     private DimLayerState getOrCreateDimLayerState(DimLayer.DimLayerUser dimLayerUser) {
         if (DEBUG_DIM_LAYER) Slog.v(TAG, "getOrCreateDimLayerState, dimLayerUser="
                 + dimLayerUser.toShortString());
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b6aa3f2..9bceee7 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -16,6 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
+import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.RemoteCallbackList;
@@ -30,19 +41,6 @@
 
 import com.android.server.wm.DimLayer.DimLayerUser;
 
-import java.util.ArrayList;
-
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
-import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
-import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-
 /**
  * Keeps information about the docked stack divider.
  */
@@ -106,7 +104,8 @@
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = context.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
-        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId());
+        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
+                "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
     }
@@ -247,8 +246,9 @@
 
     void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
         SurfaceControl.openTransaction();
-        TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
-        boolean visibleAndValid = visible && stack != null;
+        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
+        final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
+        boolean visibleAndValid = visible && stack != null && dockedStack != null;
         if (visibleAndValid) {
             stack.getDimBounds(mTmpRect);
             if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index f7035c5..92701de 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -60,7 +60,8 @@
 import java.lang.annotation.RetentionPolicy;
 
 class TaskPositioner implements DimLayer.DimLayerUser {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskPositioner" : TAG_WM;
+    private static final String TAG_LOCAL = "TaskPositioner";
+    private static final String TAG = TAG_WITH_CLASS_NAME ? TAG_LOCAL : TAG_WM;
 
     // The margin the pointer position has to be within the side of the screen to be
     // considered at the side of the screen.
@@ -287,7 +288,7 @@
         }
         mService.pauseRotationLocked();
 
-        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId());
+        mDimLayer = new DimLayer(mService, this, mDisplay.getDisplayId(), TAG_LOCAL);
         mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics);
         mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics);
         mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics);
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 86327f7..676b90f 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -585,7 +585,8 @@
         }
 
         mDisplayContent = displayContent;
-        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId());
+        mAnimationBackgroundSurface = new DimLayer(mService, this, mDisplayContent.getDisplayId(),
+                "animation background stackId=" + mStackId);
 
         Rect bounds = null;
         final TaskStack dockedStack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85bddee..4698e4e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -771,6 +771,7 @@
         }
 
         mService.destroyPreservedSurfaceLocked();
+        mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
         if (DEBUG_WINDOW_TRACE) {
             Slog.i(TAG, "!!! animate: exit mAnimating=" + mAnimating
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4ad021e..617d2b4 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -101,6 +101,10 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
 class WindowList extends ArrayList<WindowState> {
+    WindowList() {}
+    WindowList(WindowList windowList) {
+        super(windowList);
+    }
 }
 
 /**
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index c1ff96e..bf8a62e 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -434,9 +434,12 @@
                 + " remove=" + mWin.mRemoveOnExit
                 + " windowAnimating=" + isWindowAnimating());
 
-        final int N = mWin.mChildWindows.size();
-        for (int i=0; i<N; i++) {
-            mWin.mChildWindows.get(i).mWinAnimator.finishExit();
+        if (!mWin.mChildWindows.isEmpty()) {
+            // Copying to a different list as multiple children can be removed.
+            final WindowList childWindows = new WindowList(mWin.mChildWindows);
+            for (int i = childWindows.size() - 1; i >= 0; i--) {
+                childWindows.get(i).mWinAnimator.finishExit();
+            }
         }
 
         if (mEnteringAnimation) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index e3955fe..18aa186 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -46,7 +46,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.os.Debug;
-import android.os.Message;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -125,6 +124,8 @@
     }
     private final LayerAndToken mTmpLayerAndToken = new LayerAndToken();
 
+    private final ArrayList<SurfaceControl> mPendingDestroyingSurfaces = new ArrayList<>();
+
     public WindowSurfacePlacer(WindowManagerService service) {
         mService = service;
         mWallpaperControllerLocked = mService.mWallpaperControllerLocked;
@@ -542,6 +543,7 @@
         mService.enableScreenIfNeededLocked();
 
         mService.scheduleAnimationLocked();
+        mService.mWindowPlacerLocked.destroyPendingSurfaces();
 
         if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
                 "performSurfacePlacementInner exit: animating=" + mService.mAnimator.isAnimating());
@@ -1620,6 +1622,25 @@
         }
     }
 
+    /**
+     * Puts the {@param surface} into a pending list to be destroyed after the current transaction
+     * has been committed.
+     */
+    void destroyAfterTransaction(SurfaceControl surface) {
+        mPendingDestroyingSurfaces.add(surface);
+    }
+
+    /**
+     * Destroys any surfaces that have been put into the pending list with
+     * {@link #destroyAfterTransaction}.
+     */
+    void destroyPendingSurfaces() {
+        for (int i = mPendingDestroyingSurfaces.size() - 1; i >= 0; i--) {
+            mPendingDestroyingSurfaces.get(i).destroy();
+        }
+        mPendingDestroyingSurfaces.clear();
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mTraversalScheduled="); pw.println(mTraversalScheduled);
     }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b8c31e3..a2d5259 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -635,7 +635,6 @@
         WallpaperManagerService wallpaper = null;
         LocationManagerService location = null;
         CountryDetectorService countryDetector = null;
-        TextServicesManagerService tsms = null;
         ILockSettings lockSettings = null;
         AssetAtlasService atlas = null;
         MediaRouterService mediaRouter = null;
@@ -762,14 +761,7 @@
             }
 
             if (!disableNonCoreServices) {
-                traceBeginAndSlog("StartTextServicesManagerService");
-                try {
-                    tsms = new TextServicesManagerService(context);
-                    ServiceManager.addService(Context.TEXT_SERVICES_MANAGER_SERVICE, tsms);
-                } catch (Throwable e) {
-                    reportWtf("starting Text Service Manager Service", e);
-                }
-                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+                mSystemServiceManager.startService(TextServicesManagerService.Lifecycle.class);
             }
 
             if (!disableNetwork) {
@@ -1256,7 +1248,6 @@
         final CountryDetectorService countryDetectorF = countryDetector;
         final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
         final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService;
-        final TextServicesManagerService textServiceManagerServiceF = tsms;
         final StatusBarManagerService statusBarF = statusBar;
         final AssetAtlasService atlasF = atlas;
         final InputManagerService inputManagerF = inputManager;
@@ -1372,12 +1363,6 @@
                     reportWtf("Notifying CommonTimeManagementService running", e);
                 }
                 try {
-                    if (textServiceManagerServiceF != null)
-                        textServiceManagerServiceF.systemRunning();
-                } catch (Throwable e) {
-                    reportWtf("Notifying TextServicesManagerService running", e);
-                }
-                try {
                     if (atlasF != null) atlasF.systemRunning();
                 } catch (Throwable e) {
                     reportWtf("Notifying AssetAtlasService running", e);
diff --git a/services/print/java/com/android/server/print/PrintManagerService.java b/services/print/java/com/android/server/print/PrintManagerService.java
index e6f4177..8cfc4a3 100644
--- a/services/print/java/com/android/server/print/PrintManagerService.java
+++ b/services/print/java/com/android/server/print/PrintManagerService.java
@@ -41,13 +41,16 @@
 import android.print.IPrintDocumentAdapter;
 import android.print.IPrintJobStateChangeListener;
 import android.print.IPrintManager;
+import android.print.IPrintServicesChangeListener;
 import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintAttributes;
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
+import android.print.PrintManager;
 import android.print.PrinterId;
 import android.printservice.PrintServiceInfo;
 import android.provider.Settings;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.content.PackageMonitor;
@@ -66,6 +69,8 @@
  * PrintManager implementation is contained within.
  */
 public final class PrintManagerService extends SystemService {
+    private static final String LOG_TAG = "PrintManagerService";
+
     private final PrintManagerImpl mPrintManagerImpl;
 
     public PrintManagerService(Context context) {
@@ -253,7 +258,10 @@
         }
 
         @Override
-        public List<PrintServiceInfo> getEnabledPrintServices(int userId) {
+        public List<PrintServiceInfo> getPrintServices(int selectionFlags, int userId) {
+            Preconditions.checkFlagsArgument(selectionFlags,
+                    PrintManager.DISABLED_SERVICES | PrintManager.ENABLED_SERVICES);
+
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
             final UserState userState;
             synchronized (mLock) {
@@ -262,34 +270,44 @@
                     return null;
                 }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
-
-                // The user state might be updated via the same observer-set as the caller of this
-                // interface. If the caller is called back first the user state is not yet updated
-                // and the user gets and inconsistent view. Hence force an update.
-                userState.updateIfNeededLocked();
             }
             final long identity = Binder.clearCallingIdentity();
             try {
-                return userState.getEnabledPrintServices();
+                return userState.getPrintServices(selectionFlags);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
 
         @Override
-        public List<PrintServiceInfo> getInstalledPrintServices(int userId) {
+        public void setPrintServiceEnabled(ComponentName service, boolean isEnabled, int userId) {
             final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final int appId = UserHandle.getAppId(Binder.getCallingUid());
+
+            try {
+                if (appId != Process.SYSTEM_UID && appId != UserHandle.getAppId(
+                        mContext.getPackageManager().getPackageUidAsUser(
+                                PrintManager.PRINT_SPOOLER_PACKAGE_NAME, resolvedUserId))) {
+                    throw new SecurityException("Only system and print spooler can call this");
+                }
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(LOG_TAG, "Could not verify caller", e);
+                return;
+            }
+
+            service = Preconditions.checkNotNull(service);
+
             final UserState userState;
             synchronized (mLock) {
-                // Only the current group members can get installed services.
+                // Only the current group members can enable / disable services.
                 if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
-                    return null;
+                    return;
                 }
                 userState = getOrCreateUserStateLocked(resolvedUserId);
             }
             final long identity = Binder.clearCallingIdentity();
             try {
-                return userState.getInstalledPrintServices();
+                userState.setPrintServiceEnabled(service, isEnabled);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -496,6 +514,50 @@
         }
 
         @Override
+        public void addPrintServicesChangeListener(IPrintServicesChangeListener listener,
+                int userId) throws RemoteException {
+            listener = Preconditions.checkNotNull(listener);
+
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                // Only the current group members can add a print services listener.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+                    return;
+                }
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.addPrintServicesChangeListener(listener);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void removePrintServicesChangeListener(IPrintServicesChangeListener listener,
+                int userId) {
+            listener = Preconditions.checkNotNull(listener);
+
+            final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+            final UserState userState;
+            synchronized (mLock) {
+                // Only the current group members can remove a print job listener.
+                if (resolveCallingProfileParentLocked(resolvedUserId) != getCurrentUserId()) {
+                    return;
+                }
+                userState = getOrCreateUserStateLocked(resolvedUserId);
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                userState.removePrintServicesChangeListener(listener);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             fd = Preconditions.checkNotNull(fd);
             pw = Preconditions.checkNotNull(pw);
@@ -550,43 +612,79 @@
 
         private void registerBroadcastReceivers() {
             PackageMonitor monitor = new PackageMonitor() {
-                private void updateServices(String packageName) {
-                    if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-                    synchronized (mLock) {
-                        // A background user/profile's print jobs are running but there is
-                        // no UI shown. Hence, if the packages of such a user change we need
-                        // to handle it as the change may affect ongoing print jobs.
-                        boolean servicesChanged = false;
-                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                /**
+                 * Checks if the package contains a print service.
+                 *
+                 * @param packageName The name of the package
+                 *
+                 * @return true iff the package contains a print service
+                 */
+                private boolean hasPrintService(String packageName) {
+                    Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+                    intent.setPackage(packageName);
 
-                        List<PrintServiceInfo> installedServices = userState
-                                .getInstalledPrintServices();
-                        final int numInstalledServices = installedServices.size();
-                        for (int i = 0; i < numInstalledServices; i++) {
-                            if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
-                                    .equals(packageName)) {
-                                servicesChanged = true;
-                                break;
-                            }
-                        }
-                        if (servicesChanged) {
-                            userState.updateIfNeededLocked();
+                    List<ResolveInfo> installedServices = mContext.getPackageManager()
+                            .queryIntentServicesAsUser(intent,
+                                    GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
+                                    getChangingUserId());
+
+                    return installedServices != null && !installedServices.isEmpty();
+                }
+
+                /**
+                 * Checks if there is a print service currently registered for this package.
+                 *
+                 * @param userState The userstate for the current user
+                 * @param packageName The name of the package
+                 *
+                 * @return true iff the package contained (and might still contain) a print service
+                 */
+                private boolean hadPrintService(@NonNull UserState userState, String packageName) {
+                    List<PrintServiceInfo> installedServices = userState
+                            .getPrintServices(PrintManager.ALL_SERVICES);
+
+                    if (installedServices == null) {
+                        return false;
+                    }
+
+                    final int numInstalledServices = installedServices.size();
+                    for (int i = 0; i < numInstalledServices; i++) {
+                        if (installedServices.get(i).getResolveInfo().serviceInfo.packageName
+                                .equals(packageName)) {
+                            return true;
                         }
                     }
+
+                    return false;
                 }
 
                 @Override
                 public void onPackageModified(String packageName) {
                     if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-                    updateServices(packageName);
-                    getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+                    synchronized (mLock) {
+                        if (hadPrintService(userState, packageName)
+                                || hasPrintService(packageName)) {
+                            userState.updateIfNeededLocked();
+                        }
+                    }
+
+                    userState.prunePrintServices();
                 }
 
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
                     if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-                    updateServices(packageName);
-                    getOrCreateUserStateLocked(getChangingUserId()).prunePrintServices();
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+
+                    synchronized (mLock) {
+                        if (hadPrintService(userState, packageName)) {
+                            userState.updateIfNeededLocked();
+                        }
+                    }
+
+                    userState.prunePrintServices();
                 }
 
                 @Override
@@ -601,7 +699,7 @@
                         boolean stoppedSomePackages = false;
 
                         List<PrintServiceInfo> enabledServices = userState
-                                .getEnabledPrintServices();
+                                .getPrintServices(PrintManager.ENABLED_SERVICES);
                         if (enabledServices == null) {
                             return false;
                         }
@@ -630,21 +728,11 @@
                 @Override
                 public void onPackageAdded(String packageName, int uid) {
                     if (!mUserManager.isUserUnlocked(getChangingUserId())) return;
-
-                    // A background user/profile's print jobs are running but there is
-                    // no UI shown. Hence, if the packages of such a user change we need
-                    // to handle it as the change may affect ongoing print jobs.
-                    Intent intent = new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
-                    intent.setPackage(packageName);
-
-                    List<ResolveInfo> installedServices = mContext.getPackageManager()
-                            .queryIntentServicesAsUser(intent,
-                                    GET_SERVICES | MATCH_DEBUG_TRIAGED_MISSING,
-                                    getChangingUserId());
-
-                    if (installedServices != null) {
-                        UserState userState = getOrCreateUserStateLocked(getChangingUserId());
-                        userState.updateIfNeededLocked();
+                    synchronized (mLock) {
+                        if (hasPrintService(packageName)) {
+                            UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                            userState.updateIfNeededLocked();
+                        }
                     }
                 }
             };
diff --git a/services/print/java/com/android/server/print/RemotePrintSpooler.java b/services/print/java/com/android/server/print/RemotePrintSpooler.java
index d179b95..e1d8c6c 100644
--- a/services/print/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/print/java/com/android/server/print/RemotePrintSpooler.java
@@ -36,6 +36,7 @@
 import android.print.IPrintSpoolerClient;
 import android.print.PrintJobId;
 import android.print.PrintJobInfo;
+import android.print.PrintManager;
 import android.print.PrinterId;
 import android.printservice.PrintService;
 import android.util.Slog;
@@ -115,8 +116,8 @@
         mCallbacks = callbacks;
         mClient = new PrintSpoolerClient(this);
         mIntent = new Intent();
-        mIntent.setComponent(new ComponentName("com.android.printspooler",
-                "com.android.printspooler.model.PrintSpoolerService"));
+        mIntent.setComponent(new ComponentName(PrintManager.PRINT_SPOOLER_PACKAGE_NAME,
+                PrintManager.PRINT_SPOOLER_PACKAGE_NAME + ".model.PrintSpoolerService"));
     }
 
     public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
diff --git a/services/print/java/com/android/server/print/UserState.java b/services/print/java/com/android/server/print/UserState.java
index fcf2fc8..f2f555b 100644
--- a/services/print/java/com/android/server/print/UserState.java
+++ b/services/print/java/com/android/server/print/UserState.java
@@ -44,6 +44,7 @@
 import android.os.UserHandle;
 import android.print.IPrintDocumentAdapter;
 import android.print.IPrintJobStateChangeListener;
+import android.print.IPrintServicesChangeListener;
 import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintAttributes;
 import android.print.PrintJobId;
@@ -121,6 +122,8 @@
 
     private List<PrintJobStateChangeListenerRecord> mPrintJobStateChangeListenerRecords;
 
+    private List<PrintServicesChangeListenerRecord> mPrintServicesChangeListenerRecords;
+
     private boolean mDestroyed;
 
     public UserState(Context context, int userId, Object lock) {
@@ -342,29 +345,63 @@
         mSpooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
     }
 
-    public @Nullable List<PrintServiceInfo> getEnabledPrintServices() {
+    public @Nullable List<PrintServiceInfo> getPrintServices(int selectionFlags) {
         synchronized (mLock) {
-            List<PrintServiceInfo> enabledServices = null;
+            List<PrintServiceInfo> selectedServices = null;
             final int installedServiceCount = mInstalledServices.size();
             for (int i = 0; i < installedServiceCount; i++) {
                 PrintServiceInfo installedService = mInstalledServices.get(i);
+
                 ComponentName componentName = new ComponentName(
                         installedService.getResolveInfo().serviceInfo.packageName,
                         installedService.getResolveInfo().serviceInfo.name);
-                if (mActiveServices.containsKey(componentName)) {
-                    if (enabledServices == null) {
-                        enabledServices = new ArrayList<PrintServiceInfo>();
+
+                // Update isEnabled under the same lock the final returned list is created
+                installedService.setIsEnabled(mActiveServices.containsKey(componentName));
+
+                if (installedService.isEnabled()) {
+                    if ((selectionFlags & PrintManager.ENABLED_SERVICES) == 0) {
+                        continue;
                     }
-                    enabledServices.add(installedService);
+                } else {
+                    if ((selectionFlags & PrintManager.DISABLED_SERVICES) == 0) {
+                        continue;
+                    }
                 }
+
+                if (selectedServices == null) {
+                    selectedServices = new ArrayList<>();
+                }
+                selectedServices.add(installedService);
             }
-            return enabledServices;
+            return selectedServices;
         }
     }
 
-    public List<PrintServiceInfo> getInstalledPrintServices() {
+    public void setPrintServiceEnabled(@NonNull ComponentName serviceName, boolean isEnabled) {
         synchronized (mLock) {
-            return mInstalledServices;
+            boolean isChanged = false;
+            if (isEnabled) {
+                isChanged = mDisabledServices.remove(serviceName);
+            } else {
+                // Make sure to only disable services that are currently installed
+                final int numServices = mInstalledServices.size();
+                for (int i = 0; i < numServices; i++) {
+                    PrintServiceInfo service = mInstalledServices.get(i);
+
+                    if (service.getComponentName().equals(serviceName)) {
+                        mDisabledServices.add(serviceName);
+                        isChanged = true;
+                        break;
+                    }
+                }
+            }
+
+            if (isChanged) {
+                writeDisabledPrintServicesLocked(mDisabledServices);
+
+                onConfigurationChangedLocked();
+            }
         }
     }
 
@@ -523,6 +560,44 @@
         }
     }
 
+    public void addPrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener)
+            throws RemoteException {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (mPrintServicesChangeListenerRecords == null) {
+                mPrintServicesChangeListenerRecords = new ArrayList<>();
+            }
+            mPrintServicesChangeListenerRecords.add(
+                    new PrintServicesChangeListenerRecord(listener) {
+                        @Override
+                        public void onBinderDied() {
+                            mPrintServicesChangeListenerRecords.remove(this);
+                        }
+                    });
+        }
+    }
+
+    public void removePrintServicesChangeListener(@NonNull IPrintServicesChangeListener listener) {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (mPrintServicesChangeListenerRecords == null) {
+                return;
+            }
+            final int recordCount = mPrintServicesChangeListenerRecords.size();
+            for (int i = 0; i < recordCount; i++) {
+                PrintServicesChangeListenerRecord record =
+                        mPrintServicesChangeListenerRecords.get(i);
+                if (record.listener.asBinder().equals(listener.asBinder())) {
+                    mPrintServicesChangeListenerRecords.remove(i);
+                    break;
+                }
+            }
+            if (mPrintServicesChangeListenerRecords.isEmpty()) {
+                mPrintServicesChangeListenerRecords = null;
+            }
+        }
+    }
+
     @Override
     public void onPrintJobStateChanged(PrintJobInfo printJob) {
         mPrintJobForAppCache.onPrintJobStateChanged(printJob);
@@ -530,6 +605,10 @@
                 printJob.getAppId(), 0, printJob.getId()).sendToTarget();
     }
 
+    public void onPrintServicesChanged() {
+        mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_SERVICES_CHANGED).sendToTarget();
+    }
+
     @Override
     public void onPrintersAdded(List<PrinterInfo> printers) {
         synchronized (mLock) {
@@ -894,6 +973,8 @@
                 iterator.remove();
             }
         }
+
+        onPrintServicesChanged();
     }
 
     private void addServiceLocked(RemotePrintService service) {
@@ -978,8 +1059,29 @@
         }
     }
 
+    private void handleDispatchPrintServicesChanged() {
+        final List<PrintServicesChangeListenerRecord> records;
+        synchronized (mLock) {
+            if (mPrintServicesChangeListenerRecords == null) {
+                return;
+            }
+            records = new ArrayList<>(mPrintServicesChangeListenerRecords);
+        }
+        final int recordCount = records.size();
+        for (int i = 0; i < recordCount; i++) {
+            PrintServicesChangeListenerRecord record = records.get(i);
+
+            try {
+                record.listener.onPrintServicesChanged();;
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error notifying for print services change", re);
+            }
+        }
+    }
+
     private final class UserStateHandler extends Handler {
         public static final int MSG_DISPATCH_PRINT_JOB_STATE_CHANGED = 1;
+        public static final int MSG_DISPATCH_PRINT_SERVICES_CHANGED = 2;
 
         public UserStateHandler(Looper looper) {
             super(looper, null, false);
@@ -987,10 +1089,17 @@
 
         @Override
         public void handleMessage(Message message) {
-            if (message.what == MSG_DISPATCH_PRINT_JOB_STATE_CHANGED) {
-                PrintJobId printJobId = (PrintJobId) message.obj;
-                final int appId = message.arg1;
-                handleDispatchPrintJobStateChanged(printJobId, appId);
+            switch (message.what) {
+                case MSG_DISPATCH_PRINT_JOB_STATE_CHANGED:
+                    PrintJobId printJobId = (PrintJobId) message.obj;
+                    final int appId = message.arg1;
+                    handleDispatchPrintJobStateChanged(printJobId, appId);
+                    break;
+                case MSG_DISPATCH_PRINT_SERVICES_CHANGED:
+                    handleDispatchPrintServicesChanged();
+                    break;
+                default:
+                    // not reached
             }
         }
     }
@@ -1015,6 +1124,23 @@
         public abstract void onBinderDied();
     }
 
+    private abstract class PrintServicesChangeListenerRecord implements DeathRecipient {
+        @NonNull final IPrintServicesChangeListener listener;
+
+        public PrintServicesChangeListenerRecord(@NonNull IPrintServicesChangeListener listener) throws RemoteException {
+            this.listener = listener;
+            listener.asBinder().linkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            listener.asBinder().unlinkToDeath(this, 0);
+            onBinderDied();
+        }
+
+        public abstract void onBinderDied();
+    }
+
     private class PrinterDiscoverySessionMediator {
         private final ArrayMap<PrinterId, PrinterInfo> mPrinters =
                 new ArrayMap<PrinterId, PrinterInfo>();
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 697dfbf..d0e431a 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -65,7 +65,7 @@
  */
 public final class BridgeTypedArray extends TypedArray {
 
-    private final BridgeResources mBridgeResources;
+    private final Resources mBridgeResources;
     private final BridgeContext mContext;
     private final boolean mPlatformFile;
 
@@ -78,7 +78,7 @@
     @Nullable
     private int[] mEmptyIds;
 
-    public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+    public BridgeTypedArray(Resources resources, BridgeContext context, int len,
             boolean platformFile) {
         super(resources, null, null, 0);
         mBridgeResources = resources;
@@ -701,7 +701,8 @@
         if (resVal instanceof ArrayResourceValue) {
             ArrayResourceValue array = (ArrayResourceValue) resVal;
             int count = array.getElementCount();
-            return count >= 0 ? mBridgeResources.fillValues(array, new CharSequence[count]) : null;
+            return count >= 0 ? Resources_Delegate.fillValues(mBridgeResources, array, new CharSequence[count]) :
+                    null;
         }
         int id = getResourceId(index, 0);
         String resIdMessage = id > 0 ? " (resource id 0x" + Integer.toHexString(id) + ')' : "";
@@ -949,7 +950,6 @@
     }
 
     static TypedArray obtain(Resources res, int len) {
-        return res instanceof BridgeResources ?
-                new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+        return new BridgeTypedArray(res, null, len, true);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
similarity index 63%
rename from tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
rename to tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
index fe46480..985dd5a 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Delegate.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 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.
@@ -28,8 +28,10 @@
 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
 import com.android.layoutlib.bridge.impl.ParserFactory;
 import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
 import com.android.ninepatch.NinePatch;
 import com.android.resources.ResourceType;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 import com.android.util.Pair;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -37,6 +39,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources.Theme;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -50,132 +54,101 @@
 import java.util.Iterator;
 
 @SuppressWarnings("deprecation")
-public final class BridgeResources extends Resources {
+public class Resources_Delegate {
 
-    private BridgeContext mContext;
-    private LayoutlibCallback mLayoutlibCallback;
-    private boolean[] mPlatformResourceFlag = new boolean[1];
-    private TypedValue mTmpValue = new TypedValue();
+    private static boolean[] mPlatformResourceFlag = new boolean[1];
 
-    /**
-     * Simpler wrapper around FileInputStream. This is used when the input stream represent
-     * not a normal bitmap but a nine patch.
-     * This is useful when the InputStream is created in a method but used in another that needs
-     * to know whether this is 9-patch or not, such as BitmapFactory.
-     */
-    public class NinePatchInputStream extends FileInputStream {
-        private boolean mFakeMarkSupport = true;
-        public NinePatchInputStream(File file) throws FileNotFoundException {
-            super(file);
-        }
-
-        @Override
-        public boolean markSupported() {
-            //noinspection SimplifiableIfStatement
-            if (mFakeMarkSupport) {
-                // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
-                return true;
-            }
-
-            return super.markSupported();
-        }
-
-        public void disableFakeMarkSupport() {
-            // disable fake mark support so that in case codec actually try to use them
-            // we don't lie to them.
-            mFakeMarkSupport = false;
-        }
-    }
-
-    /**
-     * This initializes the static field {@link Resources#mSystem} which is used
-     * by methods who get global resources using {@link Resources#getSystem()}.
-     * <p/>
-     * They will end up using our bridge resources.
-     * <p/>
-     * {@link Bridge} calls this method after setting up a new bridge.
-     */
     public static Resources initSystem(BridgeContext context,
             AssetManager assets,
             DisplayMetrics metrics,
             Configuration config,
             LayoutlibCallback layoutlibCallback) {
-        return Resources.mSystem = new BridgeResources(context,
-                assets,
-                metrics,
-                config,
-                layoutlibCallback);
+        Resources resources = new Resources(assets, metrics, config);
+        resources.mContext = context;
+        resources.mLayoutlibCallback = layoutlibCallback;
+        return Resources.mSystem = resources;
     }
 
     /**
-     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
-     * around that would prevent us from unloading the library.
+     * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects around that
+     * would prevent us from unloading the library.
      */
     public static void disposeSystem() {
-        if (Resources.mSystem instanceof BridgeResources) {
-            ((BridgeResources)(Resources.mSystem)).mContext = null;
-            ((BridgeResources)(Resources.mSystem)).mLayoutlibCallback = null;
-        }
+        Resources.mSystem.mContext = null;
+        Resources.mSystem.mLayoutlibCallback = null;
         Resources.mSystem = null;
     }
 
-    private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
-            Configuration config, LayoutlibCallback layoutlibCallback) {
-        super(assets, metrics, config);
-        mContext = context;
-        mLayoutlibCallback = layoutlibCallback;
+    public static BridgeTypedArray newTypeArray(Resources resources, int numEntries,
+            boolean platformFile) {
+        return new BridgeTypedArray(resources, resources.mContext, numEntries, platformFile);
     }
 
-    public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
-        return new BridgeTypedArray(this, mContext, numEntries, platformFile);
-    }
-
-    private Pair<String, ResourceValue> getResourceValue(int id, boolean[] platformResFlag_out) {
+    private static Pair<String, ResourceValue> getResourceValue(Resources resources, int id,
+            boolean[] platformResFlag_out) {
         // first get the String related to this id in the framework
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
+        // Set the layoutlib callback and context for resources
+        if (resources != Resources.mSystem && resources.mLayoutlibCallback == null) {
+            resources.mLayoutlibCallback = Resources.mSystem.mLayoutlibCallback;
+            resources.mContext = Resources.mSystem.mContext;
+        }
+
         if (resourceInfo != null) {
             platformResFlag_out[0] = true;
             String attributeName = resourceInfo.getSecond();
 
-            return Pair.of(attributeName, mContext.getRenderResources().getFrameworkResource(
-                    resourceInfo.getFirst(), attributeName));
+            return Pair.of(attributeName,
+                    resources.mContext.getRenderResources().getFrameworkResource(
+                            resourceInfo.getFirst(), attributeName));
         }
 
         // didn't find a match in the framework? look in the project.
-        if (mLayoutlibCallback != null) {
-            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+        if (resources.mLayoutlibCallback != null) {
+            resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
 
             if (resourceInfo != null) {
                 platformResFlag_out[0] = false;
                 String attributeName = resourceInfo.getSecond();
 
-                return Pair.of(attributeName, mContext.getRenderResources().getProjectResource(
-                        resourceInfo.getFirst(), attributeName));
+                return Pair.of(attributeName,
+                        resources.mContext.getRenderResources().getProjectResource(
+                                resourceInfo.getFirst(), attributeName));
             }
         }
 
         return null;
     }
 
-    @Override
-    public Drawable getDrawable(int id, Theme theme) {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id) {
+        return getDrawable(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static Drawable getDrawable(Resources resources, int id, Theme theme) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
-            return ResourceHelper.getDrawable(value.getSecond(), mContext, theme);
+            return ResourceHelper.getDrawable(value.getSecond(), resources.mContext, theme);
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public int getColor(int id, Theme theme) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id) {
+        return getColor(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static int getColor(Resources resources, int id, Theme theme) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resourceValue = value.getSecond();
@@ -187,7 +160,7 @@
                 String message;
                 if (new File(resourceValue.getValue()).isFile()) {
                     String resource = (resourceValue.isFramework() ? "@android:" : "@") + "color/"
-                      + resourceValue.getName();
+                            + resourceValue.getName();
                     message = "Hexadecimal color expected, found Color State List for " + resource;
                 } else {
                     message = e.getMessage();
@@ -200,31 +173,57 @@
         // Suppress possible NPE. getColorStateList will never return null, it will instead
         // throw an exception, but intelliJ can't figure that out
         //noinspection ConstantConditions
-        return getColorStateList(id, theme).getDefaultColor();
+        return getColorStateList(resources, id, theme).getDefaultColor();
     }
 
-    @Override
-    public ColorStateList getColorStateList(int id, Theme theme) throws NotFoundException {
-        Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id) throws NotFoundException {
+        return getColorStateList(resources, id, null);
+    }
+
+    @LayoutlibDelegate
+    static ColorStateList getColorStateList(Resources resources, int id, Theme theme)
+            throws NotFoundException {
+        Pair<String, ResourceValue> resValue =
+                getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (resValue != null) {
             ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
-                    mContext);
+                    resources.mContext);
             if (stateList != null) {
                 return stateList.obtainForTheme(theme);
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public CharSequence getText(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id, CharSequence def) {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
+
+        if (value != null) {
+            ResourceValue resValue = value.getSecond();
+
+            assert resValue != null;
+            if (resValue != null) {
+                String v = resValue.getValue();
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        return def;
+    }
+
+    @LayoutlibDelegate
+    static CharSequence getText(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -239,38 +238,38 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public CharSequence[] getTextArray(int id) throws NotFoundException {
-        ResourceValue resValue = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static CharSequence[] getTextArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new CharSequence[0];
         } else if (!(resValue instanceof ArrayResourceValue)) {
             return new CharSequence[]{
-                    resolveReference(resValue.getValue(), resValue.isFramework())};
+                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
         ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(arv, new CharSequence[arv.getElementCount()]);
+        return fillValues(resources, arv, new CharSequence[arv.getElementCount()]);
     }
 
-    @Override
-    public String[] getStringArray(int id) throws NotFoundException {
-        ResourceValue resValue = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static String[] getStringArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue resValue = getArrayResourceValue(resources, id);
         if (resValue == null) {
             // Error already logged by getArrayResourceValue.
             return new String[0];
         } else if (!(resValue instanceof ArrayResourceValue)) {
             return new String[]{
-                    resolveReference(resValue.getValue(), resValue.isFramework())};
+                    resolveReference(resources, resValue.getValue(), resValue.isFramework())};
         }
         ArrayResourceValue arv = ((ArrayResourceValue) resValue);
-        return fillValues(arv, new String[arv.getElementCount()]);
+        return fillValues(resources, arv, new String[arv.getElementCount()]);
     }
 
     /**
@@ -278,25 +277,26 @@
      * always Strings. The ideal signature for the method should be &lt;T super String&gt;, but java
      * generics don't support it.
      */
-    <T extends CharSequence> T[] fillValues(ArrayResourceValue resValue, T[] values) {
+    static <T extends CharSequence> T[] fillValues(Resources resources, ArrayResourceValue resValue,
+            T[] values) {
         int i = 0;
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
             @SuppressWarnings("unchecked")
-            T s = (T) resolveReference(iterator.next(), resValue.isFramework());
+            T s = (T) resolveReference(resources, iterator.next(), resValue.isFramework());
             values[i] = s;
         }
         return values;
     }
 
-    @Override
-    public int[] getIntArray(int id) throws NotFoundException {
-        ResourceValue rv = getArrayResourceValue(id);
+    @LayoutlibDelegate
+    static int[] getIntArray(Resources resources, int id) throws NotFoundException {
+        ResourceValue rv = getArrayResourceValue(resources, id);
         if (rv == null) {
             // Error already logged by getArrayResourceValue.
             return new int[0];
         } else if (!(rv instanceof ArrayResourceValue)) {
             // This is an older IDE that can only give us the first element of the array.
-            String firstValue = resolveReference(rv.getValue(), rv.isFramework());
+            String firstValue = resolveReference(resources, rv.getValue(), rv.isFramework());
             try {
                 return new int[]{getInt(firstValue)};
             } catch (NumberFormatException e) {
@@ -310,7 +310,7 @@
         int[] values = new int[resValue.getElementCount()];
         int i = 0;
         for (Iterator<String> iterator = resValue.iterator(); iterator.hasNext(); i++) {
-            String element = resolveReference(iterator.next(), resValue.isFramework());
+            String element = resolveReference(resources, iterator.next(), resValue.isFramework());
             try {
                 values[i] = getInt(element);
             } catch (NumberFormatException e) {
@@ -330,11 +330,13 @@
      * method returns the ResourceValue. This happens on older versions of the IDE, which did not
      * parse the array resources properly.
      * <p/>
+     *
      * @throws NotFoundException if no resource if found
      */
     @Nullable
-    private ResourceValue getArrayResourceValue(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    private static ResourceValue getArrayResourceValue(Resources resources, int id)
+            throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue resValue = v.getSecond();
@@ -360,19 +362,20 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
     @NonNull
-    private String resolveReference(@NonNull String ref, boolean forceFrameworkOnly) {
+    private static String resolveReference(Resources resources, @NonNull String ref,
+            boolean forceFrameworkOnly) {
         if (ref.startsWith(SdkConstants.PREFIX_RESOURCE_REF) || ref.startsWith
                 (SdkConstants.PREFIX_THEME_REF)) {
             ResourceValue rv =
-                    mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
-            rv = mContext.getRenderResources().resolveResValue(rv);
+                    resources.mContext.getRenderResources().findResValue(ref, forceFrameworkOnly);
+            rv = resources.mContext.getRenderResources().resolveResValue(rv);
             if (rv != null) {
                 return rv.getValue();
             } else {
@@ -384,9 +387,9 @@
         return ref;
     }
 
-    @Override
-    public XmlResourceParser getLayout(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getLayout(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
@@ -394,8 +397,8 @@
 
             try {
                 // check if the current parser can provide us with a custom parser.
-                if (mPlatformResourceFlag[0] == false) {
-                    parser = mLayoutlibCallback.getParser(value);
+                if (!mPlatformResourceFlag[0]) {
+                    parser = resources.mLayoutlibCallback.getParser(value);
                 }
 
                 // create a new one manually if needed.
@@ -409,7 +412,8 @@
                 }
 
                 if (parser != null) {
-                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    return new BridgeXmlBlockParser(parser, resources.mContext,
+                            mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -422,19 +426,19 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public XmlResourceParser getAnimation(int id) throws NotFoundException {
-        Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getAnimation(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> v = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (v != null) {
             ResourceValue value = v.getSecond();
-            XmlPullParser parser = null;
+            XmlPullParser parser;
 
             try {
                 File xml = new File(value.getValue());
@@ -443,7 +447,8 @@
                     // give that to our XmlBlockParser
                     parser = ParserFactory.create(xml);
 
-                    return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                    return new BridgeXmlBlockParser(parser, resources.mContext,
+                            mPlatformResourceFlag[0]);
                 }
             } catch (XmlPullParserException e) {
                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
@@ -456,26 +461,31 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
-        return mContext.obtainStyledAttributes(set, attrs);
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, AttributeSet set, int[] attrs) {
+        return resources.mContext.obtainStyledAttributes(set, attrs);
     }
 
-    @Override
-    public TypedArray obtainTypedArray(int id) throws NotFoundException {
+    @LayoutlibDelegate
+    static TypedArray obtainAttributes(Resources resources, Resources.Theme theme, AttributeSet
+            set, int[] attrs) {
+        return Resources.obtainAttributes_Original(resources, theme, set, attrs);
+    }
+
+    @LayoutlibDelegate
+    static TypedArray obtainTypedArray(Resources resources, int id) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-
-    @Override
-    public float getDimension(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static float getDimension(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -490,26 +500,26 @@
                     } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
                         return LayoutParams.WRAP_CONTENT;
                     }
-
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return mTmpValue.getDimension(getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return tmpValue.getDimension(resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getDimensionPixelOffset(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getDimensionPixelOffset(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -518,26 +528,27 @@
             if (resValue != null) {
                 String v = resValue.getValue();
                 if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return TypedValue.complexToDimensionPixelOffset(mTmpValue.data,
-                                getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelOffset(tmpValue.data,
+                                resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getDimensionPixelSize(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getDimensionPixelSize(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -546,26 +557,27 @@
             if (resValue != null) {
                 String v = resValue.getValue();
                 if (v != null) {
+                    TypedValue tmpValue = new TypedValue();
                     if (ResourceHelper.parseFloatAttribute(
-                            value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
-                            mTmpValue.type == TypedValue.TYPE_DIMENSION) {
-                        return TypedValue.complexToDimensionPixelSize(mTmpValue.data,
-                                getDisplayMetrics());
+                            value.getFirst(), v, tmpValue, true /*requireUnit*/) &&
+                            tmpValue.type == TypedValue.TYPE_DIMENSION) {
+                        return TypedValue.complexToDimensionPixelSize(tmpValue.data,
+                                resources.getDisplayMetrics());
                     }
                 }
             }
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public int getInteger(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static int getInteger(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -584,15 +596,15 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return 0;
     }
 
-    @Override
-    public boolean getBoolean(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static boolean getBoolean(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resValue = value.getSecond();
@@ -606,61 +618,62 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return false;
     }
 
-    @Override
-    public String getResourceEntryName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceEntryName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getResourceName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getResourceTypeName(int resid) throws NotFoundException {
+    @LayoutlibDelegate
+    static String getResourceTypeName(Resources resources, int resid) throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public String getString(int id, Object... formatArgs) throws NotFoundException {
-        String s = getString(id);
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id, Object... formatArgs)
+            throws NotFoundException {
+        String s = getString(resources, id);
         if (s != null) {
             return String.format(s, formatArgs);
 
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public String getString(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static String getString(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null && value.getSecond().getValue() != null) {
             return value.getSecond().getValue();
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+    @LayoutlibDelegate
+    static void getValue(Resources resources, int id, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             ResourceValue resVal = value.getSecond();
@@ -673,7 +686,7 @@
                 }
                 if (resVal instanceof DensityBasedResourceValue) {
                     outValue.density =
-                      ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
+                            ((DensityBasedResourceValue) resVal).getResourceDensity().getDpiValue();
                 }
 
                 // else it's a string
@@ -684,18 +697,18 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
     }
 
-    @Override
-    public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+    @LayoutlibDelegate
+    static void getValue(Resources resources, String name, TypedValue outValue, boolean resolveRefs)
             throws NotFoundException {
         throw new UnsupportedOperationException();
     }
 
-    @Override
-    public XmlResourceParser getXml(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static XmlResourceParser getXml(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             String v = value.getSecond().getValue();
@@ -707,7 +720,8 @@
                     try {
                         XmlPullParser parser = ParserFactory.create(f);
 
-                        return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+                        return new BridgeXmlBlockParser(parser, resources.mContext,
+                                mPlatformResourceFlag[0]);
                     } catch (XmlPullParserException e) {
                         NotFoundException newE = new NotFoundException();
                         newE.initCause(e);
@@ -722,25 +736,31 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public XmlResourceParser loadXmlResourceParser(String file, int id,
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, int id,
+            String type) throws NotFoundException {
+        return resources.loadXmlResourceParser_Original(id, type);
+    }
+
+    @LayoutlibDelegate
+    static XmlResourceParser loadXmlResourceParser(Resources resources, String file, int id,
             int assetCookie, String type) throws NotFoundException {
         // even though we know the XML file to load directly, we still need to resolve the
         // id so that we can know if it's a platform or project resource.
         // (mPlatformResouceFlag will get the result and will be used later).
-        getResourceValue(id, mPlatformResourceFlag);
+        getResourceValue(resources, id, mPlatformResourceFlag);
 
         File f = new File(file);
         try {
             XmlPullParser parser = ParserFactory.create(f);
 
-            return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+            return new BridgeXmlBlockParser(parser, resources.mContext, mPlatformResourceFlag[0]);
         } catch (XmlPullParserException e) {
             NotFoundException newE = new NotFoundException();
             newE.initCause(e);
@@ -752,9 +772,9 @@
         }
     }
 
-    @Override
-    public InputStream openRawResource(int id) throws NotFoundException {
-        Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id) throws NotFoundException {
+        Pair<String, ResourceValue> value = getResourceValue(resources, id, mPlatformResourceFlag);
 
         if (value != null) {
             String path = value.getSecond().getValue();
@@ -781,15 +801,16 @@
         }
 
         // id was not found or not resolved. Throw a NotFoundException.
-        throwException(id);
+        throwException(resources, id);
 
         // this is not used since the method above always throws
         return null;
     }
 
-    @Override
-    public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
-        getValue(id, value, true);
+    @LayoutlibDelegate
+    static InputStream openRawResource(Resources resources, int id, TypedValue value) throws
+            NotFoundException {
+        getValue(resources, id, value, true);
 
         String path = value.string.toString();
 
@@ -813,23 +834,27 @@
         throw new NotFoundException();
     }
 
-    @Override
-    public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+    @LayoutlibDelegate
+    static AssetFileDescriptor openRawResourceFd(Resources resources, int id) throws
+            NotFoundException {
         throw new UnsupportedOperationException();
     }
 
     /**
-     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
+     * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource
+     * type.
+     *
      * @param id the id of the resource
+     *
      * @throws NotFoundException
      */
-    private void throwException(int id) throws NotFoundException {
+    private static void throwException(Resources resources, int id) throws NotFoundException {
         // first get the String related to this id in the framework
         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
 
         // if the name is unknown in the framework, get it from the custom view loader.
-        if (resourceInfo == null && mLayoutlibCallback != null) {
-            resourceInfo = mLayoutlibCallback.resolveResourceId(id);
+        if (resourceInfo == null && resources.mLayoutlibCallback != null) {
+            resourceInfo = resources.mLayoutlibCallback.resolveResourceId(id);
         }
 
         String message;
@@ -845,7 +870,7 @@
         throw new NotFoundException(message);
     }
 
-    private int getInt(String v) throws NumberFormatException {
+    private static int getInt(String v) throws NumberFormatException {
         int radix = 10;
         if (v.startsWith("0x")) {
             v = v.substring(2);
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 8d5863b..8bd2a7a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -23,7 +23,7 @@
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
 import android.annotation.Nullable;
-import android.content.res.BridgeResources.NinePatchInputStream;
+import com.android.layoutlib.bridge.util.NinePatchInputStream;
 import android.graphics.BitmapFactory.Options;
 import android.graphics.Bitmap_Delegate.BitmapCreateFlags;
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
new file mode 100644
index 0000000..200fe3b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_Delegate.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License") {}
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.internal.view.animation.NativeInterpolatorFactoryHelper_Delegate;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+import android.graphics.drawable.VectorDrawable_Delegate.VFullPath_Delegate;
+import android.graphics.drawable.VectorDrawable_Delegate.VGroup_Delegate;
+import android.graphics.drawable.VectorDrawable_Delegate.VNativeObject;
+import android.graphics.drawable.VectorDrawable_Delegate.VPathRenderer_Delegate;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * AnimatedVectorDrawable}
+ * <p>
+ * Through the layoutlib_create tool, the original  methods of AnimatedVectorDrawable have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class AnimatedVectorDrawable_Delegate {
+    private static DelegateManager<AnimatorSetHolder> sAnimatorSets = new
+            DelegateManager<>(AnimatorSetHolder.class);
+    private static DelegateManager<PropertySetter> sHolders = new
+            DelegateManager<>(PropertySetter.class);
+
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateAnimatorSet() {
+        return sAnimatorSets.addNewDelegate(new AnimatorSetHolder());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nAddAnimator(long setPtr, long propertyValuesHolder,
+            long nativeInterpolator, long startDelay, long duration, int repeatCount) {
+        PropertySetter holder = sHolders.getDelegate(propertyValuesHolder);
+        if (holder == null || holder.getValues() == null) {
+            return;
+        }
+
+        ObjectAnimator animator = new ObjectAnimator();
+        animator.setValues(holder.getValues());
+        animator.setInterpolator(
+                NativeInterpolatorFactoryHelper_Delegate.getDelegate(nativeInterpolator));
+        animator.setStartDelay(startDelay);
+        animator.setDuration(duration);
+        animator.setRepeatCount(repeatCount);
+        animator.setTarget(holder);
+        animator.setPropertyName(holder.getValues().getPropertyName());
+
+        AnimatorSetHolder set = sAnimatorSets.getDelegate(setPtr);
+        assert set != null;
+        set.addAnimator(animator);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateGroupPropertyHolder(long nativePtr, int propertyId,
+            float startValue, float endValue) {
+        VGroup_Delegate group = VNativeObject.getDelegate(nativePtr);
+        Consumer<Float> setter = group.getPropertySetter(propertyId);
+
+        return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
+                endValue));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreatePathDataPropertyHolder(long nativePtr, long startValuePtr,
+            long endValuePtr) {
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED, "AnimatedVectorDrawable path " +
+                "animations are not supported.", null, null);
+        return 0;
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreatePathColorPropertyHolder(long nativePtr, int propertyId,
+            int startValue, int endValue) {
+        VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
+        Consumer<Integer> setter = path.getIntPropertySetter(propertyId);
+
+        return sHolders.addNewDelegate(IntPropertySetter.of(setter, startValue,
+                endValue));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreatePathPropertyHolder(long nativePtr, int propertyId,
+            float startValue, float endValue) {
+        VFullPath_Delegate path = VNativeObject.getDelegate(nativePtr);
+        Consumer<Float> setter = path.getFloatPropertySetter(propertyId);
+
+        return sHolders.addNewDelegate(FloatPropertySetter.of(setter, startValue,
+                endValue));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long nCreateRootAlphaPropertyHolder(long nativePtr, float startValue,
+            float endValue) {
+        VPathRenderer_Delegate renderer = VNativeObject.getDelegate(nativePtr);
+
+        return sHolders.addNewDelegate(FloatPropertySetter.of(renderer::setRootAlpha,
+                startValue,
+                endValue));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nSetPropertyHolderData(long nativePtr, float[] data, int length) {
+        PropertySetter setter = sHolders.getDelegate(nativePtr);
+        assert setter != null;
+
+        setter.setValues(data);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nStart(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+        assert animatorSet != null;
+
+        animatorSet.start();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nReverse(long animatorSetPtr, VectorDrawableAnimatorRT set, int id) {
+        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+        assert animatorSet != null;
+
+        animatorSet.reverse();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nEnd(long animatorSetPtr) {
+        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+        assert animatorSet != null;
+
+        animatorSet.end();
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static void nReset(long animatorSetPtr) {
+        AnimatorSetHolder animatorSet = sAnimatorSets.getDelegate(animatorSetPtr);
+        assert animatorSet != null;
+
+        animatorSet.end();
+        animatorSet.start();
+    }
+
+    private static class AnimatorSetHolder {
+        private ArrayList<Animator> mAnimators = new ArrayList<>();
+        private AnimatorSet mAnimatorSet = null;
+
+        private void addAnimator(@NonNull Animator animator) {
+            mAnimators.add(animator);
+        }
+
+        private void ensureAnimatorSet() {
+            if (mAnimatorSet == null) {
+                mAnimatorSet = new AnimatorSet();
+                mAnimatorSet.playTogether(mAnimators);
+            }
+        }
+
+        private void start() {
+            ensureAnimatorSet();
+
+            mAnimatorSet.start();
+        }
+
+        private void end() {
+            mAnimatorSet.end();
+        }
+
+        private void reset() {
+            end();
+            start();
+        }
+
+        private void reverse() {
+            mAnimatorSet.reverse();
+        }
+    }
+
+    /**
+     * Class that allows setting a value and holds the range of values for the given property.
+     *
+     * @param <T> the type of the property
+     */
+    private static class PropertySetter<T> {
+        final Consumer<T> mValueSetter;
+        private PropertyValuesHolder mValues;
+
+        private PropertySetter(@NonNull Consumer<T> valueSetter) {
+            mValueSetter = valueSetter;
+        }
+
+        /**
+         * Method to set an {@link Integer} value for this property. The default implementation of
+         * this method doesn't do anything. This method is accessed via reflection by the
+         * PropertyValuesHolder.
+         */
+        public void setIntValue(Integer value) {
+        }
+
+        /**
+         * Method to set an {@link Integer} value for this property. The default implementation of
+         * this method doesn't do anything. This method is accessed via reflection by the
+         * PropertyValuesHolder.
+         */
+        public void setFloatValue(Float value) {
+        }
+
+        void setValues(float... values) {
+            mValues = PropertyValuesHolder.ofFloat("floatValue", values);
+        }
+
+        @Nullable
+        PropertyValuesHolder getValues() {
+            return mValues;
+        }
+
+        void setValues(int... values) {
+            mValues = PropertyValuesHolder.ofInt("intValue", values);
+        }
+    }
+
+    private static class IntPropertySetter extends PropertySetter<Integer> {
+        private IntPropertySetter(Consumer<Integer> valueSetter) {
+            super(valueSetter);
+        }
+
+        private static PropertySetter of(Consumer<Integer> valueSetter, int... values) {
+            PropertySetter setter = new IntPropertySetter(valueSetter);
+            setter.setValues(values);
+
+            return setter;
+        }
+
+        public void setIntValue(Integer value) {
+            mValueSetter.accept(value);
+        }
+    }
+
+    private static class FloatPropertySetter extends PropertySetter<Float> {
+        private FloatPropertySetter(Consumer<Float> valueSetter) {
+            super(valueSetter);
+        }
+
+        private static PropertySetter of(Consumer<Float> valueSetter, float... values) {
+            PropertySetter setter = new FloatPropertySetter(valueSetter);
+            setter.setValues(values);
+
+            return setter;
+        }
+
+        public void setFloatValue(Float value) {
+            mValueSetter.accept(value);
+        }
+
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.java
new file mode 100644
index 0000000..3d78931
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate.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 android.graphics.drawable;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.graphics.drawable.AnimatedVectorDrawable.VectorDrawableAnimatorRT;
+
+public class AnimatedVectorDrawable_VectorDrawableAnimatorRT_Delegate {
+    @LayoutlibDelegate
+    /*package*/ static boolean useLastSeenTarget(VectorDrawableAnimatorRT thisDrawableAnimator) {
+        return true;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
index 2e3e7e8..90b84f8 100644
--- a/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/drawable/VectorDrawable_Delegate.java
@@ -19,6 +19,7 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.annotation.NonNull;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
@@ -44,6 +45,7 @@
 import java.nio.ByteOrder;
 import java.nio.FloatBuffer;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 import static android.graphics.Canvas.CLIP_SAVE_FLAG;
 import static android.graphics.Canvas.MATRIX_SAVE_FLAG;
@@ -68,14 +70,6 @@
     private static final DelegateManager<VNativeObject> sPathManager =
             new DelegateManager<>(VNativeObject.class);
 
-    private static <T> T getDelegate(long nativePtr) {
-        //noinspection unchecked
-        T object = (T) sPathManager.getDelegate(nativePtr);
-        assert object != null;
-
-        return object;
-    }
-
     /**
      * Obtains styled attributes from the theme, if available, or unstyled resources if the theme is
      * null.
@@ -97,21 +91,21 @@
 
     @LayoutlibDelegate
     static long nCreateRenderer(long rootGroupPtr) {
-        VGroup_Delegate rootGroup = getDelegate(rootGroupPtr);
+        VGroup_Delegate rootGroup = VNativeObject.getDelegate(rootGroupPtr);
         return sPathManager.addNewDelegate(new VPathRenderer_Delegate(rootGroup));
     }
 
     @LayoutlibDelegate
     static void nSetRendererViewportSize(long rendererPtr, float viewportWidth,
             float viewportHeight) {
-        VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
         nativePathRenderer.mViewportWidth = viewportWidth;
         nativePathRenderer.mViewportHeight = viewportHeight;
     }
 
     @LayoutlibDelegate
     static boolean nSetRootAlpha(long rendererPtr, float alpha) {
-        VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
         nativePathRenderer.setRootAlpha(alpha);
 
         return true;
@@ -119,7 +113,7 @@
 
     @LayoutlibDelegate
     static float nGetRootAlpha(long rendererPtr) {
-        VPathRenderer_Delegate nativePathRenderer = getDelegate(rendererPtr);
+        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
 
         return nativePathRenderer.getRootAlpha();
     }
@@ -132,8 +126,7 @@
     @LayoutlibDelegate
     static void nDraw(long rendererPtr, long canvasWrapperPtr,
             long colorFilterPtr, Rect bounds, boolean needsMirroring, boolean canReuseCache) {
-        VPathRenderer_Delegate nativePathRenderer =
-                getDelegate(rendererPtr);
+        VPathRenderer_Delegate nativePathRenderer = VNativeObject.getDelegate(rendererPtr);
 
         Canvas_Delegate.native_save(canvasWrapperPtr, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
         Canvas_Delegate.native_translate(canvasWrapperPtr, bounds.left, bounds.top);
@@ -159,7 +152,7 @@
 
     @LayoutlibDelegate
     static long nCreateFullPath(long nativeFullPathPtr) {
-        VFullPath_Delegate original = getDelegate(nativeFullPathPtr);
+        VFullPath_Delegate original = VNativeObject.getDelegate(nativeFullPathPtr);
 
         return sPathManager.addNewDelegate(new VFullPath_Delegate(original));
     }
@@ -167,7 +160,7 @@
     @LayoutlibDelegate
     static boolean nGetFullPathProperties(long pathPtr, byte[] propertiesData,
             int length) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
 
         ByteBuffer properties = ByteBuffer.wrap(propertiesData);
         properties.order(ByteOrder.nativeOrder());
@@ -194,7 +187,7 @@
             int strokeColor, float strokeAlpha, int fillColor, float fillAlpha, float trimPathStart,
             float trimPathEnd, float trimPathOffset, float strokeMiterLimit, int strokeLineCap,
             int strokeLineJoin) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
 
         path.setStrokeWidth(strokeWidth);
         path.setStrokeColor(strokeColor);
@@ -211,14 +204,14 @@
 
     @LayoutlibDelegate
     static void nUpdateFullPathFillGradient(long pathPtr, long fillGradientPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
 
         path.setFillGradient(fillGradientPtr);
     }
 
     @LayoutlibDelegate
     static void nUpdateFullPathStrokeGradient(long pathPtr, long strokeGradientPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
 
         path.setStrokeGradient(strokeGradientPtr);
     }
@@ -230,7 +223,7 @@
 
     @LayoutlibDelegate
     static long nCreateClipPath(long clipPathPtr) {
-        VClipPath_Delegate original = getDelegate(clipPathPtr);
+        VClipPath_Delegate original = VNativeObject.getDelegate(clipPathPtr);
         return sPathManager.addNewDelegate(new VClipPath_Delegate(original));
     }
 
@@ -241,21 +234,21 @@
 
     @LayoutlibDelegate
     static long nCreateGroup(long groupPtr) {
-        VGroup_Delegate original = getDelegate(groupPtr);
+        VGroup_Delegate original = VNativeObject.getDelegate(groupPtr);
         return sPathManager.addNewDelegate(
                 new VGroup_Delegate(original, new ArrayMap<String, Object>()));
     }
 
     @LayoutlibDelegate
     static void nSetName(long nodePtr, String name) {
-        VNativeObject group = getDelegate(nodePtr);
+        VNativeObject group = VNativeObject.getDelegate(nodePtr);
         group.setName(name);
     }
 
     @LayoutlibDelegate
     static boolean nGetGroupProperties(long groupPtr, float[] propertiesData,
             int length) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
 
         FloatBuffer properties = FloatBuffer.wrap(propertiesData);
 
@@ -272,7 +265,7 @@
     @LayoutlibDelegate
     static void nUpdateGroupProperties(long groupPtr, float rotate, float pivotX,
             float pivotY, float scaleX, float scaleY, float translateX, float translateY) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
 
         group.setRotation(rotate);
         group.setPivotX(pivotX);
@@ -285,13 +278,13 @@
 
     @LayoutlibDelegate
     static void nAddChild(long groupPtr, long nodePtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
-        group.mChildren.add(getDelegate(nodePtr));
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
+        group.mChildren.add(VNativeObject.getDelegate(nodePtr));
     }
 
     @LayoutlibDelegate
     static void nSetPathString(long pathPtr, String pathString, int length) {
-        VPath_Delegate path = getDelegate(pathPtr);
+        VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setPathData(PathParser_Delegate.createNodesFromPathData(pathString));
     }
 
@@ -304,187 +297,187 @@
     // Setters and getters during animation.
     @LayoutlibDelegate
     static float nGetRotation(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getRotation();
     }
 
     @LayoutlibDelegate
     static void nSetRotation(long groupPtr, float rotation) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setRotation(rotation);
     }
 
     @LayoutlibDelegate
     static float nGetPivotX(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getPivotX();
     }
 
     @LayoutlibDelegate
     static void nSetPivotX(long groupPtr, float pivotX) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setPivotX(pivotX);
     }
 
     @LayoutlibDelegate
     static float nGetPivotY(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getPivotY();
     }
 
     @LayoutlibDelegate
     static void nSetPivotY(long groupPtr, float pivotY) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setPivotY(pivotY);
     }
 
     @LayoutlibDelegate
     static float nGetScaleX(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getScaleX();
     }
 
     @LayoutlibDelegate
     static void nSetScaleX(long groupPtr, float scaleX) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setScaleX(scaleX);
     }
 
     @LayoutlibDelegate
     static float nGetScaleY(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getScaleY();
     }
 
     @LayoutlibDelegate
     static void nSetScaleY(long groupPtr, float scaleY) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setScaleY(scaleY);
     }
 
     @LayoutlibDelegate
     static float nGetTranslateX(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getTranslateX();
     }
 
     @LayoutlibDelegate
     static void nSetTranslateX(long groupPtr, float translateX) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setTranslateX(translateX);
     }
 
     @LayoutlibDelegate
     static float nGetTranslateY(long groupPtr) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         return group.getTranslateY();
     }
 
     @LayoutlibDelegate
     static void nSetTranslateY(long groupPtr, float translateY) {
-        VGroup_Delegate group = getDelegate(groupPtr);
+        VGroup_Delegate group = VNativeObject.getDelegate(groupPtr);
         group.setTranslateY(translateY);
     }
 
     @LayoutlibDelegate
     static void nSetPathData(long pathPtr, long pathDataPtr) {
-        VPath_Delegate path = getDelegate(pathPtr);
+        VPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setPathData(PathParser_Delegate.getDelegate(pathDataPtr).getPathDataNodes());
     }
 
     @LayoutlibDelegate
     static float nGetStrokeWidth(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getStrokeWidth();
     }
 
     @LayoutlibDelegate
     static void nSetStrokeWidth(long pathPtr, float width) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setStrokeWidth(width);
     }
 
     @LayoutlibDelegate
     static int nGetStrokeColor(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getStrokeColor();
     }
 
     @LayoutlibDelegate
     static void nSetStrokeColor(long pathPtr, int strokeColor) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setStrokeColor(strokeColor);
     }
 
     @LayoutlibDelegate
     static float nGetStrokeAlpha(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getStrokeAlpha();
     }
 
     @LayoutlibDelegate
     static void nSetStrokeAlpha(long pathPtr, float alpha) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setStrokeAlpha(alpha);
     }
 
     @LayoutlibDelegate
     static int nGetFillColor(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getFillColor();
     }
 
     @LayoutlibDelegate
     static void nSetFillColor(long pathPtr, int fillColor) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setFillColor(fillColor);
     }
 
     @LayoutlibDelegate
     static float nGetFillAlpha(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getFillAlpha();
     }
 
     @LayoutlibDelegate
     static void nSetFillAlpha(long pathPtr, float fillAlpha) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setFillAlpha(fillAlpha);
     }
 
     @LayoutlibDelegate
     static float nGetTrimPathStart(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getTrimPathStart();
     }
 
     @LayoutlibDelegate
     static void nSetTrimPathStart(long pathPtr, float trimPathStart) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setTrimPathStart(trimPathStart);
     }
 
     @LayoutlibDelegate
     static float nGetTrimPathEnd(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getTrimPathEnd();
     }
 
     @LayoutlibDelegate
     static void nSetTrimPathEnd(long pathPtr, float trimPathEnd) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setTrimPathEnd(trimPathEnd);
     }
 
     @LayoutlibDelegate
     static float nGetTrimPathOffset(long pathPtr) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         return path.getTrimPathOffset();
     }
 
     @LayoutlibDelegate
     static void nSetTrimPathOffset(long pathPtr, float trimPathOffset) {
-        VFullPath_Delegate path = getDelegate(pathPtr);
+        VFullPath_Delegate path = VNativeObject.getDelegate(pathPtr);
         path.setTrimPathOffset(trimPathOffset);
     }
 
@@ -496,7 +489,16 @@
      *     not need it
      * </ol>
      */
-    private interface VNativeObject {
+    interface VNativeObject {
+        @NonNull
+        static <T> T getDelegate(long nativePtr) {
+            //noinspection unchecked
+            T vNativeObject = (T) sPathManager.getDelegate(nativePtr);
+
+            assert vNativeObject != null;
+            return vNativeObject;
+        }
+
         void setName(String name);
     }
 
@@ -515,7 +517,7 @@
         }
     }
 
-    private static class VFullPath_Delegate extends VPath_Delegate {
+    static class VFullPath_Delegate extends VPath_Delegate {
         // These constants need to be kept in sync with their values in VectorDrawable.VFullPath
         private static final int STROKE_WIDTH_INDEX = 0;
         private static final int STROKE_COLOR_INDEX = 1;
@@ -537,6 +539,38 @@
         private static final int LINEJOIN_ROUND = 1;
         private static final int LINEJOIN_BEVEL = 2;
 
+        @NonNull
+        public Consumer<Float> getFloatPropertySetter(int propertyIdx) {
+            switch (propertyIdx) {
+                case STROKE_ALPHA_INDEX:
+                    return this::setStrokeAlpha;
+                case FILL_ALPHA_INDEX:
+                    return this::setFillAlpha;
+                case TRIM_PATH_START_INDEX:
+                    return this::setTrimPathStart;
+                case TRIM_PATH_END_INDEX:
+                    return this::setTrimPathEnd;
+                case TRIM_PATH_OFFSET_INDEX:
+                    return this::setTrimPathOffset;
+            }
+
+            throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
+                    + propertyIdx);
+        }
+
+        @NonNull
+        public Consumer<Integer> getIntPropertySetter(int propertyIdx) {
+            switch (propertyIdx) {
+                case STROKE_COLOR_INDEX:
+                    return this::setStrokeColor;
+                case FILL_COLOR_INDEX:
+                    return this::setFillColor;
+            }
+
+            throw new IllegalArgumentException("Invalid VFullPath_Delegate property index "
+                    + propertyIdx);
+        }
+
         /////////////////////////////////////////////////////
         // Variables below need to be copied (deep copy if applicable) for mutation.
 
@@ -723,7 +757,7 @@
         }
     }
 
-    private static class VGroup_Delegate implements VNativeObject {
+    static class VGroup_Delegate implements VNativeObject {
         // This constants need to be kept in sync with their definitions in VectorDrawable.Group
         private static final int ROTATE_INDEX = 0;
         private static final int PIVOT_X_INDEX = 1;
@@ -733,6 +767,28 @@
         private static final int TRANSLATE_X_INDEX = 5;
         private static final int TRANSLATE_Y_INDEX = 6;
 
+        public Consumer<Float> getPropertySetter(int propertyIdx) {
+            switch (propertyIdx) {
+                case ROTATE_INDEX:
+                    return this::setRotation;
+                case PIVOT_X_INDEX:
+                    return this::setPivotX;
+                case PIVOT_Y_INDEX:
+                    return this::setPivotY;
+                case SCALE_X_INDEX:
+                    return this::setScaleX;
+                case SCALE_Y_INDEX:
+                    return this::setScaleY;
+                case TRANSLATE_X_INDEX:
+                    return this::setTranslateX;
+                case TRANSLATE_Y_INDEX:
+                    return this::setTranslateY;
+            }
+
+            throw new IllegalArgumentException("Invalid VGroup_Delegate property index "
+                    + propertyIdx);
+        }
+
         /////////////////////////////////////////////////////
         // Variables below need to be copied (deep copy if applicable) for mutation.
         final ArrayList<Object> mChildren = new ArrayList<>();
@@ -931,7 +987,7 @@
         }
     }
 
-    private static class VPathRenderer_Delegate implements VNativeObject {
+    static class VPathRenderer_Delegate implements VNativeObject {
         /* Right now the internal data structure is organized as a tree.
          * Each node can be a group node, or a path.
          * A group node can have groups or paths as children, but a path node has
@@ -967,7 +1023,7 @@
             return mRootAlpha;
         }
 
-        private void setRootAlpha(float alpha) {
+        void setRootAlpha(float alpha) {
             mRootAlpha = alpha;
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
new file mode 100644
index 0000000..0f39e80
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/animation/NativeInterpolatorFactoryHelper_Delegate.java
@@ -0,0 +1,128 @@
+/*
+ * 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.internal.view.animation;
+
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.MathUtils;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.AnticipateInterpolator;
+import android.view.animation.AnticipateOvershootInterpolator;
+import android.view.animation.BaseInterpolator;
+import android.view.animation.BounceInterpolator;
+import android.view.animation.CycleInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+import android.view.animation.OvershootInterpolator;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link
+ * NativeInterpolatorFactoryHelper}
+ * <p>
+ * Through the layoutlib_create tool, the original  methods of NativeInterpolatorFactoryHelper have
+ * been replaced by calls to methods of the same name in this delegate class.
+ */
+@SuppressWarnings("unused")
+public class NativeInterpolatorFactoryHelper_Delegate {
+    private static final DelegateManager<Interpolator> sManager = new DelegateManager<>
+            (Interpolator.class);
+
+    public static Interpolator getDelegate(long nativePtr) {
+        return sManager.getDelegate(nativePtr);
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createAccelerateDecelerateInterpolator() {
+        return sManager.addNewDelegate(new AccelerateDecelerateInterpolator());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createAccelerateInterpolator(float factor) {
+        return sManager.addNewDelegate(new AccelerateInterpolator(factor));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createAnticipateInterpolator(float tension) {
+        return sManager.addNewDelegate(new AnticipateInterpolator(tension));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createAnticipateOvershootInterpolator(float tension) {
+        return sManager.addNewDelegate(new AnticipateOvershootInterpolator(tension));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createBounceInterpolator() {
+        return sManager.addNewDelegate(new BounceInterpolator());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createCycleInterpolator(float cycles) {
+        return sManager.addNewDelegate(new CycleInterpolator(cycles));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createDecelerateInterpolator(float factor) {
+        return sManager.addNewDelegate(new DecelerateInterpolator(factor));
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createLinearInterpolator() {
+        return sManager.addNewDelegate(new LinearInterpolator());
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createOvershootInterpolator(float tension) {
+        return sManager.addNewDelegate(new OvershootInterpolator(tension));
+    }
+
+    private static class LutInterpolator extends BaseInterpolator {
+        private final float[] mValues;
+        private final int mSize;
+
+        private LutInterpolator(float[] values) {
+            mValues = values;
+            mSize = mValues.length;
+        }
+
+        @Override
+        public float getInterpolation(float input) {
+            float lutpos = input * mSize;
+            if (lutpos >= (mSize - 1)) {
+                return mValues[mSize - 1];
+            }
+
+            int ipart = (int) lutpos;
+            float weight = lutpos - ipart;
+
+            int i1 = ipart;
+            int i2 = Math.min(i1 + 1, mSize - 1);
+
+            assert i1 >= 0 && i2 >= 0 : "Negatives in the interpolation";
+
+            return MathUtils.lerp(mValues[i1], mValues[i2], weight);
+        }
+    }
+
+    @LayoutlibDelegate
+    /*package*/ static long createLutInterpolator(float[] values) {
+        return sManager.addNewDelegate(new LutInterpolator(values));
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 17ab2ff5..c7ae6fc 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -52,11 +52,11 @@
 import android.content.pm.PackageManager;
 import android.content.res.AssetManager;
 import android.content.res.BridgeAssetManager;
-import android.content.res.BridgeResources;
 import android.content.res.BridgeTypedArray;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
+import android.content.res.Resources_Delegate;
 import android.database.DatabaseErrorHandler;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
@@ -168,8 +168,8 @@
         RTL_ATTRS.put("?android:attr/paddingRight", "paddingEnd");
         RTL_ATTRS.put("?android:attr/layout_marginLeft", "layout_marginStart");
         RTL_ATTRS.put("?android:attr/layout_marginRight", "layout_marginEnd");
-        RTL_ATTRS.put("?android:attr/layout_toLeft", "layout_toStartOf");
-        RTL_ATTRS.put("?android:attr/layout_toRight", "layout_toEndOf");
+        RTL_ATTRS.put("?android:attr/layout_toLeftOf", "layout_toStartOf");
+        RTL_ATTRS.put("?android:attr/layout_toRightOf", "layout_toEndOf");
         RTL_ATTRS.put("?android:attr/layout_alignParentLeft", "layout_alignParentStart");
         RTL_ATTRS.put("?android:attr/layout_alignParentRight", "layout_alignParentEnd");
         RTL_ATTRS.put("?android:attr/drawableLeft", "drawableStart");
@@ -224,7 +224,7 @@
     public void initResources() {
         AssetManager assetManager = AssetManager.getSystem();
 
-        mSystemResources = BridgeResources.initSystem(
+        mSystemResources = Resources_Delegate.initSystem(
                 this,
                 assetManager,
                 mMetrics,
@@ -237,7 +237,7 @@
      * Disposes the {@link Resources} singleton.
      */
     public void disposeResources() {
-        BridgeResources.disposeSystem();
+        Resources_Delegate.disposeSystem();
     }
 
     public void setBridgeInflater(BridgeInflater inflater) {
@@ -706,8 +706,8 @@
 
         List<Pair<String, Boolean>> attributeList = searchAttrs(attrs);
 
-        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
-                isPlatformFile);
+        BridgeTypedArray ta =
+                Resources_Delegate.newTypeArray(mSystemResources, attrs.length, isPlatformFile);
 
         // look for a custom style.
         String customStyle = null;
@@ -941,7 +941,7 @@
 
         List<Pair<String, Boolean>> attributes = searchAttrs(attrs);
 
-        BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
+        BridgeTypedArray ta = Resources_Delegate.newTypeArray(mSystemResources, attrs.length,
                 false);
 
         // for each attribute, get its name so that we can search it in the style
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
new file mode 100644
index 0000000..96b795a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/NinePatchInputStream.java
@@ -0,0 +1,50 @@
+/*
+ * 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.layoutlib.bridge.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+
+/**
+ * Simpler wrapper around FileInputStream. This is used when the input stream represent
+ * not a normal bitmap but a nine patch.
+ * This is useful when the InputStream is created in a method but used in another that needs
+ * to know whether this is 9-patch or not, such as BitmapFactory.
+ */
+public class NinePatchInputStream extends FileInputStream {
+    private boolean mFakeMarkSupport = true;
+    public NinePatchInputStream(File file) throws FileNotFoundException {
+        super(file);
+    }
+
+    @Override
+    public boolean markSupported() {
+        if (mFakeMarkSupport) {
+            // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
+            return true;
+        }
+
+        return super.markSupported();
+    }
+
+    public void disableFakeMarkSupport() {
+        // disable fake mark support so that in case codec actually try to use them
+        // we don't lie to them.
+        mFakeMarkSupport = false;
+    }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 5b99a6b..3b37612 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -367,6 +367,10 @@
 
         ClassVisitor cv = cw;
 
+        // FIXME Generify
+        if ("android/content/res/Resources".equals(className)) {
+            cv = new FieldInjectorAdapter(cv);
+        }
         if (mReplaceMethodCallsClasses.contains(className)) {
             cv = new ReplaceMethodCallsAdapter(cv, className);
         }
@@ -445,4 +449,5 @@
         }
         return buffer.toByteArray();
     }
+
 }
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 8c3bd2f..bd37665 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -155,6 +155,31 @@
      */
     public final static String[] DELEGATE_METHODS = new String[] {
         "android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
+        "android.content.res.Resources#getAnimation",
+        "android.content.res.Resources#getBoolean",
+        "android.content.res.Resources#getColor",
+        "android.content.res.Resources#getColorStateList",
+        "android.content.res.Resources#getDimension",
+        "android.content.res.Resources#getDimensionPixelOffset",
+        "android.content.res.Resources#getDimensionPixelSize",
+        "android.content.res.Resources#getDrawable",
+        "android.content.res.Resources#getIntArray",
+        "android.content.res.Resources#getInteger",
+        "android.content.res.Resources#getLayout",
+        "android.content.res.Resources#getResourceEntryName",
+        "android.content.res.Resources#getResourceName",
+        "android.content.res.Resources#getResourceTypeName",
+        "android.content.res.Resources#getString",
+        "android.content.res.Resources#getStringArray",
+        "android.content.res.Resources#getText",
+        "android.content.res.Resources#getTextArray",
+        "android.content.res.Resources#getValue",
+        "android.content.res.Resources#getXml",
+        "android.content.res.Resources#loadXmlResourceParser",
+        "android.content.res.Resources#obtainAttributes",
+        "android.content.res.Resources#obtainTypedArray",
+        "android.content.res.Resources#openRawResource",
+        "android.content.res.Resources#openRawResourceFd",
         "android.content.res.Resources$Theme#obtainStyledAttributes",
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.content.res.Resources$Theme#resolveAttributes",
@@ -164,6 +189,7 @@
         "android.content.res.TypedArray#obtain",
         "android.graphics.BitmapFactory#finishDecode",
         "android.graphics.BitmapFactory#setDensityFromOptions",
+        "android.graphics.drawable.AnimatedVectorDrawable$VectorDrawableAnimatorRT#useLastSeenTarget",
         "android.graphics.drawable.GradientDrawable#buildRing",
         "android.graphics.Typeface#getSystemFontConfigLocation",
         "android.graphics.Typeface#makeFamilyFromParsed",
@@ -269,6 +295,7 @@
         "android.graphics.SweepGradient",
         "android.graphics.Typeface",
         "android.graphics.Xfermode",
+        "android.graphics.drawable.AnimatedVectorDrawable",
         "android.graphics.drawable.VectorDrawable",
         "android.os.SystemClock",
         "android.os.SystemProperties",
@@ -276,6 +303,7 @@
         "android.text.StaticLayout",
         "android.util.PathParser",
         "android.view.Display",
+        "com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
         "libcore.icu.ICU",
     };
 
@@ -313,7 +341,7 @@
             // Use android.icu.text versions of DateFormat and SimpleDateFormat since the
             // original ones do not match the Android implementation
             "java.text.DateFormat",                            "android.icu.text.DateFormat",
-            "java.text.SimpleDateFormat",                      "android.icu.text.SimpleDateFormat"
+            "java.text.SimpleDateFormat",                      "android.icu.text.SimpleDateFormat",
         };
 
     private final static String[] EXCLUDED_CLASSES =
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
new file mode 100644
index 0000000..4608a84
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/FieldInjectorAdapter.java
@@ -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.
+ */
+
+package com.android.tools.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Injects fields in a class.
+ * <p>
+ * TODO: Generify
+ */
+public class FieldInjectorAdapter extends ClassVisitor {
+    public FieldInjectorAdapter(ClassVisitor cv) {
+        super(Opcodes.ASM4, cv);
+    }
+
+    @Override
+    public void visitEnd() {
+        super.visitField(Opcodes.ACC_PUBLIC, "mLayoutlibCallback",
+                "Lcom/android/ide/common/rendering/api/LayoutlibCallback;", null, null);
+        super.visitField(Opcodes.ACC_PUBLIC, "mContext",
+                "Lcom/android/layoutlib/bridge/android/BridgeContext;", null, null);
+        super.visitEnd();
+    }
+}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index a46aaec..a9259fa 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -305,9 +305,12 @@
      */
     public static class InformationElement {
         public static final int EID_SSID = 0;
+        public static final int EID_SUPPORTED_RATES = 1;
         public static final int EID_TIM = 5;
         public static final int EID_BSS_LOAD = 11;
+        public static final int EID_ERP = 42;
         public static final int EID_RSN = 48;
+        public static final int EID_EXTENDED_SUPPORTED_RATES = 50;
         public static final int EID_HT_OPERATION = 61;
         public static final int EID_INTERWORKING = 107;
         public static final int EID_ROAMING_CONSORTIUM = 111;
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 97dd985..c5e7bff 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -169,6 +169,13 @@
         public int band;
         /** list of channels; used when band is set to WIFI_BAND_UNSPECIFIED */
         public ChannelSpec[] channels;
+        /**
+         * list of networkId's of hidden networks to scan for.
+         * These Id's should correspond to the wpa_supplicant's networkId's and will be used
+         * in connectivity scans using wpa_supplicant.
+         * {@hide}
+         * */
+        public int[] hiddenNetworkIds;
         /** period of background scan; in millisecond, 0 => single shot scan */
         public int periodInMs;
         /** must have a valid REPORT_EVENT value */
@@ -213,10 +220,8 @@
             dest.writeInt(maxPeriodInMs);
             dest.writeInt(stepCount);
             dest.writeInt(isPnoScan ? 1 : 0);
-
             if (channels != null) {
                 dest.writeInt(channels.length);
-
                 for (int i = 0; i < channels.length; i++) {
                     dest.writeInt(channels[i].frequency);
                     dest.writeInt(channels[i].dwellTimeMS);
@@ -225,13 +230,13 @@
             } else {
                 dest.writeInt(0);
             }
+            dest.writeIntArray(hiddenNetworkIds);
         }
 
         /** Implement the Parcelable interface {@hide} */
         public static final Creator<ScanSettings> CREATOR =
                 new Creator<ScanSettings>() {
                     public ScanSettings createFromParcel(Parcel in) {
-
                         ScanSettings settings = new ScanSettings();
                         settings.band = in.readInt();
                         settings.periodInMs = in.readInt();
@@ -245,13 +250,12 @@
                         settings.channels = new ChannelSpec[num_channels];
                         for (int i = 0; i < num_channels; i++) {
                             int frequency = in.readInt();
-
                             ChannelSpec spec = new ChannelSpec(frequency);
                             spec.dwellTimeMS = in.readInt();
                             spec.passive = in.readInt() == 1;
                             settings.channels[i] = spec;
                         }
-
+                        settings.hiddenNetworkIds = in.createIntArray();
                         return settings;
                     }