Merge "ExifInterface: inform the developers to close their FD/streams" into nyc-dev
diff --git a/Android.mk b/Android.mk
index fc9c319..ae5d67e 100644
--- a/Android.mk
+++ b/Android.mk
@@ -244,6 +244,8 @@
 	core/java/android/service/notification/IConditionListener.aidl \
 	core/java/android/service/notification/IConditionProvider.aidl \
 	core/java/android/service/vr/IVrListener.aidl \
+	core/java/android/service/vr/IVrManager.aidl \
+	core/java/android/service/vr/IVrStateCallbacks.aidl \
 	core/java/android/print/ILayoutResultCallback.aidl \
 	core/java/android/print/IPrinterDiscoveryObserver.aidl \
 	core/java/android/print/IPrintDocumentAdapter.aidl \
diff --git a/api/current.txt b/api/current.txt
index f4efc92..3444597 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4915,6 +4915,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
@@ -4923,12 +4924,14 @@
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
     field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -4937,6 +4940,7 @@
     field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
     field public static final java.lang.String EXTRA_TEXT = "android.text";
     field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_THREAD_TITLE = "android.threadTitle";
     field public static final java.lang.String EXTRA_TITLE = "android.title";
     field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
@@ -4979,7 +4983,7 @@
     field public int ledARGB;
     field public int ledOffMS;
     field public int ledOnMS;
-    field public int number;
+    field public deprecated int number;
     field public int priority;
     field public android.app.Notification publicVersion;
     field public android.net.Uri sound;
@@ -5026,11 +5030,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -5072,7 +5078,7 @@
     method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
-    method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5089,7 +5095,7 @@
     method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.Builder setLights(int, int, int);
     method public android.app.Notification.Builder setLocalOnly(boolean);
-    method public android.app.Notification.Builder setNumber(int);
+    method public deprecated android.app.Notification.Builder setNumber(int);
     method public android.app.Notification.Builder setOngoing(boolean);
     method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
     method public android.app.Notification.Builder setPriority(int);
@@ -5174,6 +5180,32 @@
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
 
+  public static class Notification.MessagingStyle extends android.app.Notification.Style {
+    ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
+    method public boolean getAllowGeneratedReplies();
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.app.Notification.MessagingStyle setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class Notification.MessagingStyle.Message implements android.os.Parcelable {
+    ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public int describeContents();
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.app.Notification.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.MessagingStyle.Message> CREATOR;
+  }
+
   public static abstract class Notification.Style {
     ctor public Notification.Style();
     method public android.app.Notification build();
@@ -5207,6 +5239,7 @@
     method public android.app.PendingIntent getDisplayIntent();
     method public int getGravity();
     method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public boolean getHintHideIcon();
     method public int getHintScreenTimeout();
     method public boolean getHintShowBackgroundOnly();
@@ -5222,6 +5255,7 @@
     method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
     method public android.app.Notification.WearableExtender setGravity(int);
     method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
     method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
     method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -5921,7 +5955,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
-    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -7669,8 +7703,6 @@
     method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
-    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
-    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -7885,6 +7917,7 @@
     method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+    method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -7915,6 +7948,8 @@
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -8650,6 +8685,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -8661,6 +8697,7 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -13888,11 +13925,11 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createCaptureSessionByOutputConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createReprocessableCaptureSessionWithConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -19214,7 +19251,7 @@
     field public static final int ADR_STATE_VALID = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR;
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
     field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
     field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
     field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
@@ -19234,21 +19271,20 @@
   }
 
   public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    ctor public GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]);
     method public int describeContents();
     method public android.location.GnssClock getClock();
     method public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public static abstract class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
     method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public final class GnssNavigationMessage implements android.os.Parcelable {
@@ -19276,36 +19312,24 @@
     field public static final int TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+  public static abstract class GnssNavigationMessage.Callback {
+    ctor public GnssNavigationMessage.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessage);
+    method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
     field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
     field public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
-    method public int getNumSatellites();
+    method public int getSatelliteCount();
     method public int getSvid(int);
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
+    method public boolean hasAlmanacData(int);
+    method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
     field public static final int CONSTELLATION_GALILEO = 6; // 0x6
@@ -19316,15 +19340,15 @@
     field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
   }
 
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
+  public static abstract class GnssStatus.Callback {
+    ctor public GnssStatus.Callback();
     method public void onFirstFix(int);
     method public void onSatelliteStatusChanged(android.location.GnssStatus);
     method public void onStarted();
     method public void onStopped();
   }
 
-  public final class GpsSatellite {
+  public final deprecated class GpsSatellite {
     method public float getAzimuth();
     method public float getElevation();
     method public int getPrn();
@@ -19334,7 +19358,7 @@
     method public boolean usedInFix();
   }
 
-  public final class GpsStatus {
+  public final deprecated class GpsStatus {
     method public int getMaxSatellites();
     method public java.lang.Iterable<android.location.GpsSatellite> getSatellites();
     method public int getTimeToFirstFix();
@@ -19344,11 +19368,11 @@
     field public static final int GPS_EVENT_STOPPED = 2; // 0x2
   }
 
-  public static abstract interface GpsStatus.Listener {
+  public static abstract deprecated interface GpsStatus.Listener {
     method public abstract void onGpsStatusChanged(int);
   }
 
-  public static abstract interface GpsStatus.NmeaListener {
+  public static abstract deprecated interface GpsStatus.NmeaListener {
     method public abstract void onNmeaReceived(long, java.lang.String);
   }
 
@@ -19410,8 +19434,8 @@
   public class LocationManager {
     method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -19427,13 +19451,13 @@
     method public boolean isProviderEnabled(java.lang.String);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler);
     method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void removeNmeaListener(android.location.OnNmeaMessageListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -19452,8 +19476,8 @@
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -19482,6 +19506,10 @@
     field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
   }
 
+  public abstract interface OnNmeaMessageListener {
+    method public abstract void onNmeaMessage(java.lang.String, long);
+  }
+
   public abstract class SettingInjectorService extends android.app.Service {
     ctor public SettingInjectorService(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -20073,7 +20101,6 @@
   }
 
   public abstract class DrmInitData {
-    ctor public DrmInitData();
     method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
   }
 
@@ -23452,6 +23479,7 @@
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static deprecated boolean isNetworkTypeValid(int);
+    method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
     method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -34685,6 +34713,7 @@
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
+    method public void onListenerDisconnected();
     method public void onListenerHintsChanged(int);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
@@ -34696,7 +34725,9 @@
     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 int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -34712,6 +34743,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -34742,13 +34774,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -34784,7 +34819,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
@@ -34792,8 +34827,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -46435,21 +46469,21 @@
     ctor public DatePicker(android.content.Context, android.util.AttributeSet);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public android.widget.CalendarView getCalendarView();
-    method public boolean getCalendarViewShown();
+    method public deprecated android.widget.CalendarView getCalendarView();
+    method public deprecated boolean getCalendarViewShown();
     method public int getDayOfMonth();
     method public int getFirstDayOfWeek();
     method public long getMaxDate();
     method public long getMinDate();
     method public int getMonth();
-    method public boolean getSpinnersShown();
+    method public deprecated boolean getSpinnersShown();
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
-    method public void setCalendarViewShown(boolean);
+    method public deprecated void setCalendarViewShown(boolean);
     method public void setFirstDayOfWeek(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
-    method public void setSpinnersShown(boolean);
+    method public deprecated void setSpinnersShown(boolean);
     method public void updateDate(int, int, int);
   }
 
@@ -50022,6 +50056,9 @@
     method public int compareTo(java.lang.Boolean);
     method public static boolean getBoolean(java.lang.String);
     method public static int hashCode(boolean);
+    method public static boolean logicalAnd(boolean, boolean);
+    method public static boolean logicalOr(boolean, boolean);
+    method public static boolean logicalXor(boolean, boolean);
     method public static boolean parseBoolean(java.lang.String);
     method public static java.lang.String toString(boolean);
     method public static java.lang.Boolean valueOf(boolean);
@@ -50985,6 +51022,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -50994,12 +51033,20 @@
     method public static float copySign(float, float);
     method public static double cos(double);
     method public static double cosh(double);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
     method public static double log(double);
     method public static double log10(double);
     method public static double log1p(double);
@@ -51011,8 +51058,14 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -51027,9 +51080,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -51313,6 +51369,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -51325,6 +51383,10 @@
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
@@ -51339,8 +51401,12 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -51355,9 +51421,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -51915,7 +51984,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -51929,7 +51997,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
@@ -57405,6 +57472,14 @@
     method public static int hashCode(float[]);
     method public static int hashCode(double[]);
     method public static int hashCode(java.lang.Object[]);
+    method public static void parallelPrefix(T[], java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(T[], int, int, java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(long[], java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(long[], int, int, java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(double[], java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(double[], int, int, java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(int[], java.util.function.IntBinaryOperator);
+    method public static void parallelPrefix(int[], int, int, java.util.function.IntBinaryOperator);
     method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
     method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
     method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
diff --git a/api/removed.txt b/api/removed.txt
index 3f16bca..8c6abdc 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -84,6 +84,67 @@
 
 }
 
+package android.location {
+
+  public final class GnssMeasurement implements android.os.Parcelable {
+    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+  }
+
+  public final class GnssMeasurementsEvent implements android.os.Parcelable {
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
+    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
+    method public int describeContents();
+    method public android.location.GnssNavigationMessage getNavigationMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GnssNavigationMessageEvent.Callback {
+    ctor public GnssNavigationMessageEvent.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public int getNumSatellites();
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public class LocationManager {
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/system-current.txt b/api/system-current.txt
index 521b853..a4927f2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5048,6 +5048,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
@@ -5056,12 +5057,14 @@
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
     field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -5070,8 +5073,10 @@
     field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
     field public static final java.lang.String EXTRA_TEXT = "android.text";
     field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_THREAD_TITLE = "android.threadTitle";
     field public static final java.lang.String EXTRA_TITLE = "android.title";
     field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
+    field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
     field public static final int FLAG_FOREGROUND_SERVICE = 64; // 0x40
     field public static final int FLAG_GROUP_SUMMARY = 512; // 0x200
@@ -5112,7 +5117,7 @@
     field public int ledARGB;
     field public int ledOffMS;
     field public int ledOnMS;
-    field public int number;
+    field public deprecated int number;
     field public int priority;
     field public android.app.Notification publicVersion;
     field public android.net.Uri sound;
@@ -5159,11 +5164,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -5205,7 +5212,7 @@
     method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
-    method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5222,7 +5229,7 @@
     method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.Builder setLights(int, int, int);
     method public android.app.Notification.Builder setLocalOnly(boolean);
-    method public android.app.Notification.Builder setNumber(int);
+    method public deprecated android.app.Notification.Builder setNumber(int);
     method public android.app.Notification.Builder setOngoing(boolean);
     method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
     method public android.app.Notification.Builder setPriority(int);
@@ -5307,6 +5314,32 @@
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
 
+  public static class Notification.MessagingStyle extends android.app.Notification.Style {
+    ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
+    method public boolean getAllowGeneratedReplies();
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.app.Notification.MessagingStyle setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class Notification.MessagingStyle.Message implements android.os.Parcelable {
+    ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public int describeContents();
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.app.Notification.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.MessagingStyle.Message> CREATOR;
+  }
+
   public static abstract class Notification.Style {
     ctor public Notification.Style();
     method public android.app.Notification build();
@@ -5340,6 +5373,7 @@
     method public android.app.PendingIntent getDisplayIntent();
     method public int getGravity();
     method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public boolean getHintHideIcon();
     method public int getHintScreenTimeout();
     method public boolean getHintShowBackgroundOnly();
@@ -5355,6 +5389,7 @@
     method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
     method public android.app.Notification.WearableExtender setGravity(int);
     method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
     method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
     method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -6070,7 +6105,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
     method public deprecated boolean setActiveProfileOwner(android.content.ComponentName, java.lang.String) throws java.lang.IllegalArgumentException;
-    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -7967,8 +8002,6 @@
     method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
-    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
-    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -8183,6 +8216,7 @@
     method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+    method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -8213,6 +8247,8 @@
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -8969,6 +9005,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -8982,6 +9019,7 @@
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
     field public static final java.lang.String EXTRA_EPHEMERAL_FAILURE = "android.intent.extra.EPHEMERAL_FAILURE";
     field public static final java.lang.String EXTRA_EPHEMERAL_SUCCESS = "android.intent.extra.EPHEMERAL_SUCCESS";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -14295,11 +14333,11 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createCaptureSessionByOutputConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createReprocessableCaptureSessionWithConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -20374,7 +20412,7 @@
     field public static final int ADR_STATE_VALID = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR;
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
     field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
     field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
     field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
@@ -20394,21 +20432,20 @@
   }
 
   public final class GnssMeasurementsEvent implements android.os.Parcelable {
-    ctor public GnssMeasurementsEvent(android.location.GnssClock, android.location.GnssMeasurement[]);
     method public int describeContents();
     method public android.location.GnssClock getClock();
     method public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public static abstract class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
     method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public final class GnssNavigationMessage implements android.os.Parcelable {
@@ -20436,36 +20473,24 @@
     field public static final int TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+  public static abstract class GnssNavigationMessage.Callback {
+    ctor public GnssNavigationMessage.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessage);
+    method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
     field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
     field public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
-    method public int getNumSatellites();
+    method public int getSatelliteCount();
     method public int getSvid(int);
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
+    method public boolean hasAlmanacData(int);
+    method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
     field public static final int CONSTELLATION_GALILEO = 6; // 0x6
@@ -20476,8 +20501,8 @@
     field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
   }
 
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
+  public static abstract class GnssStatus.Callback {
+    ctor public GnssStatus.Callback();
     method public void onFirstFix(int);
     method public void onSatelliteStatusChanged(android.location.GnssStatus);
     method public void onStarted();
@@ -20709,7 +20734,7 @@
     method public abstract void onStatusChanged(int);
   }
 
-  public final class GpsSatellite {
+  public final deprecated class GpsSatellite {
     method public float getAzimuth();
     method public float getElevation();
     method public int getPrn();
@@ -20719,7 +20744,7 @@
     method public boolean usedInFix();
   }
 
-  public final class GpsStatus {
+  public final deprecated class GpsStatus {
     method public int getMaxSatellites();
     method public java.lang.Iterable<android.location.GpsSatellite> getSatellites();
     method public int getTimeToFirstFix();
@@ -20729,11 +20754,11 @@
     field public static final int GPS_EVENT_STOPPED = 2; // 0x2
   }
 
-  public static abstract interface GpsStatus.Listener {
+  public static abstract deprecated interface GpsStatus.Listener {
     method public abstract void onGpsStatusChanged(int);
   }
 
-  public static abstract interface GpsStatus.NmeaListener {
+  public static abstract deprecated interface GpsStatus.NmeaListener {
     method public abstract void onNmeaReceived(long, java.lang.String);
   }
 
@@ -20817,8 +20842,8 @@
     method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -20834,15 +20859,15 @@
     method public boolean isProviderEnabled(java.lang.String);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler);
     method public deprecated void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
     method public deprecated void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void removeNmeaListener(android.location.OnNmeaMessageListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -20863,8 +20888,8 @@
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -20927,6 +20952,10 @@
     field public static final int POWER_NONE = 200; // 0xc8
   }
 
+  public abstract interface OnNmeaMessageListener {
+    method public abstract void onNmeaMessage(java.lang.String, long);
+  }
+
   public abstract class SettingInjectorService extends android.app.Service {
     ctor public SettingInjectorService(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -21552,7 +21581,6 @@
   }
 
   public abstract class DrmInitData {
-    ctor public DrmInitData();
     method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
   }
 
@@ -25188,6 +25216,7 @@
     method public boolean isDefaultNetworkActive();
     method public static deprecated boolean isNetworkTypeValid(int);
     method public boolean isTetheringSupported();
+    method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
     method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -37091,6 +37120,22 @@
 
 package android.service.notification {
 
+  public final class Adjustment implements android.os.Parcelable {
+    ctor public Adjustment(java.lang.String, java.lang.String, int, android.os.Bundle, java.lang.CharSequence, android.net.Uri);
+    ctor protected Adjustment(android.os.Parcel);
+    method public int describeContents();
+    method public java.lang.CharSequence getExplanation();
+    method public int getImportance();
+    method public java.lang.String getKey();
+    method public java.lang.String getPackage();
+    method public android.net.Uri getReference();
+    method public android.os.Bundle getSignals();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
+    field public static final java.lang.String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
+    field public static final java.lang.String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
+  }
+
   public class Condition implements android.os.Parcelable {
     ctor public Condition(android.net.Uri, java.lang.String, int);
     ctor public Condition(android.net.Uri, java.lang.String, java.lang.String, java.lang.String, int, int, int);
@@ -37151,6 +37196,7 @@
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
+    method public void onListenerDisconnected();
     method public void onListenerHintsChanged(int);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
@@ -37165,7 +37211,9 @@
     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 int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -37183,6 +37231,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -37206,11 +37255,12 @@
 
   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 void adjustNotification(android.service.notification.Adjustment);
+    method public final void adjustNotifications(java.util.List<android.service.notification.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 abstract android.service.notification.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
@@ -37227,14 +37277,11 @@
     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_UNAUTOBUNDLED = 16; // 0x10
     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);
@@ -37244,13 +37291,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -37319,7 +37369,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void setStatusIcon(android.graphics.drawable.Icon, java.lang.String);
@@ -37328,8 +37378,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -49532,21 +49581,21 @@
     ctor public DatePicker(android.content.Context, android.util.AttributeSet);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public android.widget.CalendarView getCalendarView();
-    method public boolean getCalendarViewShown();
+    method public deprecated android.widget.CalendarView getCalendarView();
+    method public deprecated boolean getCalendarViewShown();
     method public int getDayOfMonth();
     method public int getFirstDayOfWeek();
     method public long getMaxDate();
     method public long getMinDate();
     method public int getMonth();
-    method public boolean getSpinnersShown();
+    method public deprecated boolean getSpinnersShown();
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
-    method public void setCalendarViewShown(boolean);
+    method public deprecated void setCalendarViewShown(boolean);
     method public void setFirstDayOfWeek(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
-    method public void setSpinnersShown(boolean);
+    method public deprecated void setSpinnersShown(boolean);
     method public void updateDate(int, int, int);
   }
 
@@ -53119,6 +53168,9 @@
     method public int compareTo(java.lang.Boolean);
     method public static boolean getBoolean(java.lang.String);
     method public static int hashCode(boolean);
+    method public static boolean logicalAnd(boolean, boolean);
+    method public static boolean logicalOr(boolean, boolean);
+    method public static boolean logicalXor(boolean, boolean);
     method public static boolean parseBoolean(java.lang.String);
     method public static java.lang.String toString(boolean);
     method public static java.lang.Boolean valueOf(boolean);
@@ -54082,6 +54134,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -54091,12 +54145,20 @@
     method public static float copySign(float, float);
     method public static double cos(double);
     method public static double cosh(double);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
     method public static double log(double);
     method public static double log10(double);
     method public static double log1p(double);
@@ -54108,8 +54170,14 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -54124,9 +54192,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -54410,6 +54481,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -54422,6 +54495,10 @@
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
@@ -54436,8 +54513,12 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -54452,9 +54533,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -55012,7 +55096,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -55026,7 +55109,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
@@ -60502,6 +60584,14 @@
     method public static int hashCode(float[]);
     method public static int hashCode(double[]);
     method public static int hashCode(java.lang.Object[]);
+    method public static void parallelPrefix(T[], java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(T[], int, int, java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(long[], java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(long[], int, int, java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(double[], java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(double[], int, int, java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(int[], java.util.function.IntBinaryOperator);
+    method public static void parallelPrefix(int[], int, int, java.util.function.IntBinaryOperator);
     method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
     method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
     method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 03cf8b0..95734c1 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -82,6 +82,67 @@
 
 }
 
+package android.location {
+
+  public final class GnssMeasurement implements android.os.Parcelable {
+    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+  }
+
+  public final class GnssMeasurementsEvent implements android.os.Parcelable {
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
+    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
+    method public int describeContents();
+    method public android.location.GnssNavigationMessage getNavigationMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GnssNavigationMessageEvent.Callback {
+    ctor public GnssNavigationMessageEvent.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public int getNumSatellites();
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public class LocationManager {
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 7ae0f97..4179948 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4915,6 +4915,7 @@
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
     field public static final int DEFAULT_SOUND = 1; // 0x1
     field public static final int DEFAULT_VIBRATE = 2; // 0x2
+    field public static final java.lang.String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
     field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
     field public static final java.lang.String EXTRA_BIG_TEXT = "android.bigText";
     field public static final java.lang.String EXTRA_CHRONOMETER_COUNTS_DOWN = "android.chronometerCountsDown";
@@ -4923,12 +4924,14 @@
     field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
     field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
     field public static final java.lang.String EXTRA_MEDIA_SESSION = "android.mediaSession";
+    field public static final java.lang.String EXTRA_MESSAGES = "android.messages";
     field public static final java.lang.String EXTRA_PEOPLE = "android.people";
     field public static final java.lang.String EXTRA_PICTURE = "android.picture";
     field public static final java.lang.String EXTRA_PROGRESS = "android.progress";
     field public static final java.lang.String EXTRA_PROGRESS_INDETERMINATE = "android.progressIndeterminate";
     field public static final java.lang.String EXTRA_PROGRESS_MAX = "android.progressMax";
     field public static final java.lang.String EXTRA_REMOTE_INPUT_HISTORY = "android.remoteInputHistory";
+    field public static final java.lang.String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
     field public static final java.lang.String EXTRA_SHOW_CHRONOMETER = "android.showChronometer";
     field public static final java.lang.String EXTRA_SHOW_WHEN = "android.showWhen";
     field public static final java.lang.String EXTRA_SMALL_ICON = "android.icon";
@@ -4937,6 +4940,7 @@
     field public static final java.lang.String EXTRA_TEMPLATE = "android.template";
     field public static final java.lang.String EXTRA_TEXT = "android.text";
     field public static final java.lang.String EXTRA_TEXT_LINES = "android.textLines";
+    field public static final java.lang.String EXTRA_THREAD_TITLE = "android.threadTitle";
     field public static final java.lang.String EXTRA_TITLE = "android.title";
     field public static final java.lang.String EXTRA_TITLE_BIG = "android.title.big";
     field public static final int FLAG_AUTO_CANCEL = 16; // 0x10
@@ -4979,7 +4983,7 @@
     field public int ledARGB;
     field public int ledOffMS;
     field public int ledOnMS;
-    field public int number;
+    field public deprecated int number;
     field public int priority;
     field public android.app.Notification publicVersion;
     field public android.net.Uri sound;
@@ -5026,11 +5030,13 @@
     method public android.app.Notification.Action.Builder extend(android.app.Notification.Action.Builder);
     method public java.lang.CharSequence getCancelLabel();
     method public java.lang.CharSequence getConfirmLabel();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public java.lang.CharSequence getInProgressLabel();
     method public boolean isAvailableOffline();
     method public android.app.Notification.Action.WearableExtender setAvailableOffline(boolean);
     method public android.app.Notification.Action.WearableExtender setCancelLabel(java.lang.CharSequence);
     method public android.app.Notification.Action.WearableExtender setConfirmLabel(java.lang.CharSequence);
+    method public android.app.Notification.Action.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.Action.WearableExtender setInProgressLabel(java.lang.CharSequence);
   }
 
@@ -5072,7 +5078,7 @@
     method public android.app.Notification.Builder setChronometerCountsDown(boolean);
     method public android.app.Notification.Builder setColor(int);
     method public deprecated android.app.Notification.Builder setContent(android.widget.RemoteViews);
-    method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
+    method public deprecated android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
     method public android.app.Notification.Builder setContentText(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentTitle(java.lang.CharSequence);
@@ -5089,7 +5095,7 @@
     method public android.app.Notification.Builder setLargeIcon(android.graphics.drawable.Icon);
     method public android.app.Notification.Builder setLights(int, int, int);
     method public android.app.Notification.Builder setLocalOnly(boolean);
-    method public android.app.Notification.Builder setNumber(int);
+    method public deprecated android.app.Notification.Builder setNumber(int);
     method public android.app.Notification.Builder setOngoing(boolean);
     method public android.app.Notification.Builder setOnlyAlertOnce(boolean);
     method public android.app.Notification.Builder setPriority(int);
@@ -5174,6 +5180,32 @@
     method public android.app.Notification.MediaStyle setShowActionsInCompactView(int...);
   }
 
+  public static class Notification.MessagingStyle extends android.app.Notification.Style {
+    ctor public Notification.MessagingStyle(java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public android.app.Notification.MessagingStyle addMessage(android.app.Notification.MessagingStyle.Message);
+    method public boolean getAllowGeneratedReplies();
+    method public java.lang.CharSequence getConversationTitle();
+    method public java.util.List<android.app.Notification.MessagingStyle.Message> getMessages();
+    method public java.lang.CharSequence getUserDisplayName();
+    method public android.app.Notification.MessagingStyle setAllowGeneratedReplies(boolean);
+    method public android.app.Notification.MessagingStyle setConversationTitle(java.lang.CharSequence);
+    field public static final int MAXIMUM_RETAINED_MESSAGES = 25; // 0x19
+  }
+
+  public static final class Notification.MessagingStyle.Message implements android.os.Parcelable {
+    ctor public Notification.MessagingStyle.Message(java.lang.CharSequence, long, java.lang.CharSequence);
+    method public int describeContents();
+    method public java.lang.String getDataMimeType();
+    method public android.net.Uri getDataUri();
+    method public java.lang.CharSequence getSender();
+    method public java.lang.CharSequence getText();
+    method public long getTimestamp();
+    method public android.app.Notification.MessagingStyle.Message setData(java.lang.String, android.net.Uri);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.Notification.MessagingStyle.Message> CREATOR;
+  }
+
   public static abstract class Notification.Style {
     ctor public Notification.Style();
     method public android.app.Notification build();
@@ -5207,6 +5239,7 @@
     method public android.app.PendingIntent getDisplayIntent();
     method public int getGravity();
     method public boolean getHintAvoidBackgroundClipping();
+    method public boolean getHintContentIntentLaunchesActivity();
     method public boolean getHintHideIcon();
     method public int getHintScreenTimeout();
     method public boolean getHintShowBackgroundOnly();
@@ -5222,6 +5255,7 @@
     method public android.app.Notification.WearableExtender setDisplayIntent(android.app.PendingIntent);
     method public android.app.Notification.WearableExtender setGravity(int);
     method public android.app.Notification.WearableExtender setHintAvoidBackgroundClipping(boolean);
+    method public android.app.Notification.WearableExtender setHintContentIntentLaunchesActivity(boolean);
     method public android.app.Notification.WearableExtender setHintHideIcon(boolean);
     method public android.app.Notification.WearableExtender setHintScreenTimeout(int);
     method public android.app.Notification.WearableExtender setHintShowBackgroundOnly(boolean);
@@ -5925,7 +5959,7 @@
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(android.content.ComponentName);
     method public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(android.content.ComponentName);
     method public void setAccountManagementDisabled(android.content.ComponentName, java.lang.String, boolean);
-    method public boolean setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String);
+    method public void setAlwaysOnVpnPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException, java.lang.UnsupportedOperationException;
     method public boolean setApplicationHidden(android.content.ComponentName, java.lang.String, boolean);
     method public void setApplicationRestrictions(android.content.ComponentName, java.lang.String, android.os.Bundle);
     method public void setApplicationRestrictionsManagingPackage(android.content.ComponentName, java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -7673,8 +7707,6 @@
     method public void setExtras(android.os.PersistableBundle);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.content.ClipDescription> CREATOR;
-    field public static final java.lang.String EXTRA_TARGET_COMPONENT_NAME = "android.content.extra.TARGET_COMPONENT_NAME";
-    field public static final java.lang.String EXTRA_USER_SERIAL_NUMBER = "android.content.extra.USER_SERIAL_NUMBER";
     field public static final java.lang.String MIMETYPE_TEXT_HTML = "text/html";
     field public static final java.lang.String MIMETYPE_TEXT_INTENT = "text/vnd.android.intent";
     field public static final java.lang.String MIMETYPE_TEXT_PLAIN = "text/plain";
@@ -7890,6 +7922,7 @@
     method public static boolean isSyncPending(android.accounts.Account, java.lang.String);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver);
     method public void notifyChange(android.net.Uri, android.database.ContentObserver, boolean);
+    method public void notifyChange(android.net.Uri, android.database.ContentObserver, int);
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     method public final android.content.res.AssetFileDescriptor openAssetFileDescriptor(android.net.Uri, java.lang.String, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public final android.os.ParcelFileDescriptor openFileDescriptor(android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
@@ -7920,6 +7953,8 @@
     field public static final java.lang.String CURSOR_DIR_BASE_TYPE = "vnd.android.cursor.dir";
     field public static final java.lang.String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item";
     field public static final java.lang.String EXTRA_SIZE = "android.content.extra.SIZE";
+    field public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 2; // 0x2
+    field public static final int NOTIFY_SYNC_TO_NETWORK = 1; // 0x1
     field public static final java.lang.String SCHEME_ANDROID_RESOURCE = "android.resource";
     field public static final java.lang.String SCHEME_CONTENT = "content";
     field public static final java.lang.String SCHEME_FILE = "file";
@@ -8657,6 +8692,7 @@
     field public static final java.lang.String EXTRA_CHANGED_PACKAGE_LIST = "android.intent.extra.changed_package_list";
     field public static final java.lang.String EXTRA_CHANGED_UID_LIST = "android.intent.extra.changed_uid_list";
     field public static final java.lang.String EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER = "android.intent.extra.CHOOSER_REFINEMENT_INTENT_SENDER";
+    field public static final java.lang.String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT = "android.intent.extra.CHOSEN_COMPONENT";
     field public static final java.lang.String EXTRA_CHOSEN_COMPONENT_INTENT_SENDER = "android.intent.extra.CHOSEN_COMPONENT_INTENT_SENDER";
     field public static final java.lang.String EXTRA_DATA_REMOVED = "android.intent.extra.DATA_REMOVED";
@@ -8668,6 +8704,7 @@
     field public static final int EXTRA_DOCK_STATE_UNDOCKED = 0; // 0x0
     field public static final java.lang.String EXTRA_DONT_KILL_APP = "android.intent.extra.DONT_KILL_APP";
     field public static final java.lang.String EXTRA_EMAIL = "android.intent.extra.EMAIL";
+    field public static final java.lang.String EXTRA_EXCLUDE_COMPONENTS = "android.intent.extra.EXCLUDE_COMPONENTS";
     field public static final java.lang.String EXTRA_HTML_TEXT = "android.intent.extra.HTML_TEXT";
     field public static final java.lang.String EXTRA_INDEX = "android.intent.extra.INDEX";
     field public static final java.lang.String EXTRA_INITIAL_INTENTS = "android.intent.extra.INITIAL_INTENTS";
@@ -13898,11 +13935,11 @@
     method public abstract void close();
     method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createCaptureSessionByOutputConfiguration(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createCaptureSessionByOutputConfigurations(java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
     method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
-    method public abstract void createReprocessableCaptureSessionWithConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+    method public abstract void createReprocessableCaptureSessionByConfigurations(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public abstract java.lang.String getId();
     field public static final int TEMPLATE_MANUAL = 6; // 0x6
     field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -19269,7 +19306,7 @@
     field public static final int ADR_STATE_VALID = 1; // 0x1
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurement> CREATOR;
     field public static final int MULTIPATH_INDICATOR_DETECTED = 1; // 0x1
-    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+    field public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2; // 0x2
     field public static final int MULTIPATH_INDICATOR_UNKNOWN = 0; // 0x0
     field public static final int STATE_BDS_D2_BIT_SYNC = 256; // 0x100
     field public static final int STATE_BDS_D2_SUBFRAME_SYNC = 512; // 0x200
@@ -19295,15 +19332,15 @@
     method public java.util.Collection<android.location.GnssMeasurement> getMeasurements();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementsEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public static abstract class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
     method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
   }
 
   public final class GnssNavigationMessage implements android.os.Parcelable {
@@ -19340,36 +19377,24 @@
     field public static final int TYPE_UNKNOWN = 0; // 0x0
   }
 
-  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
-    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
-    method public int describeContents();
-    method public android.location.GnssNavigationMessage getNavigationMessage();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
-    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+  public static abstract class GnssNavigationMessage.Callback {
+    ctor public GnssNavigationMessage.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessage);
+    method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
     field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
     field public static final int STATUS_READY = 1; // 0x1
   }
 
-  public static abstract class GnssNavigationMessageEvent.Callback {
-    ctor public GnssNavigationMessageEvent.Callback();
-    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
-    method public void onStatusChanged(int);
-  }
-
-  public abstract interface GnssNmeaListener {
-    method public abstract void onNmeaReceived(long, java.lang.String);
-  }
-
   public final class GnssStatus {
     method public float getAzimuthDegrees(int);
     method public float getCn0DbHz(int);
     method public int getConstellationType(int);
     method public float getElevationDegrees(int);
-    method public int getNumSatellites();
+    method public int getSatelliteCount();
     method public int getSvid(int);
-    method public boolean hasAlmanac(int);
-    method public boolean hasEphemeris(int);
+    method public boolean hasAlmanacData(int);
+    method public boolean hasEphemerisData(int);
     method public boolean usedInFix(int);
     field public static final int CONSTELLATION_BEIDOU = 5; // 0x5
     field public static final int CONSTELLATION_GALILEO = 6; // 0x6
@@ -19380,15 +19405,15 @@
     field public static final int CONSTELLATION_UNKNOWN = 0; // 0x0
   }
 
-  public abstract class GnssStatusCallback {
-    ctor public GnssStatusCallback();
+  public static abstract class GnssStatus.Callback {
+    ctor public GnssStatus.Callback();
     method public void onFirstFix(int);
     method public void onSatelliteStatusChanged(android.location.GnssStatus);
     method public void onStarted();
     method public void onStopped();
   }
 
-  public final class GpsSatellite {
+  public final deprecated class GpsSatellite {
     method public float getAzimuth();
     method public float getElevation();
     method public int getPrn();
@@ -19398,7 +19423,7 @@
     method public boolean usedInFix();
   }
 
-  public final class GpsStatus {
+  public final deprecated class GpsStatus {
     method public int getMaxSatellites();
     method public java.lang.Iterable<android.location.GpsSatellite> getSatellites();
     method public int getTimeToFirstFix();
@@ -19408,11 +19433,11 @@
     field public static final int GPS_EVENT_STOPPED = 2; // 0x2
   }
 
-  public static abstract interface GpsStatus.Listener {
+  public static abstract deprecated interface GpsStatus.Listener {
     method public abstract void onGpsStatusChanged(int);
   }
 
-  public static abstract interface GpsStatus.NmeaListener {
+  public static abstract deprecated interface GpsStatus.NmeaListener {
     method public abstract void onNmeaReceived(long, java.lang.String);
   }
 
@@ -19474,8 +19499,8 @@
   public class LocationManager {
     method public deprecated boolean addGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated boolean addNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener);
-    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener);
+    method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
     method public void clearTestProviderEnabled(java.lang.String);
@@ -19492,13 +19517,13 @@
     method public boolean isProviderEnabled(java.lang.String);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
     method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
-    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatus.Callback, android.os.Handler);
     method public deprecated void removeGpsStatusListener(android.location.GpsStatus.Listener);
     method public deprecated void removeNmeaListener(android.location.GpsStatus.NmeaListener);
-    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void removeNmeaListener(android.location.OnNmeaMessageListener);
     method public void removeProximityAlert(android.app.PendingIntent);
     method public void removeTestProvider(java.lang.String);
     method public void removeUpdates(android.location.LocationListener);
@@ -19517,8 +19542,8 @@
     method public void setTestProviderLocation(java.lang.String, android.location.Location);
     method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
     method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
-    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
-    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
     field public static final java.lang.String GPS_PROVIDER = "gps";
     field public static final java.lang.String KEY_LOCATION_CHANGED = "location";
     field public static final java.lang.String KEY_PROVIDER_ENABLED = "providerEnabled";
@@ -19547,6 +19572,10 @@
     field public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
   }
 
+  public abstract interface OnNmeaMessageListener {
+    method public abstract void onNmeaMessage(java.lang.String, long);
+  }
+
   public abstract class SettingInjectorService extends android.app.Service {
     ctor public SettingInjectorService(java.lang.String);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -20138,7 +20167,6 @@
   }
 
   public abstract class DrmInitData {
-    ctor public DrmInitData();
     method public abstract android.media.DrmInitData.SchemeInitData get(java.util.UUID);
   }
 
@@ -23517,6 +23545,7 @@
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static deprecated boolean isNetworkTypeValid(int);
+    method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
     method public void releaseNetworkRequest(android.app.PendingIntent);
@@ -34757,6 +34786,7 @@
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onInterruptionFilterChanged(int);
     method public void onListenerConnected();
+    method public void onListenerDisconnected();
     method public void onListenerHintsChanged(int);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification);
     method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
@@ -34768,7 +34798,9 @@
     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 int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
     field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
+    field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
     field public static final int INTERRUPTION_FILTER_ALARMS = 4; // 0x4
     field public static final int INTERRUPTION_FILTER_ALL = 1; // 0x1
     field public static final int INTERRUPTION_FILTER_NONE = 3; // 0x3
@@ -34784,6 +34816,7 @@
     method public int getImportance();
     method public java.lang.CharSequence getImportanceExplanation();
     method public java.lang.String getKey();
+    method public java.lang.String getOverrideGroupKey();
     method public int getRank();
     method public int getSuppressedVisualEffects();
     method public boolean isAmbient();
@@ -34814,13 +34847,16 @@
     method public int getId();
     method public java.lang.String getKey();
     method public android.app.Notification getNotification();
+    method public java.lang.String getOverrideGroupKey();
     method public java.lang.String getPackageName();
     method public long getPostTime();
     method public java.lang.String getTag();
     method public android.os.UserHandle getUser();
     method public deprecated int getUserId();
     method public boolean isClearable();
+    method public boolean isGroup();
     method public boolean isOngoing();
+    method public void setOverrideGroupKey(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR;
   }
@@ -34856,7 +34892,7 @@
     method public void onClick();
     method public void onStartListening();
     method public void onStopListening();
-    method public int onTileAdded();
+    method public void onTileAdded();
     method public void onTileRemoved();
     method public static final void requestListeningState(android.content.Context, android.content.ComponentName);
     method public final void showDialog(android.app.Dialog);
@@ -34864,8 +34900,7 @@
     method public final void unlockAndRun(java.lang.Runnable);
     field public static final java.lang.String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
     field public static final java.lang.String ACTION_QS_TILE_PREFERENCES = "android.service.quicksettings.action.QS_TILE_PREFERENCES";
-    field public static final int TILE_MODE_ACTIVE = 2; // 0x2
-    field public static final int TILE_MODE_PASSIVE = 1; // 0x1
+    field public static final java.lang.String META_DATA_ACTIVE_TILE = "android.service.quicksettings.ACTIVE_TILE";
   }
 
 }
@@ -46509,21 +46544,21 @@
     ctor public DatePicker(android.content.Context, android.util.AttributeSet);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
-    method public android.widget.CalendarView getCalendarView();
-    method public boolean getCalendarViewShown();
+    method public deprecated android.widget.CalendarView getCalendarView();
+    method public deprecated boolean getCalendarViewShown();
     method public int getDayOfMonth();
     method public int getFirstDayOfWeek();
     method public long getMaxDate();
     method public long getMinDate();
     method public int getMonth();
-    method public boolean getSpinnersShown();
+    method public deprecated boolean getSpinnersShown();
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
-    method public void setCalendarViewShown(boolean);
+    method public deprecated void setCalendarViewShown(boolean);
     method public void setFirstDayOfWeek(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
-    method public void setSpinnersShown(boolean);
+    method public deprecated void setSpinnersShown(boolean);
     method public void updateDate(int, int, int);
   }
 
@@ -50097,6 +50132,9 @@
     method public int compareTo(java.lang.Boolean);
     method public static boolean getBoolean(java.lang.String);
     method public static int hashCode(boolean);
+    method public static boolean logicalAnd(boolean, boolean);
+    method public static boolean logicalOr(boolean, boolean);
+    method public static boolean logicalXor(boolean, boolean);
     method public static boolean parseBoolean(java.lang.String);
     method public static java.lang.String toString(boolean);
     method public static java.lang.Boolean valueOf(boolean);
@@ -51060,6 +51098,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -51069,12 +51109,20 @@
     method public static float copySign(float, float);
     method public static double cos(double);
     method public static double cosh(double);
+    method public static int decrementExact(int);
+    method public static long decrementExact(long);
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
+    method public static int incrementExact(int);
+    method public static long incrementExact(long);
     method public static double log(double);
     method public static double log10(double);
     method public static double log1p(double);
@@ -51086,8 +51134,14 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
+    method public static int negateExact(int);
+    method public static long negateExact(long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -51102,9 +51156,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -51388,6 +51445,8 @@
     method public static float abs(float);
     method public static double abs(double);
     method public static double acos(double);
+    method public static int addExact(int, int);
+    method public static long addExact(long, long);
     method public static double asin(double);
     method public static double atan(double);
     method public static double atan2(double, double);
@@ -51400,6 +51459,10 @@
     method public static double exp(double);
     method public static double expm1(double);
     method public static double floor(double);
+    method public static int floorDiv(int, int);
+    method public static long floorDiv(long, long);
+    method public static int floorMod(int, int);
+    method public static long floorMod(long, long);
     method public static int getExponent(float);
     method public static int getExponent(double);
     method public static double hypot(double, double);
@@ -51414,8 +51477,12 @@
     method public static long min(long, long);
     method public static float min(float, float);
     method public static double min(double, double);
+    method public static int multiplyExact(int, int);
+    method public static long multiplyExact(long, long);
     method public static double nextAfter(double, double);
     method public static float nextAfter(float, double);
+    method public static double nextDown(double);
+    method public static float nextDown(float);
     method public static double nextUp(double);
     method public static float nextUp(float);
     method public static double pow(double, double);
@@ -51430,9 +51497,12 @@
     method public static double sin(double);
     method public static double sinh(double);
     method public static double sqrt(double);
+    method public static int subtractExact(int, int);
+    method public static long subtractExact(long, long);
     method public static double tan(double);
     method public static double tanh(double);
     method public static double toDegrees(double);
+    method public static int toIntExact(long);
     method public static double toRadians(double);
     method public static double ulp(double);
     method public static float ulp(float);
@@ -51990,7 +52060,6 @@
   public final class Method extends java.lang.reflect.AccessibleObject implements java.lang.reflect.GenericDeclaration java.lang.reflect.Member {
     method public boolean equals(java.lang.Object);
     method public A getAnnotation(java.lang.Class<A>);
-    method public java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public java.lang.Class<?> getDeclaringClass();
     method public java.lang.Object getDefaultValue();
     method public java.lang.Class<?>[] getExceptionTypes();
@@ -52004,7 +52073,6 @@
     method public java.lang.Class<?> getReturnType();
     method public java.lang.reflect.TypeVariable<java.lang.reflect.Method>[] getTypeParameters();
     method public java.lang.Object invoke(java.lang.Object, java.lang.Object...) throws java.lang.IllegalAccessException, java.lang.IllegalArgumentException, java.lang.reflect.InvocationTargetException;
-    method public boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
     method public boolean isBridge();
     method public boolean isDefault();
     method public boolean isSynthetic();
@@ -57480,6 +57548,14 @@
     method public static int hashCode(float[]);
     method public static int hashCode(double[]);
     method public static int hashCode(java.lang.Object[]);
+    method public static void parallelPrefix(T[], java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(T[], int, int, java.util.function.BinaryOperator<T>);
+    method public static void parallelPrefix(long[], java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(long[], int, int, java.util.function.LongBinaryOperator);
+    method public static void parallelPrefix(double[], java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(double[], int, int, java.util.function.DoubleBinaryOperator);
+    method public static void parallelPrefix(int[], java.util.function.IntBinaryOperator);
+    method public static void parallelPrefix(int[], int, int, java.util.function.IntBinaryOperator);
     method public static void parallelSetAll(T[], java.util.function.IntFunction<? extends T>);
     method public static void parallelSetAll(int[], java.util.function.IntUnaryOperator);
     method public static void parallelSetAll(long[], java.util.function.IntToLongFunction);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 3f16bca..8c6abdc 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -84,6 +84,67 @@
 
 }
 
+package android.location {
+
+  public final class GnssMeasurement implements android.os.Parcelable {
+    field public static final int MULTIPATH_INDICATOR_NOT_USED = 2; // 0x2
+  }
+
+  public final class GnssMeasurementsEvent implements android.os.Parcelable {
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public final class GnssNavigationMessageEvent implements android.os.Parcelable {
+    ctor public GnssNavigationMessageEvent(android.location.GnssNavigationMessage);
+    method public int describeContents();
+    method public android.location.GnssNavigationMessage getNavigationMessage();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.location.GnssNavigationMessageEvent> CREATOR;
+    field public static final int STATUS_GNSS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static abstract class GnssNavigationMessageEvent.Callback {
+    ctor public GnssNavigationMessageEvent.Callback();
+    method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessageEvent);
+    method public void onStatusChanged(int);
+  }
+
+  public abstract interface GnssNmeaListener {
+    method public abstract void onNmeaReceived(long, java.lang.String);
+  }
+
+  public final class GnssStatus {
+    method public int getNumSatellites();
+    method public boolean hasAlmanac(int);
+    method public boolean hasEphemeris(int);
+  }
+
+  public abstract class GnssStatusCallback {
+    ctor public GnssStatusCallback();
+    method public void onFirstFix(int);
+    method public void onSatelliteStatusChanged(android.location.GnssStatus);
+    method public void onStarted();
+    method public void onStopped();
+  }
+
+  public class LocationManager {
+    method public boolean addNmeaListener(android.location.GnssNmeaListener);
+    method public boolean addNmeaListener(android.location.GnssNmeaListener, android.os.Handler);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback, android.os.Handler);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback);
+    method public boolean registerGnssStatusCallback(android.location.GnssStatusCallback, android.os.Handler);
+    method public void removeNmeaListener(android.location.GnssNmeaListener);
+    method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessageEvent.Callback);
+    method public void unregisterGnssStatusCallback(android.location.GnssStatusCallback);
+  }
+
+}
+
 package android.media {
 
   public final class AudioFormat implements android.os.Parcelable {
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 0e674c8..d527ad7 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -74,6 +74,8 @@
             runGetPrimaryStorageUuid();
         } else if ("set-force-adoptable".equals(op)) {
             runSetForceAdoptable();
+        } else if ("set-sdcardfs".equals(op)) {
+            runSetSdcardfs();
         } else if ("partition".equals(op)) {
             runPartition();
         } else if ("mount".equals(op)) {
@@ -141,6 +143,22 @@
                 StorageManager.DEBUG_FORCE_ADOPTABLE);
     }
 
+    public void runSetSdcardfs() throws RemoteException {
+        final int mask = StorageManager.DEBUG_SDCARDFS_FORCE_ON
+                | StorageManager.DEBUG_SDCARDFS_FORCE_OFF;
+        switch (nextArg()) {
+            case "on":
+                mSm.setDebugFlags(StorageManager.DEBUG_SDCARDFS_FORCE_ON, mask);
+                break;
+            case "off":
+                mSm.setDebugFlags(StorageManager.DEBUG_SDCARDFS_FORCE_OFF, mask);
+                break;
+            case "default":
+                mSm.setDebugFlags(0, mask);
+                break;
+        }
+    }
+
     public void runSetEmulateFbe() throws RemoteException {
         final boolean emulateFbe = Boolean.parseBoolean(nextArg());
         mSm.setDebugFlags(emulateFbe ? StorageManager.DEBUG_EMULATE_FBE : 0,
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index bf823f8..56728ad 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -600,11 +600,16 @@
      * Dispatch a gesture to the touch screen. Any gestures currently in progress, whether from
      * the user, this service, or another service, will be cancelled.
      * <p>
+     * The gesture will be dispatched as if it were performed directly on the screen by a user, so
+     * the events may be affected by features such as magnification and explore by touch.
+     * </p>
+     * <p>
      * <strong>Note:</strong> In order to dispatch gestures, your service
      * must declare the capability by setting the
      * {@link android.R.styleable#AccessibilityService_canPerformGestures}
      * property in its meta-data. For more information, see
      * {@link #SERVICE_META_DATA}.
+     * </p>
      *
      * @param gesture The gesture to dispatch
      * @param callback The object to call back when the status of the gesture is known. If
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 0410a6e..672a706 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1343,20 +1343,21 @@
      * {@link #getVoiceInteractor()}.
      */
     public void onLocalVoiceInteractionStarted() {
-        Log.i(TAG, "onLocalVoiceInteractionStarted! " + getVoiceInteractor());
     }
 
     /**
-     * Callback to indicate that the local voice interaction has stopped for some
-     * reason.
+     * Callback to indicate that the local voice interaction has stopped either
+     * because it was requested through a call to {@link #stopLocalVoiceInteraction()}
+     * or because it was canceled by the user. The previously acquired {@link VoiceInteractor}
+     * is no longer valid after this.
      */
     public void onLocalVoiceInteractionStopped() {
-        Log.i(TAG, "onLocalVoiceInteractionStopped :( " + getVoiceInteractor());
     }
 
     /**
      * Request to terminate the current voice interaction that was previously started
-     * using {@link #startLocalVoiceInteraction(Bundle)}.
+     * using {@link #startLocalVoiceInteraction(Bundle)}. When the interaction is
+     * terminated, {@link #onLocalVoiceInteractionStopped()} will be called.
      */
     public void stopLocalVoiceInteraction() {
         try {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 14c4fc6..36e962e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1526,7 +1526,7 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case CREATE_SERVICE:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceCreate: " + String.valueOf(msg.obj)));
                     handleCreateService((CreateServiceData)msg.obj);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
@@ -1541,7 +1541,7 @@
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
                 case SERVICE_ARGS:
-                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStart");
+                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ("serviceStart: " + String.valueOf(msg.obj)));
                     handleServiceArgs((ServiceArgsData)msg.obj);
                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                     break;
@@ -4727,8 +4727,16 @@
         if (callbacks != null) {
             final int N = callbacks.size();
             for (int i=0; i<N; i++) {
-                performConfigurationChanged(callbacks.get(i), null, config, null,
-                        REPORT_TO_ACTIVITY);
+                ComponentCallbacks2 cb = callbacks.get(i);
+                if (cb instanceof Activity) {
+                    // If callback is an Activity - call corresponding method to consider override
+                    // config and avoid onConfigurationChanged if it hasn't changed.
+                    Activity a = (Activity) cb;
+                    performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
+                            config, REPORT_TO_ACTIVITY);
+                } else {
+                    performConfigurationChanged(cb, null, config, null, REPORT_TO_ACTIVITY);
+                }
             }
         }
     }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c745644..2a04c39 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1033,7 +1033,8 @@
      *                        false if it is not.
      */
     public void setUserVisibleHint(boolean isVisibleToUser) {
-        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED && isAdded()) {
+        if (!mUserVisibleHint && isVisibleToUser && mState < STARTED
+                && mFragmentManager != null && isAdded()) {
             mFragmentManager.performPendingDeferredStart(this);
         }
         mUserVisibleHint = isVisibleToUser;
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 7a69c62..ee80ec3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -25,6 +25,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.Uri;
 import android.os.Bundle;
+import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
@@ -80,7 +81,8 @@
     void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
     void setInterruptionFilter(String pkg, int interruptionFilter);
 
-    void setImportanceFromRankerService(in INotificationListener token, String key, int importance, CharSequence explanation);
+    void applyAdjustmentFromRankerService(in INotificationListener token, in Adjustment adjustment);
+    void applyAdjustmentsFromRankerService(in INotificationListener token, in List<Adjustment> adjustments);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index fa67529..496ca6f 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -40,4 +40,9 @@
      * Called when we launched an activity that we forced to be resizable.
      */
     void onActivityForcedResizable(String packageName, int taskId);
+
+    /**
+     * Callen when we launched an activity that is dismissed the docked stack.
+     */
+    void onActivityDismissingDockedStack();
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 13e8e75..4fca69a 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -206,7 +206,7 @@
             }
             results.putAll(mPerfMetrics);
         }
-        if (mUiAutomation != null) {
+        if ((mUiAutomation != null) && !mUiAutomation.isDestroyed()) {
             mUiAutomation.disconnect();
             mUiAutomation = null;
         }
@@ -1834,7 +1834,7 @@
     }
 
     /**
-     * Gets the {@link UiAutomation} instance.
+     * Gets the {@link UiAutomation} instance with no flags set.
      * <p>
      * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation}
      * work across application boundaries while the APIs exposed by the instrumentation
@@ -1848,25 +1848,21 @@
      * {@link Instrumentation} APIs. Using both APIs at the same time is not
      * a mistake by itself but a client has to be aware of the APIs limitations.
      * </p>
-     * @return The UI automation instance. If none exists, a new one is created with no flags set.
+     * <p>
+     * Equivalent to {@code getUiAutomation(0)}. If a {@link UiAutomation} exists with different
+     * flags, the flags on that instance will be changed, and then it will be returned.
+     * </p>
+     * @return The UI automation instance.
      *
      * @see UiAutomation
      */
     public UiAutomation getUiAutomation() {
-        if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) {
-            return getUiAutomation(0);
-        }
-        return mUiAutomation;
+        return getUiAutomation(0);
     }
 
     /**
      * Gets the {@link UiAutomation} instance with flags set.
      * <p>
-     * <strong>Note:</strong> Only one UiAutomation can be obtained. Calling this method
-     * twice with different flags will fail unless the UiAutomation obtained in the first call
-     * is released with {@link UiAutomation#destroy()}.
-     * </p>
-     * <p>
      * <strong>Note:</strong> The APIs exposed via the returned {@link UiAutomation}
      * work across application boundaries while the APIs exposed by the instrumentation
      * do not. For example, {@link Instrumentation#sendPointerSync(MotionEvent)} will
@@ -1879,6 +1875,10 @@
      * {@link Instrumentation} APIs. Using both APIs at the same time is not
      * a mistake by itself but a client has to be aware of the APIs limitations.
      * </p>
+     * <p>
+     * If a {@link UiAutomation} exists with different flags, the flags on that instance will be
+     * changed, and then it will be returned.
+     * </p>
      *
      * @param flags The flags to be passed to the UiAutomation, for example
      *        {@link UiAutomation#FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES}.
@@ -1888,17 +1888,19 @@
      * @see UiAutomation
      */
     public UiAutomation getUiAutomation(@UiAutomationFlags int flags) {
+        boolean mustCreateNewAutomation = (mUiAutomation == null) || (mUiAutomation.isDestroyed());
+
         if (mUiAutomationConnection != null) {
-            if ((mUiAutomation == null) || (mUiAutomation.isDestroyed())) {
+            if (!mustCreateNewAutomation && (mUiAutomation.getFlags() == flags)) {
+                return mUiAutomation;
+            }
+            if (mustCreateNewAutomation) {
                 mUiAutomation = new UiAutomation(getTargetContext().getMainLooper(),
                         mUiAutomationConnection);
-                mUiAutomation.connect(flags);
             } else {
-                if (mUiAutomation.getFlags() != flags) {
-                    throw new RuntimeException(
-                            "Cannot get a UiAutomation with different flags from the existing one");
-                }
+                mUiAutomation.disconnect();
             }
+            mUiAutomation.connect(flags);
             return mUiAutomation;
         }
         return null;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8423de8..4bf1aa3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
@@ -174,6 +175,9 @@
      *   <li>Notification of an ongoing countdown timer should be stamped with the timer's end time.
      * </ul>
      *
+     * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not shown
+     * anymore by default and must be opted into by using
+     * {@link android.app.Notification.Builder#setShowWhen(boolean)}
      */
     public long when;
 
@@ -206,6 +210,8 @@
      * {@link Notification.Builder} has displayed the number in the expanded notification view.
      *
      * If the number is 0 or negative, it is never shown.
+     *
+     * @deprecated this number is not shown anymore
      */
     public int number;
 
@@ -491,6 +497,15 @@
      */
     public static final int FLAG_GROUP_SUMMARY      = 0x00000200;
 
+    /**
+     * Bit to be bitswise-ored into the {@link #flags} field that should be
+     * set if this notification is the group summary for an auto-group of notifications.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int FLAG_AUTOGROUP_SUMMARY  = 0x00000400;
+
     public int flags;
 
     /** @hide */
@@ -899,6 +914,34 @@
     public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
 
     /**
+     * {@link #extras} key: the username to be displayed for all messages sent by the user including
+     * direct replies
+     * {@link android.app.Notification.MessagingStyle} notification.
+     */
+    public static final String EXTRA_SELF_DISPLAY_NAME = "android.selfDisplayName";
+
+    /**
+     * {@link #extras} key: a boolean describing whether the platform should automatically
+     * generate possible replies to
+     * {@link android.app.Notification.MessagingStyle.Message} objects provided by a
+     * {@link android.app.Notification.MessagingStyle} notification.
+     */
+    public static final String EXTRA_ALLOW_GENERATED_REPLIES = "android.allowGeneratedReplies";
+
+    /**
+     * {@link #extras} key: a {@link String} to be displayed as the title to a thread represented by
+     * a {@link android.app.Notification.MessagingStyle}
+     */
+    public static final String EXTRA_THREAD_TITLE = "android.threadTitle";
+
+    /**
+     * {@link #extras} key: an array of {@link android.app.Notification.MessagingStyle.Message}
+     * bundles provided by a
+     * {@link android.app.Notification.MessagingStyle} notification.
+     */
+    public static final String EXTRA_MESSAGES = "android.messages";
+
+    /**
      * {@link #extras} key: the user that built the notification.
      *
      * @hide
@@ -1205,6 +1248,7 @@
 
             // Flags bitwise-ored to mFlags
             private static final int FLAG_AVAILABLE_OFFLINE = 0x1;
+            private static final int FLAG_HINT_LAUNCHES_ACTIVITY = 1 << 1;
 
             // Default value for flags integer
             private static final int DEFAULT_FLAGS = FLAG_AVAILABLE_OFFLINE;
@@ -1367,6 +1411,30 @@
             public CharSequence getCancelLabel() {
                 return mCancelLabel;
             }
+
+            /**
+             * Set a hint that this Action will launch an {@link Activity} directly, telling the
+             * platform that it can generate the appropriate transitions.
+             * @param hintLaunchesActivity {@code true} if the content intent will launch
+             * an activity and transitions should be generated, false otherwise.
+             * @return this object for method chaining
+             */
+            public WearableExtender setHintContentIntentLaunchesActivity(
+                    boolean hintLaunchesActivity) {
+                setFlag(FLAG_HINT_LAUNCHES_ACTIVITY, hintLaunchesActivity);
+                return this;
+            }
+
+            /**
+             * Get a hint that this Action will launch an {@link Activity} directly, telling the
+             * platform that it can generate the appropriate transitions
+             * @return {@code true} if the content intent will launch an activity and transitions
+             * should be generated, false otherwise. The default value is {@code false} if this was
+             * never set.
+             */
+            public boolean getHintContentIntentLaunchesActivity() {
+                return (mFlags & FLAG_HINT_LAUNCHES_ACTIVITY) != 0;
+            }
         }
     }
 
@@ -1887,13 +1955,9 @@
      * @hide
      */
     public static void addFieldsFromContext(Context context, Notification notification) {
-        if (notification.extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO) == null) {
-            notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
-                    context.getApplicationInfo());
-        }
-        if (!notification.extras.containsKey(EXTRA_ORIGINATING_USERID)) {
-            notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
-        }
+        notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO,
+                context.getApplicationInfo());
+        notification.extras.putInt(EXTRA_ORIGINATING_USERID, context.getUserId());
     }
 
     @Override
@@ -2133,7 +2197,9 @@
 
             if (toAdopt == null) {
                 mN = new Notification();
-                mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+                if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N) {
+                    mN.extras.putBoolean(EXTRA_SHOW_WHEN, true);
+                }
                 mN.priority = PRIORITY_DEFAULT;
                 mN.visibility = VISIBILITY_PRIVATE;
             } else {
@@ -2183,8 +2249,10 @@
 
         /**
          * Add a timestamp pertaining to the notification (usually the time the event occurred).
-         * It will be shown in the notification content view by default; use
-         * {@link #setShowWhen(boolean) setShowWhen} to control this.
+         *
+         * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this time is not
+         * shown anymore by default and must be opted into by using
+         * {@link android.app.Notification.Builder#setShowWhen(boolean)}
          *
          * @see Notification#when
          */
@@ -2196,6 +2264,8 @@
         /**
          * Control whether the timestamp set with {@link #setWhen(long) setWhen} is shown
          * in the content view.
+         * For apps targeting {@link android.os.Build.VERSION_CODES#N} and above, this defaults to
+         * {@code false}. For earlier apps, the default is {@code true}.
          */
         public Builder setShowWhen(boolean show) {
             mN.extras.putBoolean(EXTRA_SHOW_WHEN, show);
@@ -2304,9 +2374,22 @@
         }
 
         /**
-         * Set the third line of text in the platform notification template.
-         * Don't use if you're also using {@link #setProgress(int, int, boolean)}; they occupy the
-         * same location in the standard template.
+         * This provides some additional information that is displayed in the notification. No
+         * guarantees are given where exactly it is displayed.
+         *
+         * <p>This information should only be provided if it provides an essential
+         * benefit to the understanding of the notification. The more text you provide the
+         * less readable it becomes. For example, an email client should only provide the account
+         * name here if more than one email account has been added.</p>
+         *
+         * <p>As of {@link android.os.Build.VERSION_CODES#N} this information is displayed in the
+         * notification header area.
+         *
+         * On Android versions before {@link android.os.Build.VERSION_CODES#N}
+         * this will be shown in the third line of text in the platform notification template.
+         * You should not be using {@link #setProgress(int, int, boolean)} at the
+         * same time on those versions; they occupy the same place.
+         * </p>
          */
         public Builder setSubText(CharSequence text) {
             mN.extras.putCharSequence(EXTRA_SUB_TEXT, safeCharSequence(text));
@@ -2345,6 +2428,8 @@
          * Set the large number at the right-hand side of the notification.  This is
          * equivalent to setContentInfo, although it might show the number in a different
          * font size for readability.
+         *
+         * @deprecated this number is not shown anywhere anymore
          */
         public Builder setNumber(int number) {
             mN.number = number;
@@ -2356,6 +2441,10 @@
          *
          * The platform template will draw this on the last line of the notification, at the far
          * right (to the right of a smallIcon if it has been placed there).
+         *
+         * @deprecated use {@link #setSubText(CharSequence)} instead to set a text in the header.
+         * For legacy apps targeting a version below {@link android.os.Build.VERSION_CODES#N} this
+         * field will still show up, but the subtext will take precedence.
          */
         public Builder setContentInfo(CharSequence info) {
             mN.extras.putCharSequence(EXTRA_INFO_TEXT, safeCharSequence(info));
@@ -2937,12 +3026,13 @@
         /**
          * @hide
          */
-        public void setFlag(int mask, boolean value) {
+        public Builder setFlag(int mask, boolean value) {
             if (value) {
                 mN.flags |= mask;
             } else {
                 mN.flags &= ~mask;
             }
+            return this;
         }
 
         /**
@@ -3009,10 +3099,8 @@
             contentView.setBoolean(R.id.notification_header, "setExpanded", false);
             contentView.setTextViewText(R.id.app_name_text, null);
             contentView.setViewVisibility(R.id.chronometer, View.GONE);
-            contentView.setViewVisibility(R.id.header_sub_text, View.GONE);
-            contentView.setViewVisibility(R.id.header_content_info, View.GONE);
-            contentView.setViewVisibility(R.id.sub_text_divider, View.GONE);
-            contentView.setViewVisibility(R.id.content_info_divider, View.GONE);
+            contentView.setViewVisibility(R.id.header_text, View.GONE);
+            contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
             contentView.setViewVisibility(R.id.time_divider, View.GONE);
             contentView.setImageViewIcon(R.id.profile_badge, null);
             contentView.setViewVisibility(R.id.profile_badge, View.GONE);
@@ -3112,39 +3200,12 @@
         private void bindNotificationHeader(RemoteViews contentView) {
             bindSmallIcon(contentView);
             bindHeaderAppName(contentView);
-            bindHeaderSubText(contentView);
-            bindContentInfo(contentView);
+            bindHeaderText(contentView);
             bindHeaderChronometerAndTime(contentView);
             bindExpandButton(contentView);
             bindProfileBadge(contentView);
         }
 
-        private void bindContentInfo(RemoteViews contentView) {
-            boolean visible = false;
-            if (mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
-                contentView.setTextViewText(R.id.header_content_info,
-                        processLegacyText(mN.extras.getCharSequence(EXTRA_INFO_TEXT)));
-                contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
-                visible = true;
-            } else if (mN.number > 0) {
-                final int tooBig = mContext.getResources().getInteger(
-                        R.integer.status_bar_notification_info_maxnum);
-                if (mN.number > tooBig) {
-                    contentView.setTextViewText(R.id.header_content_info, processLegacyText(
-                            mContext.getResources().getString(
-                                    R.string.status_bar_notification_info_overflow)));
-                } else {
-                    contentView.setTextViewText(R.id.header_content_info,
-                            processLegacyText(String.valueOf(mN.number)));
-                }
-                contentView.setViewVisibility(R.id.header_content_info, View.VISIBLE);
-                visible = true;
-            }
-            if (visible) {
-                contentView.setViewVisibility(R.id.content_info_divider, View.VISIBLE);
-            }
-        }
-
         private void bindExpandButton(RemoteViews contentView) {
             contentView.setDrawableParameters(R.id.expand_button, false, -1, resolveContrastColor(),
                     PorterDuff.Mode.SRC_ATOP, -1);
@@ -3169,17 +3230,22 @@
             }
         }
 
-        private void bindHeaderSubText(RemoteViews contentView) {
-            CharSequence subText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
-            if (subText == null && mStyle != null && mStyle.mSummaryTextSet
+        private void bindHeaderText(RemoteViews contentView) {
+            CharSequence headerText = mN.extras.getCharSequence(EXTRA_SUB_TEXT);
+            if (headerText == null && mStyle != null && mStyle.mSummaryTextSet
                     && mStyle.hasSummaryInHeader()) {
-                subText = mStyle.mSummaryText;
+                headerText = mStyle.mSummaryText;
             }
-            if (subText != null) {
+            if (headerText == null
+                    && mContext.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.N
+                    && mN.extras.getCharSequence(EXTRA_INFO_TEXT) != null) {
+                headerText = mN.extras.getCharSequence(EXTRA_INFO_TEXT);
+            }
+            if (headerText != null) {
                 // TODO: Remove the span entirely to only have the string with propper formating.
-                contentView.setTextViewText(R.id.header_sub_text, processLegacyText(subText));
-                contentView.setViewVisibility(R.id.header_sub_text, View.VISIBLE);
-                contentView.setViewVisibility(R.id.sub_text_divider, View.VISIBLE);
+                contentView.setTextViewText(R.id.header_text, processLegacyText(headerText));
+                contentView.setViewVisibility(R.id.header_text, View.VISIBLE);
+                contentView.setViewVisibility(R.id.header_text_divider, View.VISIBLE);
             }
         }
 
@@ -3529,7 +3595,8 @@
         private static Class<? extends Style> getNotificationStyleClass(String templateClass) {
             Class<? extends Style>[] classes = new Class[] {
                     BigTextStyle.class, BigPictureStyle.class, InboxStyle.class, MediaStyle.class,
-                    DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class };
+                    DecoratedCustomViewStyle.class, DecoratedMediaCustomViewStyle.class,
+                    MessagingStyle.class };
             for (Class<? extends Style> innerClass : classes) {
                 if (templateClass.equals(innerClass.getName())) {
                     return innerClass;
@@ -4126,6 +4193,350 @@
     }
 
     /**
+     * Helper class for generating large-format notifications that include multiple back-and-forth
+     * messages of varying types between any number of people.
+     *
+     * <br>
+     * If the platform does not provide large-format notifications, this method has no effect. The
+     * user will always see the normal notification view.
+     * <br>
+     * This class is a "rebuilder": It attaches to a Builder object and modifies its behavior, like
+     * so:
+     * <pre class="prettyprint">
+     *
+     * Notification noti = new Notification.Builder()
+     *     .setContentTitle(&quot;2 new messages wtih &quot; + sender.toString())
+     *     .setContentText(subject)
+     *     .setSmallIcon(R.drawable.new_message)
+     *     .setLargeIcon(aBitmap)
+     *     .setStyle(new Notification.MessagingStyle(resources.getString(R.string.reply_name))
+     *         .addMessage(messages[0].getText(), messages[0].getTime(), messages[0].getSender())
+     *         .addMessage(messages[1].getText(), messages[1].getTime(), messages[1].getSender()))
+     *     .build();
+     * </pre>
+     */
+    public static class MessagingStyle extends Style {
+
+        /**
+         * The maximum number of messages that will be retained in the Notification itself (the
+         * number displayed is up to the platform).
+         */
+        public static final int MAXIMUM_RETAINED_MESSAGES = 25;
+
+        CharSequence mUserDisplayName;
+        CharSequence mConversationTitle;
+        boolean mAllowGeneratedReplies = true;
+        ArrayList<Message> mMessages = new ArrayList<>();
+
+        MessagingStyle() {
+        }
+
+        /**
+         * @param userDisplayName the name to be displayed for any replies sent by the user before the
+         * posting app reposts the notification with those messages after they've been actually
+         * sent and in previous messages sent by the user added in
+         * {@link #addMessage(Notification.MessagingStyle.Message)}
+         */
+        public MessagingStyle(CharSequence userDisplayName) {
+            mUserDisplayName = userDisplayName;
+        }
+
+        /**
+         * Returns the name to be displayed for any replies sent by the user
+         */
+        public CharSequence getUserDisplayName() {
+            return mUserDisplayName;
+        }
+
+        /**
+         * Set whether the platform should automatically generate possible replies from messages.
+         * @param allowGeneratedReplies {@code true} to allow generated replies, {@code false}
+         * otherwise
+         * @return this object for method chaining
+         * The default value is {@code true}
+         */
+        public MessagingStyle setAllowGeneratedReplies(boolean allowGeneratedReplies) {
+            mAllowGeneratedReplies = allowGeneratedReplies;
+            return this;
+        }
+
+        /**
+         * Return whether the platform should automatically generate possible replies from messages.
+         */
+        public boolean getAllowGeneratedReplies() {
+            return mAllowGeneratedReplies;
+        }
+
+        /**
+         * Sets the title to be displayed on this conversation. This should only be used for
+         * group messaging and left unset for one-on-one conversations.
+         * @param conversationTitle
+         * @return this object for method chaining.
+         */
+        public MessagingStyle setConversationTitle(CharSequence conversationTitle) {
+            mConversationTitle = conversationTitle;
+            return this;
+        }
+
+        /**
+         * Return the title to be displayed on this conversation. Can be <code>null</code> and
+         * should be for one-on-one conversations
+         */
+        public CharSequence getConversationTitle() {
+            return mConversationTitle;
+        }
+
+        /**
+         * Adds a message for display by this notification. Convenience call for a simple
+         * {@link Message} in {@link #addMessage(Notification.MessagingStyle.Message)}.
+         * @param text A {@link CharSequence} to be displayed as the message content
+         * @param timestamp Time at which the message arrived
+         * @param sender A {@link CharSequence} to be used for displaying the name of the
+         * sender. Should be <code>null</code> for messages by the current user, in which case
+         * the platform will insert {@link #getUserDisplayName()}.
+         * Should be unique amongst all individuals in the conversation, and should be
+         * consistent during re-posts of the notification.
+         *
+         * @see Message#Message(CharSequence, long, CharSequence)
+         *
+         * @return this object for method chaining
+         */
+        public MessagingStyle addMessage(CharSequence text, long timestamp, CharSequence sender) {
+            mMessages.add(new Message(text, timestamp, sender));
+            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
+                mMessages.remove(0);
+            }
+            return this;
+        }
+
+        /**
+         * Adds a {@link Message} for display in this notification.
+         * @param message The {@link Message} to be displayed
+         * @return this object for method chaining
+         */
+        public MessagingStyle addMessage(Message message) {
+            mMessages.add(message);
+            if (mMessages.size() > MAXIMUM_RETAINED_MESSAGES) {
+                mMessages.remove(0);
+            }
+            return this;
+        }
+
+        /**
+         * Gets the list of {@code Message} objects that represent the notification
+         */
+        public List<Message> getMessages() {
+            return mMessages;
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        public void addExtras(Bundle extras) {
+            super.addExtras(extras);
+            if (mUserDisplayName != null) {
+                extras.putCharSequence(EXTRA_SELF_DISPLAY_NAME, mUserDisplayName);
+            }
+            if (mConversationTitle != null) {
+                extras.putCharSequence(EXTRA_THREAD_TITLE, mConversationTitle);
+            }
+            extras.putBoolean(EXTRA_ALLOW_GENERATED_REPLIES, mAllowGeneratedReplies);
+            if (!mMessages.isEmpty()) {
+                extras.putParcelableArrayList(EXTRA_MESSAGES, mMessages);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        @Override
+        protected void restoreFromExtras(Bundle extras) {
+            super.restoreFromExtras(extras);
+
+            mMessages.clear();
+            mUserDisplayName = extras.getString(EXTRA_SELF_DISPLAY_NAME);
+            mConversationTitle = extras.getString(EXTRA_THREAD_TITLE);
+            mAllowGeneratedReplies = extras.getBoolean(EXTRA_ALLOW_GENERATED_REPLIES,
+                    mAllowGeneratedReplies);
+            List<Message> messages = extras.getParcelableArrayList(EXTRA_MESSAGES);
+            if (messages != null) {
+                mMessages.addAll(messages);
+            }
+        }
+
+        /**
+         * @hide
+         */
+        public RemoteViews makeBigContentView() {
+            // TODO handset to write implementation
+            RemoteViews contentView = getStandardView(mBuilder.getBigTextLayoutResource());
+
+            return contentView;
+        }
+
+        public static final class Message implements Parcelable {
+
+            private final CharSequence mText;
+            private final long mTimestamp;
+            private final CharSequence mSender;
+
+            private String mDataMimeType;
+            private Uri mDataUri;
+
+            /**
+             * Constructor
+             * @param text A {@link CharSequence} to be displayed as the message content
+             * @param timestamp Time at which the message arrived
+             * @param sender A {@link CharSequence} to be used for displaying the name of the
+             * sender. Should be <code>null</code> for messages by the current user, in which case
+             * the platform will insert {@link MessagingStyle#getUserDisplayName()}.
+             * Should be unique amongst all individuals in the conversation, and should be
+             * consistent during re-posts of the notification.
+             */
+            public Message(CharSequence text, long timestamp, CharSequence sender){
+                mText = text;
+                mTimestamp = timestamp;
+                mSender = sender;
+            }
+
+            /**
+             * Sets a binary blob of data and an associated MIME type for a message. In the case
+             * where the platform doesn't support the MIME type, the original text provided in the
+             * constructor will be used.
+             * @param dataMimeType The MIME type of the content. See
+             * <a href="{@docRoot}notifications/messaging.html"> for the list of supported MIME
+             * types on Android and Android Wear.
+             * @param dataUri The uri containing the content whose type is given by the MIME type.
+             * <p class="note">
+             * <ol>
+             *   <li>Notification Listeners including the System UI need permission to access the
+             *       data the Uri points to. The recommended ways to do this are:</li>
+             *   <li>Store the data in your own ContentProvider, making sure that other apps have
+             *       the correct permission to access your provider. The preferred mechanism for
+             *       providing access is to use per-URI permissions which are temporary and only
+             *       grant access to the receiving application. An easy way to create a
+             *       ContentProvider like this is to use the FileProvider helper class.</li>
+             *   <li>Use the system MediaStore. The MediaStore is primarily aimed at video, audio
+             *       and image MIME types, however beginning with Android 3.0 (API level 11) it can
+             *       also store non-media types (see MediaStore.Files for more info). Files can be
+             *       inserted into the MediaStore using scanFile() after which a content:// style
+             *       Uri suitable for sharing is passed to the provided onScanCompleted() callback.
+             *       Note that once added to the system MediaStore the content is accessible to any
+             *       app on the device.</li>
+             * </ol>
+             * @return this object for method chaining
+             */
+            public Message setData(String dataMimeType, Uri dataUri) {
+                mDataMimeType = dataMimeType;
+                mDataUri = dataUri;
+                return this;
+            }
+
+            private Message(Parcel in) {
+                if (in.readInt() != 0) {
+                    mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+                } else {
+                    mText = null;
+                }
+                mTimestamp = in.readLong();
+                if (in.readInt() != 0) {
+                    mSender = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+                } else {
+                    mSender = null;
+                }
+                if (in.readInt() != 0) {
+                    mDataMimeType = in.readString();
+                }
+                if (in.readInt() != 0) {
+                    mDataUri = in.readParcelable(Uri.class.getClassLoader());
+                }
+            }
+
+            /**
+             * Get the text to be used for this message, or the fallback text if a type and content
+             * Uri have been set
+             */
+            public CharSequence getText() {
+                return mText;
+            }
+
+            /**
+             * Get the time at which this message arrived
+             */
+            public long getTimestamp() {
+                return mTimestamp;
+            }
+
+            /**
+             * Get the text used to display the contact's name in the messaging experience
+             */
+            public CharSequence getSender() {
+                return mSender;
+            }
+
+            /**
+             * Get the MIME type of the data pointed to by the Uri
+             */
+            public String getDataMimeType() {
+                return mDataMimeType;
+            }
+
+            /**
+             * Get the the Uri pointing to the content of the message. Can be null, in which case
+             * {@see #getText()} is used.
+             */
+            public Uri getDataUri() {
+                return mDataUri;
+            }
+
+            @Override
+            public int describeContents() {
+                return 0;
+            }
+
+            @Override
+            public void writeToParcel(Parcel out, int flags) {
+                if (mText != null) {
+                    out.writeInt(1);
+                    TextUtils.writeToParcel(mText, out, flags);
+                } else {
+                    out.writeInt(0);
+                }
+                out.writeLong(mTimestamp);
+                if (mSender != null) {
+                    out.writeInt(1);
+                    TextUtils.writeToParcel(mSender, out, flags);
+                } else {
+                    out.writeInt(0);
+                }
+                if (mDataMimeType != null) {
+                    out.writeInt(1);
+                    out.writeString(mDataMimeType);
+                } else {
+                    out.writeInt(0);
+                }
+                if (mDataUri != null) {
+                    out.writeInt(1);
+                    out.writeParcelable(mDataUri, flags);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+
+            public static final Parcelable.Creator<Message> CREATOR =
+                    new Parcelable.Creator<Message>() {
+                        public Message createFromParcel(Parcel in) {
+                            return new Message(in);
+                        }
+                        public Message[] newArray(int size) {
+                            return new Message[size];
+                        }
+                    };
+        }
+    }
+
+    /**
      * Helper class for generating large-format notifications that include a list of (up to 5) strings.
      *
      * Here's how you'd set the <code>InboxStyle</code> on a notification:
@@ -4863,6 +5274,7 @@
         private static final int FLAG_HINT_SHOW_BACKGROUND_ONLY = 1 << 2;
         private static final int FLAG_START_SCROLL_BOTTOM = 1 << 3;
         private static final int FLAG_HINT_AVOID_BACKGROUND_CLIPPING = 1 << 4;
+        private static final int FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY = 1 << 6;
 
         // Default value for flags integer
         private static final int DEFAULT_FLAGS = FLAG_CONTENT_INTENT_AVAILABLE_OFFLINE;
@@ -5429,6 +5841,29 @@
             return mHintScreenTimeout;
         }
 
+        /**
+         * Set a hint that this notification's content intent will launch an {@link Activity}
+         * directly, telling the platform that it can generate the appropriate transitions.
+         * @param hintContentIntentLaunchesActivity {@code true} if the content intent will launch
+         * an activity and transitions should be generated, false otherwise.
+         * @return this object for method chaining
+         */
+        public WearableExtender setHintContentIntentLaunchesActivity(
+                boolean hintContentIntentLaunchesActivity) {
+            setFlag(FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY, hintContentIntentLaunchesActivity);
+            return this;
+        }
+
+        /**
+         * Get a hint that this notification's content intent will launch an {@link Activity}
+         * directly, telling the platform that it can generate the appropriate transitions
+         * @return {@code true} if the content intent will launch an activity and transitions should
+         * be generated, false otherwise. The default value is {@code false} if this was never set.
+         */
+        public boolean getHintContentIntentLaunchesActivity() {
+            return (mFlags & FLAG_HINT_CONTENT_INTENT_LAUNCHES_ACTIVITY) != 0;
+        }
+
         private void setFlag(int mask, boolean value) {
             if (value) {
                 mFlags |= mask;
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 0d4729d..47bff64 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -153,25 +153,31 @@
 
     /**
      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
-     *     Normal interruption filter.
+     *     Normal interruption filter - no notifications are suppressed.
      */
     public static final int INTERRUPTION_FILTER_ALL = 1;
 
     /**
      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
-     *     Priority interruption filter.
+     *     Priority interruption filter - all notifications are suppressed except those that match
+     *     the priority criteria. Some audio streams are muted. See
+     *     {@link Policy#priorityCallSenders}, {@link Policy#priorityCategories},
+     *     {@link Policy#priorityMessageSenders} to define or query this criteria. Users can
+     *     additionally specify packages that can bypass this interruption filter.
      */
     public static final int INTERRUPTION_FILTER_PRIORITY = 2;
 
     /**
      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
-     *     No interruptions filter.
+     *     No interruptions filter - all notifications are suppressed and all audio streams (except
+     *     those used for phone calls) and vibrations are muted.
      */
     public static final int INTERRUPTION_FILTER_NONE = 3;
 
     /**
      * {@link #getCurrentInterruptionFilter() Interruption filter} constant -
-     *     Alarms only interruption filter.
+     *     Alarms only interruption filter - all notifications except those of category
+     *     {@link Notification#CATEGORY_ALARM} are suppressed. Some audio streams are muted.
      */
     public static final int INTERRUPTION_FILTER_ALARMS = 4;
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b924327..3f9629c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2216,9 +2216,7 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD}
      */
     public boolean resetPassword(String password, int flags) {
-        if (mParentInstance) {
-            throw new SecurityException("Reset password does not work across profiles.");
-        }
+        throwIfParentInstance("resetPassword");
         if (mService != null) {
             try {
                 return mService.resetPassword(password, flags);
@@ -2288,6 +2286,23 @@
     }
 
     /**
+     * Returns maximum time to lock that applied by all profiles in this user. We do this because we
+     * do not have a separate timeout to lock for work challenge only.
+     *
+     * @hide
+     */
+    public long getMaximumTimeToLockForUserAndProfiles(int userHandle) {
+        if (mService != null) {
+            try {
+                return mService.getMaximumTimeToLockForUserAndProfiles(userHandle);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return 0;
+    }
+
+    /**
      * Make the device lock immediately, as if the lock screen timeout has expired at the point of
      * this call.
      * <p>
@@ -2338,6 +2353,7 @@
      *             that uses {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}
      */
     public void wipeData(int flags) {
+        throwIfParentInstance("wipeData");
         if (mService != null) {
             try {
                 mService.wipeData(flags);
@@ -2371,6 +2387,7 @@
      */
     public ComponentName setGlobalProxy(@NonNull ComponentName admin, Proxy proxySpec,
             List<String> exclusionList ) {
+        throwIfParentInstance("setGlobalProxy");
         if (proxySpec == null) {
             throw new NullPointerException();
         }
@@ -2436,6 +2453,7 @@
      */
     public void setRecommendedGlobalProxy(@NonNull ComponentName admin, @Nullable ProxyInfo
             proxyInfo) {
+        throwIfParentInstance("setRecommendedGlobalProxy");
         if (mService != null) {
             try {
                 mService.setRecommendedGlobalProxy(admin, proxyInfo);
@@ -2586,6 +2604,7 @@
      *             {@link DeviceAdminInfo#USES_ENCRYPTED_STORAGE}
      */
     public int setStorageEncryption(@NonNull ComponentName admin, boolean encrypt) {
+        throwIfParentInstance("setStorageEncryption");
         if (mService != null) {
             try {
                 return mService.setStorageEncryption(admin, encrypt);
@@ -2606,6 +2625,7 @@
      * @return true if the admin(s) are requesting encryption, false if not.
      */
     public boolean getStorageEncryption(@Nullable ComponentName admin) {
+        throwIfParentInstance("getStorageEncryption");
         if (mService != null) {
             try {
                 return mService.getStorageEncryption(admin, myUserId());
@@ -2701,6 +2721,7 @@
      *         owner.
      */
     public boolean installCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("installCaCert");
         if (mService != null) {
             try {
                 return mService.installCaCert(admin, certBuffer);
@@ -2721,6 +2742,7 @@
      *         owner.
      */
     public void uninstallCaCert(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("uninstallCaCert");
         if (mService != null) {
             try {
                 final String alias = getCaCertAlias(certBuffer);
@@ -2746,6 +2768,7 @@
      */
     public List<byte[]> getInstalledCaCerts(@Nullable ComponentName admin) {
         List<byte[]> certs = new ArrayList<byte[]>();
+        throwIfParentInstance("getInstalledCaCerts");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2774,6 +2797,7 @@
      *         owner.
      */
     public void uninstallAllUserCaCerts(@Nullable ComponentName admin) {
+        throwIfParentInstance("uninstallAllUserCaCerts");
         if (mService != null) {
             try {
                 mService.uninstallCaCerts(admin, new TrustedCertificateStore().userAliases()
@@ -2794,6 +2818,7 @@
      *         owner.
      */
     public boolean hasCaCertInstalled(@Nullable ComponentName admin, byte[] certBuffer) {
+        throwIfParentInstance("hasCaCertInstalled");
         if (mService != null) {
             try {
                 mService.enforceCanManageCaCerts(admin);
@@ -2862,6 +2887,7 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
+        throwIfParentInstance("installKeyPair");
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -2894,6 +2920,7 @@
      *         owner.
      */
     public boolean removeKeyPair(@Nullable ComponentName admin, @NonNull String alias) {
+        throwIfParentInstance("removeKeyPair");
         try {
             return mService.removeKeyPair(admin, alias);
         } catch (RemoteException e) {
@@ -2934,6 +2961,7 @@
      */
     public void setCertInstallerPackage(@NonNull ComponentName admin, @Nullable String
             installerPackage) throws SecurityException {
+        throwIfParentInstance("setCertInstallerPackage");
         if (mService != null) {
             try {
                 mService.setCertInstallerPackage(admin, installerPackage);
@@ -2953,6 +2981,7 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getCertInstallerPackage(@NonNull ComponentName admin) throws SecurityException {
+        throwIfParentInstance("getCertInstallerPackage");
         if (mService != null) {
             try {
                 return mService.getCertInstallerPackage(admin);
@@ -2977,17 +3006,22 @@
      * @return {@code true} if the package is set as always-on VPN controller; {@code false}
      *         otherwise.
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
+     * @throws NameNotFoundException if {@code vpnPackage} is not installed.
+     * @throws UnsupportedOperationException if {@code vpnPackage} exists but does not support being
+     *         set as always-on, or if always-on VPN is not available.
      */
-    public boolean setAlwaysOnVpnPackage(@NonNull ComponentName admin,
-            @Nullable String vpnPackage) {
+    public void setAlwaysOnVpnPackage(@NonNull ComponentName admin, @Nullable String vpnPackage)
+            throws NameNotFoundException, UnsupportedOperationException {
+        throwIfParentInstance("setAlwaysOnVpnPackage");
         if (mService != null) {
             try {
-                return mService.setAlwaysOnVpnPackage(admin, vpnPackage);
+                if (!mService.setAlwaysOnVpnPackage(admin, vpnPackage)) {
+                    throw new NameNotFoundException(vpnPackage);
+                }
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
         }
-        return false;
     }
 
     /**
@@ -3000,6 +3034,7 @@
      * @throws SecurityException if {@code admin} is not a device or a profile owner.
      */
     public String getAlwaysOnVpnPackage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getAlwaysOnVpnPackage");
         if (mService != null) {
             try {
                 return mService.getAlwaysOnVpnPackage(admin);
@@ -3027,6 +3062,7 @@
      *             {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
      */
     public void setCameraDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setCameraDisabled");
         if (mService != null) {
             try {
                 mService.setCameraDisabled(admin, disabled);
@@ -3043,6 +3079,7 @@
      * have disabled the camera
      */
     public boolean getCameraDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getCameraDisabled");
         return getCameraDisabled(admin, myUserId());
     }
 
@@ -3072,6 +3109,7 @@
      *             than the one managed by the device owner.
      */
     public boolean requestBugreport(@NonNull ComponentName admin) {
+        throwIfParentInstance("requestBugreport");
         if (mService != null) {
             try {
                 return mService.requestBugreport(admin);
@@ -3110,6 +3148,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setScreenCaptureDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setScreenCaptureDisabled");
         if (mService != null) {
             try {
                 mService.setScreenCaptureDisabled(admin, disabled);
@@ -3126,6 +3165,7 @@
      * have disabled screen capture.
      */
     public boolean getScreenCaptureDisabled(@Nullable ComponentName admin) {
+        throwIfParentInstance("getScreenCaptureDisabled");
         return getScreenCaptureDisabled(admin, myUserId());
     }
 
@@ -3155,6 +3195,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setAutoTimeRequired(@NonNull ComponentName admin, boolean required) {
+        throwIfParentInstance("setAutoTimeRequired");
         if (mService != null) {
             try {
                 mService.setAutoTimeRequired(admin, required);
@@ -3168,6 +3209,7 @@
      * @return true if auto time is required.
      */
     public boolean getAutoTimeRequired() {
+        throwIfParentInstance("getAutoTimeRequired");
         if (mService != null) {
             try {
                 return mService.getAutoTimeRequired();
@@ -3194,6 +3236,7 @@
      */
     public void setForceEphemeralUsers(
             @NonNull ComponentName admin, boolean forceEphemeralUsers) {
+        throwIfParentInstance("setForceEphemeralUsers");
         if (mService != null) {
             try {
                 mService.setForceEphemeralUsers(admin, forceEphemeralUsers);
@@ -3209,6 +3252,7 @@
      * @hide
      */
     public boolean getForceEphemeralUsers(@NonNull ComponentName admin) {
+        throwIfParentInstance("getForceEphemeralUsers");
         if (mService != null) {
             try {
                 return mService.getForceEphemeralUsers(admin);
@@ -3496,6 +3540,7 @@
      * @return whether or not the package is registered as the device owner app.
      */
     public boolean isDeviceOwnerApp(String packageName) {
+        throwIfParentInstance("isDeviceOwnerApp");
         return isDeviceOwnerAppOnCallingUser(packageName);
     }
 
@@ -3593,6 +3638,7 @@
      *             does not own the current device owner component.
      */
     public void clearDeviceOwnerApp(String packageName) {
+        throwIfParentInstance("clearDeviceOwnerApp");
         if (mService != null) {
             try {
                 mService.clearDeviceOwner(packageName);
@@ -3710,6 +3756,7 @@
      * @throws SecurityException if {@code admin} is not an active profile owner.
      */
     public void clearProfileOwner(@NonNull ComponentName admin) {
+        throwIfParentInstance("clearProfileOwner");
         if (mService != null) {
             try {
                 mService.clearProfileOwner(admin);
@@ -3783,6 +3830,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setDeviceOwnerLockScreenInfo(@NonNull ComponentName admin, CharSequence info) {
+        throwIfParentInstance("setDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 mService.setDeviceOwnerLockScreenInfo(admin, info);
@@ -3796,6 +3844,7 @@
      * @return The device owner information. If it is not set returns {@code null}.
      */
     public CharSequence getDeviceOwnerLockScreenInfo() {
+        throwIfParentInstance("getDeviceOwnerLockScreenInfo");
         if (mService != null) {
             try {
                 return mService.getDeviceOwnerLockScreenInfo();
@@ -3827,6 +3876,7 @@
      */
     public String[] setPackagesSuspended(@NonNull ComponentName admin, String[] packageNames,
             boolean suspended) {
+        throwIfParentInstance("setPackagesSuspended");
         if (mService != null) {
             try {
                 return mService.setPackagesSuspended(admin, packageNames, suspended);
@@ -3849,6 +3899,7 @@
      */
     public boolean isPackageSuspended(@NonNull ComponentName admin, String packageName)
             throws NameNotFoundException {
+        throwIfParentInstance("isPackageSuspended");
         if (mService != null) {
             try {
                 return mService.isPackageSuspended(admin, packageName);
@@ -3870,6 +3921,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setProfileEnabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("setProfileEnabled");
         if (mService != null) {
             try {
                 mService.setProfileEnabled(admin);
@@ -3891,6 +3943,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setProfileName(@NonNull ComponentName admin, String profileName) {
+        throwIfParentInstance("setProfileName");
         if (mService != null) {
             try {
                 mService.setProfileName(admin, profileName);
@@ -3909,6 +3962,7 @@
      * @return Whether or not the package is registered as the profile owner.
      */
     public boolean isProfileOwnerApp(String packageName) {
+        throwIfParentInstance("isProfileOwnerApp");
         if (mService != null) {
             try {
                 ComponentName profileOwner = mService.getProfileOwner(myUserId());
@@ -4003,6 +4057,7 @@
      */
     public void addPersistentPreferredActivity(@NonNull ComponentName admin, IntentFilter filter,
             @NonNull ComponentName activity) {
+        throwIfParentInstance("addPersistentPreferredActivity");
         if (mService != null) {
             try {
                 mService.addPersistentPreferredActivity(admin, filter, activity);
@@ -4025,6 +4080,7 @@
      */
     public void clearPackagePersistentPreferredActivities(@NonNull ComponentName admin,
             String packageName) {
+        throwIfParentInstance("clearPackagePersistentPreferredActivities");
         if (mService != null) {
             try {
                 mService.clearPackagePersistentPreferredActivities(admin, packageName);
@@ -4053,6 +4109,7 @@
      */
     public void setApplicationRestrictionsManagingPackage(@NonNull ComponentName admin,
             @Nullable String packageName) throws NameNotFoundException {
+        throwIfParentInstance("setApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 if (!mService.setApplicationRestrictionsManagingPackage(admin, packageName)) {
@@ -4074,6 +4131,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public String getApplicationRestrictionsManagingPackage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictionsManagingPackage(admin);
@@ -4093,6 +4151,7 @@
      * that method.
      */
     public boolean isCallerApplicationRestrictionsManagingPackage() {
+        throwIfParentInstance("isCallerApplicationRestrictionsManagingPackage");
         if (mService != null) {
             try {
                 return mService.isCallerApplicationRestrictionsManagingPackage();
@@ -4138,6 +4197,7 @@
      */
     public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
             Bundle settings) {
+        throwIfParentInstance("setApplicationRestrictions");
         if (mService != null) {
             try {
                 mService.setApplicationRestrictions(admin, packageName, settings);
@@ -4156,6 +4216,10 @@
      * The calling device admin must have requested
      * {@link DeviceAdminInfo#USES_POLICY_DISABLE_KEYGUARD_FEATURES} to be able to call this method;
      * if not, a security exception will be thrown.
+     * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance returned by
+     * {@link #getParentProfileInstance(ComponentName)} in order to set the configuration for
+     * the parent profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param target Component name of the agent to be enabled.
@@ -4176,7 +4240,7 @@
             @NonNull ComponentName target, PersistableBundle configuration) {
         if (mService != null) {
             try {
-                mService.setTrustAgentConfiguration(admin, target, configuration);
+                mService.setTrustAgentConfiguration(admin, target, configuration, mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4187,6 +4251,10 @@
      * Gets configuration for the given trust agent based on aggregating all calls to
      * {@link #setTrustAgentConfiguration(ComponentName, ComponentName, PersistableBundle)} for
      * all device admins.
+     * <p>
+     * This method can be called on the {@link DevicePolicyManager} instance returned by
+     * {@link #getParentProfileInstance(ComponentName)} in order to retrieve the configuration set
+     * on the parent profile.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with. If null,
      * this function returns a list of configurations for all admins that declare
@@ -4207,7 +4275,8 @@
             @NonNull ComponentName agent, int userHandle) {
         if (mService != null) {
             try {
-                return mService.getTrustAgentConfiguration(admin, agent, userHandle);
+                return mService.getTrustAgentConfiguration(admin, agent, userHandle,
+                        mParentInstance);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -4227,6 +4296,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setCrossProfileCallerIdDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileCallerIdDisabled(admin, disabled);
@@ -4247,6 +4317,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileCallerIdDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileCallerIdDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileCallerIdDisabled(admin);
@@ -4287,6 +4358,7 @@
      */
     public void setCrossProfileContactsSearchDisabled(@NonNull ComponentName admin,
             boolean disabled) {
+        throwIfParentInstance("setCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 mService.setCrossProfileContactsSearchDisabled(admin, disabled);
@@ -4307,6 +4379,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getCrossProfileContactsSearchDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileContactsSearchDisabled");
         if (mService != null) {
             try {
                 return mService.getCrossProfileContactsSearchDisabled(admin);
@@ -4377,6 +4450,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setBluetoothContactSharingDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 mService.setBluetoothContactSharingDisabled(admin, disabled);
@@ -4399,6 +4473,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean getBluetoothContactSharingDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getBluetoothContactSharingDisabled");
         if (mService != null) {
             try {
                 return mService.getBluetoothContactSharingDisabled(admin);
@@ -4442,6 +4517,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addCrossProfileIntentFilter(@NonNull ComponentName admin, IntentFilter filter, int flags) {
+        throwIfParentInstance("addCrossProfileIntentFilter");
         if (mService != null) {
             try {
                 mService.addCrossProfileIntentFilter(admin, filter, flags);
@@ -4460,6 +4536,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearCrossProfileIntentFilters(@NonNull ComponentName admin) {
+        throwIfParentInstance("clearCrossProfileIntentFilters");
         if (mService != null) {
             try {
                 mService.clearCrossProfileIntentFilters(admin);
@@ -4489,6 +4566,7 @@
      */
     public boolean setPermittedAccessibilityServices(@NonNull ComponentName admin,
             List<String> packageNames) {
+        throwIfParentInstance("setPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.setPermittedAccessibilityServices(admin, packageNames);
@@ -4510,6 +4588,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedAccessibilityServices(@NonNull ComponentName admin) {
+        throwIfParentInstance("getPermittedAccessibilityServices");
         if (mService != null) {
             try {
                 return mService.getPermittedAccessibilityServices(admin);
@@ -4587,6 +4666,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean setPermittedInputMethods(@NonNull ComponentName admin, List<String> packageNames) {
+        throwIfParentInstance("setPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.setPermittedInputMethods(admin, packageNames);
@@ -4609,6 +4689,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
+        throwIfParentInstance("getPermittedInputMethods");
         if (mService != null) {
             try {
                 return mService.getPermittedInputMethods(admin);
@@ -4804,6 +4885,7 @@
     public UserHandle createAndManageUser(@NonNull ComponentName admin, @NonNull String name,
             @NonNull ComponentName profileOwner, @Nullable PersistableBundle adminExtras,
             int flags) {
+        throwIfParentInstance("createAndManageUser");
         try {
             return mService.createAndManageUser(admin, name, profileOwner, adminExtras, flags);
         } catch (RemoteException re) {
@@ -4821,6 +4903,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean removeUser(@NonNull ComponentName admin, UserHandle userHandle) {
+        throwIfParentInstance("removeUser");
         try {
             return mService.removeUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4838,6 +4921,7 @@
      * @see Intent#ACTION_USER_FOREGROUND
      */
     public boolean switchUser(@NonNull ComponentName admin, @Nullable UserHandle userHandle) {
+        throwIfParentInstance("switchUser");
         try {
             return mService.switchUser(admin, userHandle);
         } catch (RemoteException re) {
@@ -4863,6 +4947,7 @@
      * @see {@link #setApplicationRestrictionsManagingPackage}
      */
     public Bundle getApplicationRestrictions(@Nullable ComponentName admin, String packageName) {
+        throwIfParentInstance("getApplicationRestrictions");
         if (mService != null) {
             try {
                 return mService.getApplicationRestrictions(admin, packageName);
@@ -4885,6 +4970,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void addUserRestriction(@NonNull ComponentName admin, String key) {
+        throwIfParentInstance("addUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, true);
@@ -4906,6 +4992,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void clearUserRestriction(@NonNull ComponentName admin, String key) {
+        throwIfParentInstance("clearUserRestriction");
         if (mService != null) {
             try {
                 mService.setUserRestriction(admin, key, false);
@@ -4927,6 +5014,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public Bundle getUserRestrictions(@NonNull ComponentName admin) {
+        throwIfParentInstance("getUserRestrictions");
         Bundle ret = null;
         if (mService != null) {
             try {
@@ -4971,6 +5059,7 @@
      */
     public boolean setApplicationHidden(@NonNull ComponentName admin, String packageName,
             boolean hidden) {
+        throwIfParentInstance("setApplicationHidden");
         if (mService != null) {
             try {
                 return mService.setApplicationHidden(admin, packageName, hidden);
@@ -4990,6 +5079,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isApplicationHidden(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("isApplicationHidden");
         if (mService != null) {
             try {
                 return mService.isApplicationHidden(admin, packageName);
@@ -5009,6 +5099,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void enableSystemApp(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 mService.enableSystemApp(admin, packageName);
@@ -5029,6 +5120,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public int enableSystemApp(@NonNull ComponentName admin, Intent intent) {
+        throwIfParentInstance("enableSystemApp");
         if (mService != null) {
             try {
                 return mService.enableSystemAppWithIntent(admin, intent);
@@ -5061,6 +5153,7 @@
      */
     public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
             boolean disabled) {
+        throwIfParentInstance("setAccountManagementDisabled");
         if (mService != null) {
             try {
                 mService.setAccountManagementDisabled(admin, accountType, disabled);
@@ -5081,6 +5174,7 @@
      * @see #setAccountManagementDisabled
      */
     public String[] getAccountTypesWithManagementDisabled() {
+        throwIfParentInstance("getAccountTypesWithManagementDisabled");
         return getAccountTypesWithManagementDisabledAsUser(myUserId());
     }
 
@@ -5118,6 +5212,7 @@
      */
     public void setLockTaskPackages(@NonNull ComponentName admin, String[] packages)
             throws SecurityException {
+        throwIfParentInstance("setLockTaskPackages");
         if (mService != null) {
             try {
                 mService.setLockTaskPackages(admin, packages);
@@ -5134,6 +5229,7 @@
      * @hide
      */
     public String[] getLockTaskPackages(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLockTaskPackages");
         if (mService != null) {
             try {
                 return mService.getLockTaskPackages(admin);
@@ -5150,6 +5246,7 @@
      * @param pkg The package to check
      */
     public boolean isLockTaskPermitted(String pkg) {
+        throwIfParentInstance("isLockTaskPermitted");
         if (mService != null) {
             try {
                 return mService.isLockTaskPermitted(pkg);
@@ -5198,6 +5295,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public void setGlobalSetting(@NonNull ComponentName admin, String setting, String value) {
+        throwIfParentInstance("setGlobalSetting");
         if (mService != null) {
             try {
                 mService.setGlobalSetting(admin, setting, value);
@@ -5230,6 +5328,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setSecureSetting(@NonNull ComponentName admin, String setting, String value) {
+        throwIfParentInstance("setSecureSetting");
         if (mService != null) {
             try {
                 mService.setSecureSetting(admin, setting, value);
@@ -5253,6 +5352,7 @@
      */
     public void setRestrictionsProvider(@NonNull ComponentName admin,
             @Nullable ComponentName provider) {
+        throwIfParentInstance("setRestrictionsProvider");
         if (mService != null) {
             try {
                 mService.setRestrictionsProvider(admin, provider);
@@ -5270,6 +5370,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setMasterVolumeMuted(@NonNull ComponentName admin, boolean on) {
+        throwIfParentInstance("setMasterVolumeMuted");
         if (mService != null) {
             try {
                 mService.setMasterVolumeMuted(admin, on);
@@ -5287,6 +5388,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isMasterVolumeMuted(@NonNull ComponentName admin) {
+        throwIfParentInstance("isMasterVolumeMuted");
         if (mService != null) {
             try {
                 return mService.isMasterVolumeMuted(admin);
@@ -5307,6 +5409,7 @@
      */
     public void setUninstallBlocked(@NonNull ComponentName admin, String packageName,
             boolean uninstallBlocked) {
+        throwIfParentInstance("setUninstallBlocked");
         if (mService != null) {
             try {
                 mService.setUninstallBlocked(admin, packageName, uninstallBlocked);
@@ -5332,6 +5435,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public boolean isUninstallBlocked(@Nullable ComponentName admin, String packageName) {
+        throwIfParentInstance("isUninstallBlocked");
         if (mService != null) {
             try {
                 return mService.isUninstallBlocked(admin, packageName);
@@ -5359,6 +5463,7 @@
      * @see #getCrossProfileWidgetProviders(android.content.ComponentName)
      */
     public boolean addCrossProfileWidgetProvider(@NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("addCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.addCrossProfileWidgetProvider(admin, packageName);
@@ -5386,6 +5491,7 @@
      */
     public boolean removeCrossProfileWidgetProvider(
             @NonNull ComponentName admin, String packageName) {
+        throwIfParentInstance("removeCrossProfileWidgetProvider");
         if (mService != null) {
             try {
                 return mService.removeCrossProfileWidgetProvider(admin, packageName);
@@ -5407,6 +5513,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public List<String> getCrossProfileWidgetProviders(@NonNull ComponentName admin) {
+        throwIfParentInstance("getCrossProfileWidgetProviders");
         if (mService != null) {
             try {
                 List<String> providers = mService.getCrossProfileWidgetProviders(admin);
@@ -5428,6 +5535,7 @@
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setUserIcon(@NonNull ComponentName admin, Bitmap icon) {
+        throwIfParentInstance("setUserIcon");
         try {
             mService.setUserIcon(admin, icon);
         } catch (RemoteException re) {
@@ -5447,6 +5555,7 @@
      * @see SystemUpdatePolicy
      */
     public void setSystemUpdatePolicy(@NonNull ComponentName admin, SystemUpdatePolicy policy) {
+        throwIfParentInstance("setSystemUpdatePolicy");
         if (mService != null) {
             try {
                 mService.setSystemUpdatePolicy(admin, policy);
@@ -5462,6 +5571,7 @@
      * @return The current policy object, or {@code null} if no policy is set.
      */
     public SystemUpdatePolicy getSystemUpdatePolicy() {
+        throwIfParentInstance("getSystemUpdatePolicy");
         if (mService != null) {
             try {
                 return mService.getSystemUpdatePolicy();
@@ -5487,6 +5597,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setKeyguardDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setKeyguardDisabled");
         try {
             return mService.setKeyguardDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5505,6 +5616,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean setStatusBarDisabled(@NonNull ComponentName admin, boolean disabled) {
+        throwIfParentInstance("setStatusBarDisabled");
         try {
             return mService.setStatusBarDisabled(admin, disabled);
         } catch (RemoteException re) {
@@ -5550,6 +5662,7 @@
      * @see #setPermissionGrantState
      */
     public void setPermissionPolicy(@NonNull ComponentName admin, int policy) {
+        throwIfParentInstance("setPermissionPolicy");
         try {
             mService.setPermissionPolicy(admin, policy);
         } catch (RemoteException re) {
@@ -5564,6 +5677,7 @@
      * @return the current policy for future permission requests.
      */
     public int getPermissionPolicy(ComponentName admin) {
+        throwIfParentInstance("getPermissionPolicy");
         try {
             return mService.getPermissionPolicy(admin);
         } catch (RemoteException re) {
@@ -5600,6 +5714,7 @@
      */
     public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission, int grantState) {
+        throwIfParentInstance("setPermissionGrantState");
         try {
             return mService.setPermissionGrantState(admin, packageName, permission, grantState);
         } catch (RemoteException re) {
@@ -5628,6 +5743,7 @@
      */
     public int getPermissionGrantState(@NonNull ComponentName admin, String packageName,
             String permission) {
+        throwIfParentInstance("getPermissionGrantState");
         try {
             return mService.getPermissionGrantState(admin, packageName, permission);
         } catch (RemoteException re) {
@@ -5643,6 +5759,7 @@
      * @throws IllegalArgumentException if the supplied action is not valid.
      */
     public boolean isProvisioningAllowed(String action) {
+        throwIfParentInstance("isProvisioningAllowed");
         try {
             return mService.isProvisioningAllowed(action);
         } catch (RemoteException re) {
@@ -5658,6 +5775,7 @@
      * @return if this user is a managed profile of another user.
      */
     public boolean isManagedProfile(@NonNull ComponentName admin) {
+        throwIfParentInstance("isManagedProfile");
         try {
             return mService.isManagedProfile(admin);
         } catch (RemoteException re) {
@@ -5691,6 +5809,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public String getWifiMacAddress(@NonNull ComponentName admin) {
+        throwIfParentInstance("getWifiMacAddress");
         try {
             return mService.getWifiMacAddress(admin);
         } catch (RemoteException re) {
@@ -5707,6 +5826,7 @@
      * @see TelephonyManager#CALL_STATE_IDLE
      */
     public void reboot(@NonNull ComponentName admin) {
+        throwIfParentInstance("reboot");
         try {
             mService.reboot(admin);
         } catch (RemoteException re) {
@@ -5733,6 +5853,7 @@
      */
     public void setShortSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
+        throwIfParentInstance("setShortSupportMessage");
         if (mService != null) {
             try {
                 mService.setShortSupportMessage(admin, message);
@@ -5751,6 +5872,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getShortSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getShortSupportMessage");
         if (mService != null) {
             try {
                 return mService.getShortSupportMessage(admin);
@@ -5777,6 +5899,7 @@
      */
     public void setLongSupportMessage(@NonNull ComponentName admin,
             @Nullable String message) {
+        throwIfParentInstance("setLongSupportMessage");
         if (mService != null) {
             try {
                 mService.setLongSupportMessage(admin, message);
@@ -5795,6 +5918,7 @@
      * @throws SecurityException if {@code admin} is not an active administrator.
      */
     public String getLongSupportMessage(@NonNull ComponentName admin) {
+        throwIfParentInstance("getLongSupportMessage");
         if (mService != null) {
             try {
                 return mService.getLongSupportMessage(admin);
@@ -5849,13 +5973,49 @@
     /**
      * Called by the profile owner of a managed profile to obtain a {@link DevicePolicyManager}
      * whose calls act on the parent profile.
-     * <p>
-     * Note only some methods will work on the parent Manager.
+     *
+     * <p>The following methods are supported for the parent instance, all other methods will
+     * throw a SecurityException when called on the parent instance:
+     * <ul>
+     * <li>{@link #getPasswordQuality}</li>
+     * <li>{@link #setPasswordQuality}</li>
+     * <li>{@link #getPasswordMinimumLength}</li>
+     * <li>{@link #setPasswordMinimumLength}</li>
+     * <li>{@link #getPasswordMinimumUpperCase}</li>
+     * <li>{@link #setPasswordMinimumUpperCase}</li>
+     * <li>{@link #getPasswordMinimumLowerCase}</li>
+     * <li>{@link #setPasswordMinimumLowerCase}</li>
+     * <li>{@link #getPasswordMinimumLetters}</li>
+     * <li>{@link #setPasswordMinimumLetters}</li>
+     * <li>{@link #getPasswordMinimumNumeric}</li>
+     * <li>{@link #setPasswordMinimumNumeric}</li>
+     * <li>{@link #getPasswordMinimumSymbols}</li>
+     * <li>{@link #setPasswordMinimumSymbols}</li>
+     * <li>{@link #getPasswordMinimumNonLetter}</li>
+     * <li>{@link #setPasswordMinimumNonLetter}</li>
+     * <li>{@link #getPasswordHistoryLength}</li>
+     * <li>{@link #setPasswordHistoryLength}</li>
+     * <li>{@link #getPasswordExpirationTimeout}</li>
+     * <li>{@link #setPasswordExpirationTimeout}</li>
+     * <li>{@link #getPasswordExpiration}</li>
+     * <li>{@link #isActivePasswordSufficient}</li>
+     * <li>{@link #getCurrentFailedPasswordAttempts}</li>
+     * <li>{@link #getMaximumFailedPasswordsForWipe}</li>
+     * <li>{@link #setMaximumFailedPasswordsForWipe}</li>
+     * <li>{@link #getMaximumTimeToLock}</li>
+     * <li>{@link #setMaximumTimeToLock}</li>
+     * <li>{@link #lockNow}</li>
+     * <li>{@link #getKeyguardDisabledFeatures}</li>
+     * <li>{@link #setKeyguardDisabledFeatures}</li>
+     * <li>{@link #getTrustAgentConfiguration}</li>
+     * <li>{@link #setTrustAgentConfiguration}</li>
+     * </ul>
      *
      * @return a new instance of {@link DevicePolicyManager} that acts on the parent profile.
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
+        throwIfParentInstance("getParentProfileInstance");
         try {
             if (!mService.isManagedProfile(admin)) {
                 throw new SecurityException("The current user does not have a parent profile.");
@@ -5882,6 +6042,7 @@
      * @see #retrieveSecurityLogs
      */
     public void setSecurityLoggingEnabled(@NonNull ComponentName admin, boolean enabled) {
+        throwIfParentInstance("setSecurityLoggingEnabled");
         try {
             mService.setSecurityLoggingEnabled(admin, enabled);
         } catch (RemoteException re) {
@@ -5900,6 +6061,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public boolean isSecurityLoggingEnabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("isSecurityLoggingEnabled");
         try {
             return mService.isSecurityLoggingEnabled(admin);
         } catch (RemoteException re) {
@@ -5923,6 +6085,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrieveSecurityLogs(@NonNull ComponentName admin) {
+        throwIfParentInstance("retrieveSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrieveSecurityLogs(admin);
             if (list != null) {
@@ -5968,6 +6131,7 @@
      * @throws SecurityException if {@code admin} is not a device owner.
      */
     public List<SecurityEvent> retrievePreRebootSecurityLogs(@NonNull ComponentName admin) {
+        throwIfParentInstance("retrievePreRebootSecurityLogs");
         try {
             ParceledListSlice<SecurityEvent> list = mService.retrievePreRebootSecurityLogs(admin);
             return list.getList();
@@ -5989,6 +6153,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationColor(@NonNull ComponentName admin, int color) {
+        throwIfParentInstance("setOrganizationColor");
         try {
             mService.setOrganizationColor(admin, color);
         } catch (RemoteException re) {
@@ -6024,6 +6189,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public int getOrganizationColor(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationColor");
         try {
             return mService.getOrganizationColor(admin);
         } catch (RemoteException re) {
@@ -6059,6 +6225,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public void setOrganizationName(@NonNull ComponentName admin, @Nullable String title) {
+        throwIfParentInstance("setOrganizationName");
         try {
             mService.setOrganizationName(admin, title);
         } catch (RemoteException re) {
@@ -6075,6 +6242,7 @@
      * @throws SecurityException if {@code admin} is not a profile owner.
      */
     public String getOrganizationName(@NonNull ComponentName admin) {
+        throwIfParentInstance("getOrganizationName");
         try {
             return mService.getOrganizationName(admin);
         } catch (RemoteException re) {
@@ -6200,4 +6368,10 @@
             throw re.rethrowFromSystemServer();
         }
     }
+
+    private void throwIfParentInstance(String functionName) {
+        if (mParentInstance) {
+            throw new SecurityException(functionName + " cannot be called on the parent instance");
+        }
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 6ee56aa..6df1038 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -80,6 +80,7 @@
 
     void setMaximumTimeToLock(in ComponentName who, long timeMs, boolean parent);
     long getMaximumTimeToLock(in ComponentName who, int userHandle, boolean parent);
+    long getMaximumTimeToLockForUserAndProfiles(int userHandle);
 
     void lockNow(boolean parent);
 
@@ -228,9 +229,9 @@
     boolean getBluetoothContactSharingDisabledForUser(int userId);
 
     void setTrustAgentConfiguration(in ComponentName admin, in ComponentName agent,
-            in PersistableBundle args);
+            in PersistableBundle args, boolean parent);
     List<PersistableBundle> getTrustAgentConfiguration(in ComponentName admin,
-            in ComponentName agent, int userId);
+            in ComponentName agent, int userId, boolean parent);
 
     boolean addCrossProfileWidgetProvider(in ComponentName admin, String packageName);
     boolean removeCrossProfileWidgetProvider(in ComponentName admin, String packageName);
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index 95a8ccf..77307b7 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -27,6 +27,8 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.lang.ref.WeakReference;
+
 /**
  * <p>Entry point for the callback from the {@link android.app.job.JobScheduler}.</p>
  * <p>This is the base class that handles asynchronous requests that were previously scheduled. You
@@ -62,15 +64,15 @@
      * Identifier for a message that will result in a call to
      * {@link #onStartJob(android.app.job.JobParameters)}.
      */
-    private final int MSG_EXECUTE_JOB = 0;
+    private static final int MSG_EXECUTE_JOB = 0;
     /**
      * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
      */
-    private final int MSG_STOP_JOB = 1;
+    private static final int MSG_STOP_JOB = 1;
     /**
      * Message that the client has completed execution of this job.
      */
-    private final int MSG_JOB_FINISHED = 2;
+    private static final int MSG_JOB_FINISHED = 2;
 
     /** Lock object for {@link #mHandler}. */
     private final Object mHandlerLock = new Object();
@@ -82,21 +84,36 @@
     @GuardedBy("mHandlerLock")
     JobHandler mHandler;
 
-    /** Binder for this service. */
-    IJobService mBinder = new IJobService.Stub() {
-        @Override
-        public void startJob(JobParameters jobParams) {
-            ensureHandler();
-            Message m = Message.obtain(mHandler, MSG_EXECUTE_JOB, jobParams);
-            m.sendToTarget();
+    static final class JobInterface extends IJobService.Stub {
+        final WeakReference<JobService> mService;
+
+        JobInterface(JobService service) {
+            mService = new WeakReference<>(service);
         }
+
         @Override
-        public void stopJob(JobParameters jobParams) {
-            ensureHandler();
-            Message m = Message.obtain(mHandler, MSG_STOP_JOB, jobParams);
-            m.sendToTarget();
+        public void startJob(JobParameters jobParams) throws RemoteException {
+            JobService service = mService.get();
+            if (service != null) {
+                service.ensureHandler();
+                Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
+                m.sendToTarget();
+            }
         }
-    };
+
+        @Override
+        public void stopJob(JobParameters jobParams) throws RemoteException {
+            JobService service = mService.get();
+            if (service != null) {
+                service.ensureHandler();
+                Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
+                m.sendToTarget();
+            }
+
+        }
+    }
+
+    IJobService mBinder;
 
     /** @hide */
     void ensureHandler() {
@@ -194,6 +211,9 @@
 
     /** @hide */
     public final IBinder onBind(Intent intent) {
+        if (mBinder == null) {
+            mBinder = new JobInterface(this);
+        }
         return mBinder.asBinder();
     }
 
diff --git a/core/java/android/content/ClipDescription.java b/core/java/android/content/ClipDescription.java
index 1b024e2..461d1e0 100644
--- a/core/java/android/content/ClipDescription.java
+++ b/core/java/android/content/ClipDescription.java
@@ -71,6 +71,7 @@
      * and {@link ComponentName#flattenToString()} to convert the extra value
      * to/from {@link ComponentName}.
      * </p>
+     * @hide
      */
     public static final String EXTRA_TARGET_COMPONENT_NAME =
             "android.content.extra.TARGET_COMPONENT_NAME";
@@ -81,6 +82,7 @@
      * <p>
      * Type: long
      * </p>
+     * @hide
      */
     public static final String EXTRA_USER_SERIAL_NUMBER =
             "android.content.extra.USER_SERIAL_NUMBER";
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index cd67b3e..4db4567 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -17,6 +17,7 @@
 package android.content;
 
 import android.accounts.Account;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -60,6 +61,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Random;
@@ -289,6 +292,31 @@
     /** @hide */
     public static final int SYNC_OBSERVER_TYPE_ALL = 0x7fffffff;
 
+    /** @hide */
+    @IntDef(flag = true,
+            value = {
+                NOTIFY_SYNC_TO_NETWORK,
+                NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface NotifyFlags {}
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: attempt to sync the change
+     * to the network.
+     */
+    public static final int NOTIFY_SYNC_TO_NETWORK = 1<<0;
+
+    /**
+     * Flag for {@link #notifyChange(Uri, ContentObserver, int)}: if set, this notification
+     * will be skipped if it is being delivered to the root URI of a ContentObserver that is
+     * using "notify for descendants."  The purpose of this is to allow the provide to send
+     * a general notification of "something under X" changed that observers of that specific
+     * URI can receive, while also sending a specific URI under X.  It would use this flag
+     * when sending the former, so that observers of "X and descendants" only see the latter.
+     */
+    public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
+
     // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
     private static final boolean ENABLE_CONTENT_SAMPLE = false;
@@ -1676,7 +1704,7 @@
      * The observer that originated the change will only receive the notification if it
      * has requested to receive self-change notifications by implementing
      * {@link ContentObserver#deliverSelfNotifications()} to return true.
-     * @param syncToNetwork If true, attempt to sync the change to the network.
+     * @param syncToNetwork If true, same as {@link #NOTIFY_SYNC_TO_NETWORK}.
      * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
      */
     public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
@@ -1690,6 +1718,32 @@
     }
 
     /**
+     * Notify registered observers that a row was updated.
+     * To register, call {@link #registerContentObserver(android.net.Uri, boolean, android.database.ContentObserver) registerContentObserver()}.
+     * By default, CursorAdapter objects will get this notification.
+     * If syncToNetwork is true, this will attempt to schedule a local sync using the sync
+     * adapter that's registered for the authority of the provided uri. No account will be
+     * passed to the sync adapter, so all matching accounts will be synchronized.
+     *
+     * @param uri The uri of the content that was changed.
+     * @param observer The observer that originated the change, may be <code>null</null>.
+     * The observer that originated the change will only receive the notification if it
+     * has requested to receive self-change notifications by implementing
+     * {@link ContentObserver#deliverSelfNotifications()} to return true.
+     * @param flags Additional flags: {@link #NOTIFY_SYNC_TO_NETWORK}.
+     * @see #requestSync(android.accounts.Account, String, android.os.Bundle)
+     */
+    public void notifyChange(@NonNull Uri uri, @Nullable ContentObserver observer,
+            @NotifyFlags int flags) {
+        Preconditions.checkNotNull(uri, "uri");
+        notifyChange(
+                ContentProvider.getUriWithoutUserId(uri),
+                observer,
+                flags,
+                ContentProvider.getUserIdFromUri(uri, UserHandle.myUserId()));
+    }
+
+    /**
      * Notify registered observers within the designated user(s) that a row was updated.
      *
      * @hide
@@ -1699,7 +1753,24 @@
         try {
             getContentService().notifyChange(
                     uri, observer == null ? null : observer.getContentObserver(),
-                    observer != null && observer.deliverSelfNotifications(), syncToNetwork,
+                    observer != null && observer.deliverSelfNotifications(),
+                    syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,
+                    userHandle);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Notify registered observers within the designated user(s) that a row was updated.
+     *
+     * @hide
+     */
+    public void notifyChange(Uri uri, ContentObserver observer, @NotifyFlags int flags,
+            @UserIdInt int userHandle) {
+        try {
+            getContentService().notifyChange(
+                    uri, observer == null ? null : observer.getContentObserver(),
+                    observer != null && observer.deliverSelfNotifications(), flags,
                     userHandle);
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/content/IContentService.aidl b/core/java/android/content/IContentService.aidl
index d47e780..3446e03 100644
--- a/core/java/android/content/IContentService.aidl
+++ b/core/java/android/content/IContentService.aidl
@@ -52,7 +52,7 @@
      *     USER_CURRENT are properly interpreted.
      */
     void notifyChange(in Uri uri, IContentObserver observer,
-            boolean observerWantsSelfNotifications, boolean syncToNetwork,
+            boolean observerWantsSelfNotifications, int flags,
             int userHandle);
 
     void requestSync(in Account account, String authority, in Bundle extras);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 30f2c94..207b70a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3730,6 +3730,31 @@
     public static final String EXTRA_ALTERNATE_INTENTS = "android.intent.extra.ALTERNATE_INTENTS";
 
     /**
+     * A {@link ComponentName ComponentName[]} describing components that should be filtered out
+     * and omitted from a list of components presented to the user.
+     *
+     * <p>When used with {@link #ACTION_CHOOSER}, the chooser will omit any of the components
+     * in this array if it otherwise would have shown them. Useful for omitting specific targets
+     * from your own package or other apps from your organization if the idea of sending to those
+     * targets would be redundant with other app functionality. Filtered components will not
+     * be able to present targets from an associated <code>ChooserTargetService</code>.</p>
+     */
+    public static final String EXTRA_EXCLUDE_COMPONENTS
+            = "android.intent.extra.EXCLUDE_COMPONENTS";
+
+    /**
+     * A {@link android.service.chooser.ChooserTarget ChooserTarget[]} for {@link #ACTION_CHOOSER}
+     * describing additional high-priority deep-link targets for the chooser to present to the user.
+     *
+     * <p>Targets provided in this way will be presented inline with all other targets provided
+     * by services from other apps. They will be prioritized before other service targets, but
+     * after those targets provided by sources that the user has manually pinned to the front.</p>
+     *
+     * @see #ACTION_CHOOSER
+     */
+    public static final String EXTRA_CHOOSER_TARGETS = "android.intent.extra.CHOOSER_TARGETS";
+
+    /**
      * An {@link IntentSender} for an Activity that will be invoked when the user makes a selection
      * from the chooser activity presented by {@link #ACTION_CHOOSER}.
      *
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index ed5dfa5..22ab43b 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -449,11 +449,12 @@
     }
 
     /**
-     * Modify priority of this filter.  The default priority is 0. Positive
-     * values will be before the default, lower values will be after it.
-     * Applications must use a value that is larger than
-     * {@link #SYSTEM_LOW_PRIORITY} and smaller than
-     * {@link #SYSTEM_HIGH_PRIORITY} .
+     * Modify priority of this filter.  This only affects receiver filters.
+     * The priority of activity filters are set in XML and cannot be changed
+     * programatically. The default priority is 0. Positive values will be
+     * before the default, lower values will be after it. Applications should
+     * use a value that is larger than {@link #SYSTEM_LOW_PRIORITY} and
+     * smaller than {@link #SYSTEM_HIGH_PRIORITY} .
      *
      * @param priority The new priority value.
      *
diff --git a/core/java/android/content/pm/FeatureInfo.java b/core/java/android/content/pm/FeatureInfo.java
index 7671f72..9ee6fa2 100644
--- a/core/java/android/content/pm/FeatureInfo.java
+++ b/core/java/android/content/pm/FeatureInfo.java
@@ -48,6 +48,9 @@
      * <p>
      * If this object represents a feature requested by an app, this is the
      * minimum version of the feature required by the app.
+     * <p>
+     * When a feature version is undefined by a device, it's assumed to be
+     * version 0.
      */
     public int version;
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ea251f6..aa1e372 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -385,6 +385,7 @@
         public final int installLocation;
         public final VerifierInfo[] verifiers;
         public final Signature[] signatures;
+        public final Certificate[][] certificates;
         public final boolean coreApp;
         public final boolean multiArch;
         public final boolean use32bitAbi;
@@ -392,8 +393,8 @@
 
         public ApkLite(String codePath, String packageName, String splitName, int versionCode,
                 int revisionCode, int installLocation, List<VerifierInfo> verifiers,
-                Signature[] signatures, boolean coreApp, boolean multiArch, boolean use32bitAbi,
-                boolean extractNativeLibs) {
+                Signature[] signatures, Certificate[][] certificates, boolean coreApp,
+                boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs) {
             this.codePath = codePath;
             this.packageName = packageName;
             this.splitName = splitName;
@@ -402,6 +403,7 @@
             this.installLocation = installLocation;
             this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]);
             this.signatures = signatures;
+            this.certificates = certificates;
             this.coreApp = coreApp;
             this.multiArch = multiArch;
             this.use32bitAbi = use32bitAbi;
@@ -1074,6 +1076,43 @@
     }
 
     /**
+     * Populates the correct packages fields with the given certificates.
+     * <p>
+     * This is useful when we've already processed the certificates [such as during package
+     * installation through an installer session]. We don't re-process the archive and
+     * simply populate the correct fields.
+     */
+    public static void populateCertificates(Package pkg, Certificate[][] certificates)
+            throws PackageParserException {
+        pkg.mCertificates = null;
+        pkg.mSignatures = null;
+        pkg.mSigningKeys = null;
+
+        pkg.mCertificates = certificates;
+        try {
+            pkg.mSignatures = convertToSignatures(certificates);
+        } catch (CertificateEncodingException e) {
+            // certificates weren't encoded properly; something went wrong
+            throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                    "Failed to collect certificates from " + pkg.baseCodePath, e);
+        }
+        pkg.mSigningKeys = new ArraySet<>(certificates.length);
+        for (int i = 0; i < certificates.length; i++) {
+            Certificate[] signerCerts = certificates[i];
+            Certificate signerCert = signerCerts[0];
+            pkg.mSigningKeys.add(signerCert.getPublicKey());
+        }
+        // add signatures to child packages
+        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
+        for (int i = 0; i < childCount; i++) {
+            Package childPkg = pkg.childPackages.get(i);
+            childPkg.mCertificates = pkg.mCertificates;
+            childPkg.mSignatures = pkg.mSignatures;
+            childPkg.mSigningKeys = pkg.mSigningKeys;
+        }
+    }
+
+    /**
      * Collect certificates from all the APKs described in the given package,
      * populating {@link Package#mSignatures}. Also asserts that all APK
      * contents are signed correctly and consistently.
@@ -1096,13 +1135,19 @@
         pkg.mSignatures = null;
         pkg.mSigningKeys = null;
 
-        collectCertificates(pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+        try {
+            collectCertificates(
+                    pkg, new File(pkg.baseCodePath), pkg.applicationInfo.flags, parseFlags);
 
-        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
-            for (int i = 0; i < pkg.splitCodePaths.length; i++) {
-                collectCertificates(pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i],
-                        parseFlags);
+            if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+                for (int i = 0; i < pkg.splitCodePaths.length; i++) {
+                    collectCertificates(
+                            pkg, new File(pkg.splitCodePaths[i]), pkg.splitFlags[i], parseFlags);
+                }
             }
+        } finally {
+            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
 
@@ -1118,6 +1163,7 @@
             Certificate[][] allSignersCerts = null;
             Signature[] signatures = null;
             try {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV2");
                 allSignersCerts = ApkSignatureSchemeV2Verifier.verify(apkPath);
                 signatures = convertToSignatures(allSignersCerts);
                 // APK verified using APK Signature Scheme v2.
@@ -1130,6 +1176,8 @@
                         "Failed to collect certificates from " + apkPath
                                 + " using APK Signature Scheme v2",
                         e);
+            } finally {
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
             }
 
             if (verified) {
@@ -1186,7 +1234,7 @@
             }
 
             // APK's integrity needs to be verified using JAR signature scheme.
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "buildVerifyList");
+            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV1");
             final List<ZipEntry> toVerify = new ArrayList<>();
             toVerify.add(manifestEntry);
 
@@ -1208,7 +1256,6 @@
                     toVerify.add(entry);
                 }
             }
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             if (!codeFound && requireCode) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
@@ -1218,7 +1265,6 @@
             // Verify that entries are signed consistently with the first entry
             // we encountered. Note that for splits, certificates may have
             // already been populated during an earlier parse of a base APK.
-            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyEntries");
             for (ZipEntry entry : toVerify) {
                 final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
                 if (ArrayUtils.isEmpty(entryCerts)) {
@@ -1297,17 +1343,25 @@
             parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
 
             final Signature[] signatures;
+            final Certificate[][] certificates;
             if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
                 // TODO: factor signature related items out of Package object
                 final Package tempPkg = new Package(null);
-                collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
+                try {
+                    collectCertificates(tempPkg, apkFile, 0 /*apkFlags*/, 0 /*flags*/);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+                }
                 signatures = tempPkg.mSignatures;
+                certificates = tempPkg.mCertificates;
             } else {
                 signatures = null;
+                certificates = null;
             }
 
             final AttributeSet attrs = parser;
-            return parseApkLite(apkPath, res, parser, attrs, flags, signatures);
+            return parseApkLite(apkPath, res, parser, attrs, flags, signatures, certificates);
 
         } catch (XmlPullParserException | IOException | RuntimeException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
@@ -1393,8 +1447,8 @@
     }
 
     private static ApkLite parseApkLite(String codePath, Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
-            XmlPullParserException, PackageParserException {
+            AttributeSet attrs, int flags, Signature[] signatures, Certificate[][] certificates)
+                    throws IOException, XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs);
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
@@ -1454,8 +1508,8 @@
         }
 
         return new ApkLite(codePath, packageSplit.first, packageSplit.second, versionCode,
-                revisionCode, installLocation, verifiers, signatures, coreApp, multiArch,
-                use32bitAbi, extractNativeLibs);
+                revisionCode, installLocation, verifiers, signatures, certificates, coreApp,
+                multiArch, use32bitAbi, extractNativeLibs);
     }
 
     /**
@@ -5531,7 +5585,7 @@
         return pi;
     }
 
-    public final static class Instrumentation extends Component {
+    public final static class Instrumentation extends Component<IntentInfo> {
         public final InstrumentationInfo info;
 
         public Instrumentation(final ParsePackageItemArgs args, final InstrumentationInfo _info) {
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index a762f59..34a9523 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -26,6 +26,7 @@
 import android.os.CancellationSignal;
 import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.os.Trace;
 import android.util.Log;
 import android.util.LruCache;
@@ -1311,7 +1312,8 @@
                         operation.mBindArgs.clear();
                     }
                 }
-                operation.mStartTime = System.currentTimeMillis();
+                operation.mStartWallTime = System.currentTimeMillis();
+                operation.mStartTime = SystemClock.uptimeMillis();
                 operation.mKind = kind;
                 operation.mSql = sql;
                 if (bindArgs != null) {
@@ -1376,7 +1378,7 @@
                     Trace.asyncTraceEnd(Trace.TRACE_TAG_DATABASE, operation.getTraceMethodName(),
                             operation.mCookie);
                 }
-                operation.mEndTime = System.currentTimeMillis();
+                operation.mEndTime = SystemClock.uptimeMillis();
                 operation.mFinished = true;
                 return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
                                 operation.mEndTime - operation.mStartTime);
@@ -1454,8 +1456,9 @@
         // marker for us, potentially losing metadata in the process).
         private static final int MAX_TRACE_METHOD_NAME_LEN = 256;
 
-        public long mStartTime;
-        public long mEndTime;
+        public long mStartWallTime; // in System.currentTimeMillis()
+        public long mStartTime; // in SystemClock.uptimeMillis();
+        public long mEndTime; // in SystemClock.uptimeMillis();
         public String mKind;
         public String mSql;
         public ArrayList<Object> mBindArgs;
@@ -1468,7 +1471,7 @@
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
             } else {
-                msg.append(" started ").append(System.currentTimeMillis() - mStartTime)
+                msg.append(" started ").append(System.currentTimeMillis() - mStartWallTime)
                         .append("ms ago");
             }
             msg.append(" - ").append(getStatus());
@@ -1519,7 +1522,7 @@
             //       relatively expensive to create during preloading. This method is only used
             //       when dumping a connection, which is a rare (mainly error) case. So:
             //       DO NOT CACHE.
-            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(mStartTime));
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(mStartWallTime));
         }
     }
 }
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 6aacc9c..c54d1e1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -438,7 +438,7 @@
      * @see #createCaptureSession
      * @see OutputConfiguration
      */
-    public abstract void createCaptureSessionByOutputConfiguration(
+    public abstract void createCaptureSessionByOutputConfigurations(
             List<OutputConfiguration> outputConfigurations,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException;
@@ -627,7 +627,7 @@
      * @see OutputConfiguration
      *
      */
-    public abstract void createReprocessableCaptureSessionWithConfigurations(
+    public abstract void createReprocessableCaptureSessionByConfigurations(
             @NonNull InputConfiguration inputConfig,
             @NonNull List<OutputConfiguration> outputs,
             @NonNull CameraCaptureSession.StateCallback callback,
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d2e820e..18a155d 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -493,7 +493,7 @@
     }
 
     @Override
-    public void createCaptureSessionByOutputConfiguration(
+    public void createCaptureSessionByOutputConfigurations(
             List<OutputConfiguration> outputConfigurations,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException {
@@ -532,7 +532,7 @@
     }
 
     @Override
-    public void createReprocessableCaptureSessionWithConfigurations(InputConfiguration inputConfig,
+    public void createReprocessableCaptureSessionByConfigurations(InputConfiguration inputConfig,
             List<OutputConfiguration> outputs,
             android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
                     throws CameraAccessException {
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index b8088f3..4756b372 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -815,7 +815,8 @@
                 if (groupId != reqGroupId) {
                     Log.w(TAG, "Group id didn't match: " + groupId + " != " + reqGroupId);
                 }
-                mRemovalCallback.onRemovalSucceeded(mRemovalFingerprint);
+                mRemovalCallback.onRemovalSucceeded(new Fingerprint(null, groupId, fingerId,
+                        deviceId));
             }
         }
 
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index fbac58c..47440de 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -16,9 +16,7 @@
 
 package android.hardware.input;
 
-import com.android.internal.inputmethod.InputMethodSubtypeHandle;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
 
 import android.annotation.IntDef;
 import android.annotation.SdkConstant;
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index 014e73f..10fc8e6 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -52,4 +52,11 @@
      */
     public abstract void onInputMethodSubtypeChanged(int userId,
             @Nullable InputMethodInfo inputMethodInfo, @Nullable InputMethodSubtype subtype);
+
+    /**
+     * Toggles Caps Lock state for input device with specific id.
+     *
+     * @param deviceId The id of input device.
+     */
+    public abstract void toggleCapsLock(int deviceId);
 }
diff --git a/core/java/android/hardware/location/ContextHubInfo.java b/core/java/android/hardware/location/ContextHubInfo.java
index ae44f1d..194b9ee 100644
--- a/core/java/android/hardware/location/ContextHubInfo.java
+++ b/core/java/android/hardware/location/ContextHubInfo.java
@@ -345,6 +345,24 @@
         mMemoryRegions = Arrays.copyOf(memoryRegions, memoryRegions.length);
     }
 
+    @Override
+    public String toString() {
+      String retVal = "";
+      retVal += "Id : " + mId;
+      retVal += ", Name : " + mName;
+      retVal += "\n\tVendor : " + mVendor;
+      retVal += ", ToolChain : " + mToolchain;
+      retVal += "\n\tPlatformVersion : " + mPlatformVersion;
+      retVal += ", StaticSwVersion : " + mStaticSwVersion;
+      retVal += "\n\tPeakMips : " + mPeakMips;
+      retVal += ", StoppedPowerDraw : " + mStoppedPowerDrawMw + " mW";
+      retVal += ", PeakPowerDraw : " + mPeakPowerDrawMw + " mW";
+      retVal += "\n\tSupported sensors : " + Arrays.toString(mSupportedSensors);
+      retVal += "\n\tMemory Regions : " + Arrays.toString(mMemoryRegions);
+
+      return retVal;
+    }
+
     private ContextHubInfo(Parcel in) {
         mId = in.readInt();
         mName = in.readString();
diff --git a/core/java/android/hardware/location/ContextHubService.java b/core/java/android/hardware/location/ContextHubService.java
index 3e6cb63..2b9b974 100644
--- a/core/java/android/hardware/location/ContextHubService.java
+++ b/core/java/android/hardware/location/ContextHubService.java
@@ -18,9 +18,12 @@
 
 import android.Manifest;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.HashMap;
 
@@ -28,13 +31,12 @@
  * @hide
  */
 public class ContextHubService extends IContextHubService.Stub {
-
     public static final String CONTEXTHUB_SERVICE = "contexthub_service";
 
     private static final String TAG = "ContextHubService";
     private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
     private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
-            + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
+        + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware";
 
 
     public static final int ANY_HUB             = -1;
@@ -78,8 +80,8 @@
     @Override
     public int registerCallback(IContextHubCallback callback) throws RemoteException {
         checkPermissions();
-        synchronized(this) {
-          mCallback = callback;
+        synchronized (this) {
+            mCallback = callback;
         }
         return 0;
     }
@@ -87,10 +89,10 @@
     @Override
     public int[] getContextHubHandles() throws RemoteException {
         checkPermissions();
-        int [] returnArray = new int[mContextHubInfo.length];
+        int[] returnArray = new int[mContextHubInfo.length];
 
         for (int i = 0; i < returnArray.length; ++i) {
-            returnArray[i] = i + 1; //valid handles from 1...n
+            returnArray[i] = i;
             Log.d(TAG, String.format("Hub %s is mapped to %d",
                                      mContextHubInfo[i].getName(), returnArray[i]));
         }
@@ -101,7 +103,6 @@
     @Override
     public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
         checkPermissions();
-        contextHubHandle -= 1;
         if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
             return null; // null means fail
         }
@@ -112,13 +113,12 @@
     @Override
     public int loadNanoApp(int contextHubHandle, NanoApp app) throws RemoteException {
         checkPermissions();
-        contextHubHandle -= 1;
 
         if (!(contextHubHandle >= 0 && contextHubHandle < mContextHubInfo.length)) {
-            return -1; // negative handle are invalid, means failed
+            Log.e(TAG, "Invalid contextHubhandle " + contextHubHandle);
+            return -1;
         }
 
-        // Call Native interface here
         int[] msgHeader = new int[MSG_HEADER_SIZE];
         msgHeader[MSG_FIELD_HUB_HANDLE] = contextHubHandle;
         msgHeader[MSG_FIELD_APP_INSTANCE] = OS_APP_INSTANCE;
@@ -147,7 +147,7 @@
         msgHeader[MSG_FIELD_VERSION] = 0;
         msgHeader[MSG_FIELD_TYPE] = MSG_UNLOAD_NANO_APP;
 
-        if(nativeSendMessage(msgHeader, null) != 0) {
+        if (nativeSendMessage(msgHeader, null) != 0) {
             return -1;
         }
 
@@ -157,7 +157,7 @@
 
     @Override
     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle)
-            throws RemoteException {
+                                                      throws RemoteException {
         checkPermissions();
         // This assumes that all the nanoAppInfo is current. This is reasonable
         // for the use cases for tightly controlled nanoApps.
@@ -173,10 +173,10 @@
         checkPermissions();
         ArrayList<Integer> foundInstances = new ArrayList<Integer>();
 
-        for(Integer nanoAppInstance : mNanoAppHash.keySet()) {
+        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
             NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance);
 
-            if (filter.testMatch(info)){
+            if (filter.testMatch(info)) {
                 foundInstances.add(nanoAppInstance);
             }
         }
@@ -191,7 +191,7 @@
 
     @Override
     public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage msg)
-            throws RemoteException {
+                           throws RemoteException {
         checkPermissions();
 
         int[] msgHeader = new int[MSG_HEADER_SIZE];
@@ -203,6 +203,32 @@
         return nativeSendMessage(msgHeader, msg.getData());
     }
 
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (mContext.checkCallingOrSelfPermission("android.permission.DUMP")
+            != PackageManager.PERMISSION_GRANTED) {
+            pw.println("Permission Denial: can't dump contexthub_service");
+            return;
+        }
+
+        pw.println("Dumping ContextHub Service");
+
+        pw.println("");
+        // dump ContextHubInfo
+        pw.println("=================== CONTEXT HUBS ====================");
+        for (int i = 0; i < mContextHubInfo.length; i++) {
+            pw.println("Handle " + i + " : " + mContextHubInfo[i].toString());
+        }
+        pw.println("");
+        pw.println("=================== NANOAPPS ====================");
+        // Dump nanoAppHash
+        for (Integer nanoAppInstance: mNanoAppHash.keySet()) {
+            pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString());
+        }
+
+        // dump eventLog
+    }
+
     private void checkPermissions() {
         mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
     }
@@ -212,16 +238,16 @@
             return  -1;
         }
 
-        synchronized(this) {
+        synchronized (this) {
             if (mCallback != null) {
                 ContextHubMessage msg = new ContextHubMessage(header[MSG_FIELD_TYPE],
-                        header[MSG_FIELD_VERSION],
-                        data);
+                                                              header[MSG_FIELD_VERSION],
+                                                              data);
 
                 try {
                     mCallback.onMessageReceipt(header[MSG_FIELD_HUB_HANDLE],
-                            header[MSG_FIELD_APP_INSTANCE],
-                            msg);
+                                               header[MSG_FIELD_APP_INSTANCE],
+                                               msg);
                 } catch (Exception e) {
                     Log.w(TAG, "Exception " + e + " when calling remote callback");
                     return -1;
diff --git a/core/java/android/hardware/location/MemoryRegion.java b/core/java/android/hardware/location/MemoryRegion.java
index d100de2..857434e 100644
--- a/core/java/android/hardware/location/MemoryRegion.java
+++ b/core/java/android/hardware/location/MemoryRegion.java
@@ -79,6 +79,33 @@
     }
 
     @Override
+    public String toString() {
+        String mask = "";
+
+        if (isReadable()) {
+            mask += "r";
+        } else {
+            mask += "-";
+        }
+
+        if (isWritable()) {
+            mask += "w";
+        } else {
+            mask += "-";
+        }
+
+        if (isExecutable()) {
+            mask += "x";
+        } else {
+            mask += "-";
+        }
+
+        String retVal = "[ " + mSizeBytesFree + "/ " + mSizeBytes + " ] : " + mask;
+
+        return retVal;
+    }
+
+    @Override
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/android/hardware/location/NanoApp.java b/core/java/android/hardware/location/NanoApp.java
index b447b62..c8f3439 100644
--- a/core/java/android/hardware/location/NanoApp.java
+++ b/core/java/android/hardware/location/NanoApp.java
@@ -284,4 +284,14 @@
             return new NanoApp[size];
         }
     };
+
+    @Override
+    public String toString() {
+        String retVal = "Id : " + mAppId;
+        retVal += ", Version : " + mAppVersion;
+        retVal += ", Name : " + mName;
+        retVal += ", Publisher : " + mPublisher;
+
+        return retVal;
+    }
 }
diff --git a/core/java/android/hardware/location/NanoAppInstanceInfo.java b/core/java/android/hardware/location/NanoAppInstanceInfo.java
index 977f645..e842ec6 100644
--- a/core/java/android/hardware/location/NanoAppInstanceInfo.java
+++ b/core/java/android/hardware/location/NanoAppInstanceInfo.java
@@ -320,4 +320,15 @@
             return new NanoAppInstanceInfo[size];
         }
     };
+
+    @Override
+    public String toString() {
+        String retVal = "handle : " + mHandle;
+        retVal += ", Id : 0x" + Long.toHexString(mAppId);
+        retVal += ", Version : " + mAppVersion;
+        retVal += ", Name : " + mName;
+        retVal += ", Publisher : " + mPublisher;
+
+        return retVal;
+    }
 }
diff --git a/core/java/android/inputmethodservice/CompactExtractEditLayout.java b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
new file mode 100644
index 0000000..f994c65
--- /dev/null
+++ b/core/java/android/inputmethodservice/CompactExtractEditLayout.java
@@ -0,0 +1,103 @@
+package android.inputmethodservice;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.annotation.FractionRes;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+/**
+ * A special purpose layout for the editor extract view for tiny (sub 250dp) screens.
+ * The layout is based on sizes proportional to screen pixel size to provide for the
+ * best layout fidelity on varying pixel sizes and densities.
+ *
+ * @hide
+ */
+public class CompactExtractEditLayout extends LinearLayout {
+    private View mInputExtractEditText;
+    private View mInputExtractAccessories;
+    private View mInputExtractAction;
+    private boolean mPerformLayoutChanges;
+
+    public CompactExtractEditLayout(Context context) {
+        super(context);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public CompactExtractEditLayout(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mInputExtractEditText = findViewById(com.android.internal.R.id.inputExtractEditText);
+        mInputExtractAccessories = findViewById(com.android.internal.R.id.inputExtractAccessories);
+        mInputExtractAction = findViewById(com.android.internal.R.id.inputExtractAction);
+
+        if (mInputExtractEditText != null && mInputExtractAccessories != null
+                && mInputExtractAction != null) {
+            mPerformLayoutChanges = true;
+        }
+    }
+
+    private int applyFractionInt(@FractionRes int fraction, int whole) {
+        return Math.round(getResources().getFraction(fraction, whole, whole));
+    }
+
+    private static void setLayoutHeight(View v, int px) {
+        ViewGroup.LayoutParams lp = v.getLayoutParams();
+        lp.height = px;
+        v.setLayoutParams(lp);
+    }
+
+    private static void setLayoutMarginBottom(View v, int px) {
+        ViewGroup.MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
+        lp.bottomMargin = px;
+        v.setLayoutParams(lp);
+    }
+
+    private void applyProportionalLayout(int screenWidthPx, int screenHeightPx) {
+        if (getResources().getConfiguration().isScreenRound()) {
+            setGravity(Gravity.BOTTOM);
+        }
+        setLayoutHeight(this, applyFractionInt(
+                com.android.internal.R.fraction.input_extract_layout_height, screenHeightPx));
+
+        setPadding(
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_left,
+                        screenWidthPx),
+                0,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_layout_padding_right,
+                        screenWidthPx),
+                0);
+
+        setLayoutMarginBottom(mInputExtractEditText,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_text_margin_bottom,
+                        screenHeightPx));
+
+        setLayoutMarginBottom(mInputExtractAccessories,
+                applyFractionInt(com.android.internal.R.fraction.input_extract_action_margin_bottom,
+                        screenHeightPx));
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mPerformLayoutChanges) {
+            Resources res = getResources();
+            DisplayMetrics dm = res.getDisplayMetrics();
+            int heightPixels = dm.heightPixels;
+            int widthPixels = dm.widthPixels;
+            applyProportionalLayout(widthPixels, heightPixels);
+        }
+    }
+}
+
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 9d53a00..085b97c 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -68,9 +68,10 @@
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Button;
 import android.widget.FrameLayout;
+import android.widget.ImageButton;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -302,7 +303,7 @@
     boolean mExtractViewHidden;
     ExtractEditText mExtractEditText;
     ViewGroup mExtractAccessories;
-    Button mExtractAction;
+    View mExtractAction;
     ExtractedText mExtractedText;
     int mExtractedToken;
     
@@ -717,7 +718,11 @@
                 mShowImeWithHardKeyboard = Settings.Secure.getInt(mService.getContentResolver(),
                         Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0 ?
                         ShowImeWithHardKeyboardType.TRUE : ShowImeWithHardKeyboardType.FALSE;
-                mService.updateInputViewShown();
+                // In Android M and prior, state change of
+                // Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD has triggered
+                // #onConfigurationChanged().  For compatibility reasons, we reset the internal
+                // state as if configuration was changed.
+                mService.resetStateForNewConfiguration();
             }
         }
 
@@ -884,7 +889,10 @@
      */
     @Override public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
+        resetStateForNewConfiguration();
+    }
 
+    private void resetStateForNewConfiguration() {
         boolean visible = mWindowVisible;
         int showFlags = mShowInputFlags;
         boolean showingInput = mShowInputRequested;
@@ -1337,7 +1345,7 @@
             mExtractEditText = (ExtractEditText)view.findViewById(
                     com.android.internal.R.id.inputExtractEditText);
             mExtractEditText.setIME(this);
-            mExtractAction = (Button)view.findViewById(
+            mExtractAction = view.findViewById(
                     com.android.internal.R.id.inputExtractAction);
             if (mExtractAction != null) {
                 mExtractAccessories = (ViewGroup)view.findViewById(
@@ -2401,7 +2409,35 @@
                 return getText(com.android.internal.R.string.ime_action_default);
         }
     }
-    
+
+    /**
+     * Return a drawable resource id that can be used as a button icon for the given
+     * {@link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @param imeOptions The value from @link EditorInfo#imeOptions EditorInfo.imeOptions}.
+     *
+     * @return Returns a drawable resource id to use.
+     */
+    @DrawableRes
+    private int getIconForImeAction(int imeOptions) {
+        switch (imeOptions&EditorInfo.IME_MASK_ACTION) {
+            case EditorInfo.IME_ACTION_GO:
+                return com.android.internal.R.drawable.ic_input_extract_action_go;
+            case EditorInfo.IME_ACTION_SEARCH:
+                return com.android.internal.R.drawable.ic_input_extract_action_search;
+            case EditorInfo.IME_ACTION_SEND:
+                return com.android.internal.R.drawable.ic_input_extract_action_send;
+            case EditorInfo.IME_ACTION_NEXT:
+                return com.android.internal.R.drawable.ic_input_extract_action_next;
+            case EditorInfo.IME_ACTION_DONE:
+                return com.android.internal.R.drawable.ic_input_extract_action_done;
+            case EditorInfo.IME_ACTION_PREVIOUS:
+                return com.android.internal.R.drawable.ic_input_extract_action_previous;
+            default:
+                return com.android.internal.R.drawable.ic_input_extract_action_return;
+        }
+    }
+
     /**
      * Called when the fullscreen-mode extracting editor info has changed,
      * to determine whether the extracting (extract text and candidates) portion
@@ -2452,10 +2488,20 @@
         if (hasAction) {
             mExtractAccessories.setVisibility(View.VISIBLE);
             if (mExtractAction != null) {
-                if (ei.actionLabel != null) {
-                    mExtractAction.setText(ei.actionLabel);
+                if (mExtractAction instanceof ImageButton) {
+                    ((ImageButton) mExtractAction)
+                            .setImageResource(getIconForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        mExtractAction.setContentDescription(ei.actionLabel);
+                    } else {
+                        mExtractAction.setContentDescription(getTextForImeAction(ei.imeOptions));
+                    }
                 } else {
-                    mExtractAction.setText(getTextForImeAction(ei.imeOptions));
+                    if (ei.actionLabel != null) {
+                        ((TextView) mExtractAction).setText(ei.actionLabel);
+                    } else {
+                        ((TextView) mExtractAction).setText(getTextForImeAction(ei.imeOptions));
+                    }
                 }
                 mExtractAction.setOnClickListener(mActionClickListener);
             }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f0673ff..a025337 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -767,6 +767,28 @@
     }
 
     /**
+     * Returns a {@link Network} object corresponding to the currently active
+     * default data network for a specific UID.  In the event that the default data
+     * network disconnects, the returned {@code Network} object will no longer
+     * be usable.  This will return {@code null} when there is no default
+     * network for the UID.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
+     *
+     * @return a {@link Network} object for the current default network for the
+     *         given UID or {@code null} if no default network is currently active
+     *
+     * @hide
+     */
+    public Network getActiveNetworkForUid(int uid) {
+        try {
+            return mService.getActiveNetworkForUid(uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Configures an always-on VPN connection through a specific application.
      * This connection is automatically granted and persisted after a reboot.
      *
@@ -3021,7 +3043,6 @@
      *
      * @param networkCallback The {@link NetworkCallback} that the system will call as the
      *                        system default network changes.
-     * @hide
      */
     public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
         // This works because if the NetworkCapabilities are null,
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1a9c9ea..c897c45 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -44,6 +44,7 @@
 interface IConnectivityManager
 {
     Network getActiveNetwork();
+    Network getActiveNetworkForUid(int uid);
     NetworkInfo getActiveNetworkInfo();
     NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index a9de23e..9cd563e 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -55,19 +55,22 @@
     final String mSubscriberId;
     final String mNetworkId;
     final boolean mRoaming;
+    final boolean mMetered;
 
     public NetworkIdentity(
-            int type, int subType, String subscriberId, String networkId, boolean roaming) {
+            int type, int subType, String subscriberId, String networkId, boolean roaming,
+            boolean metered) {
         mType = type;
         mSubType = COMBINE_SUBTYPE_ENABLED ? SUBTYPE_COMBINED : subType;
         mSubscriberId = subscriberId;
         mNetworkId = networkId;
         mRoaming = roaming;
+        mMetered = metered;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming);
+        return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered);
     }
 
     @Override
@@ -76,7 +79,8 @@
             final NetworkIdentity ident = (NetworkIdentity) obj;
             return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming
                     && Objects.equals(mSubscriberId, ident.mSubscriberId)
-                    && Objects.equals(mNetworkId, ident.mNetworkId);
+                    && Objects.equals(mNetworkId, ident.mNetworkId)
+                    && mMetered == ident.mMetered;
         }
         return false;
     }
@@ -102,6 +106,7 @@
         if (mRoaming) {
             builder.append(", ROAMING");
         }
+        builder.append(", metered=").append(mMetered);
         return builder.append("}").toString();
     }
 
@@ -125,6 +130,10 @@
         return mRoaming;
     }
 
+    public boolean getMetered() {
+        return mMetered;
+    }
+
     /**
      * Scrub given IMSI on production builds.
      */
@@ -162,6 +171,7 @@
         String subscriberId = null;
         String networkId = null;
         boolean roaming = false;
+        boolean metered = false;
 
         if (isNetworkTypeMobile(type)) {
             if (state.subscriberId == null) {
@@ -171,6 +181,9 @@
             subscriberId = state.subscriberId;
             roaming = state.networkInfo.isRoaming();
 
+            metered = !state.networkCapabilities.hasCapability(
+                    NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+
         } else if (type == TYPE_WIFI) {
             if (state.networkId != null) {
                 networkId = state.networkId;
@@ -182,7 +195,7 @@
             }
         }
 
-        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming);
+        return new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered);
     }
 
     @Override
@@ -200,6 +213,9 @@
         if (res == 0) {
             res = Boolean.compare(mRoaming, another.mRoaming);
         }
+        if (res == 0) {
+            res = Boolean.compare(mMetered, another.mMetered);
+        }
         return res;
     }
 }
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index b32b2cc..caf7982 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -19,6 +19,7 @@
 import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_PROXY;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
 import static android.net.ConnectivityManager.TYPE_WIMAX;
@@ -30,9 +31,6 @@
 import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
 import static android.telephony.TelephonyManager.getNetworkClass;
 
-import static com.android.internal.util.ArrayUtils.contains;
-
-import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
@@ -71,16 +69,6 @@
     public static final int MATCH_BLUETOOTH = 8;
     public static final int MATCH_PROXY = 9;
 
-    /**
-     * Set of {@link NetworkInfo#getType()} that reflect data usage.
-     */
-    private static final int[] DATA_USAGE_NETWORK_TYPES;
-
-    static {
-        DATA_USAGE_NETWORK_TYPES = Resources.getSystem().getIntArray(
-                com.android.internal.R.array.config_data_usage_network_types);
-    }
-
     private static boolean sForceAllNetworkTypes = false;
 
     @VisibleForTesting
@@ -318,9 +306,8 @@
             // TODO: consider matching against WiMAX subscriber identity
             return true;
         } else {
-            final boolean matchesType = (sForceAllNetworkTypes
-                    || contains(DATA_USAGE_NETWORK_TYPES, ident.mType));
-            return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds)
+            return (sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered))
+                    && !ArrayUtils.isEmpty(mMatchSubscriberIds)
                     && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId);
         }
     }
@@ -389,7 +376,7 @@
         if (ident.mType == TYPE_WIMAX) {
             return true;
         } else {
-            return sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType);
+            return sForceAllNetworkTypes || (ident.mType == TYPE_MOBILE && ident.mMetered);
         }
     }
 
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index e40ebf7..d39968a 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -115,6 +115,13 @@
      */
     public static final String EXTRA_MAX_CHARGING_VOLTAGE = "max_charging_voltage";
 
+    /**
+     * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+     * integer containing the charge counter present in the battery.
+     * {@hide}
+     */
+     public static final String EXTRA_CHARGE_COUNTER = "charge_counter";
+
     // values for "status" field in the ACTION_BATTERY_CHANGED Intent
     public static final int BATTERY_STATUS_UNKNOWN = 1;
     public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index c3e0f24..b509d76 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -30,6 +30,7 @@
     public int batteryLevel;
     public int batteryVoltage;
     public int batteryTemperature;
+    public int batteryChargeCounter;
     public String batteryTechnology;
 
     public BatteryProperties() {
@@ -47,6 +48,7 @@
         batteryLevel = other.batteryLevel;
         batteryVoltage = other.batteryVoltage;
         batteryTemperature = other.batteryTemperature;
+        batteryChargeCounter = other.batteryChargeCounter;
         batteryTechnology = other.batteryTechnology;
     }
 
@@ -67,6 +69,7 @@
         batteryLevel = p.readInt();
         batteryVoltage = p.readInt();
         batteryTemperature = p.readInt();
+        batteryChargeCounter = p.readInt();
         batteryTechnology = p.readString();
     }
 
@@ -82,6 +85,7 @@
         p.writeInt(batteryLevel);
         p.writeInt(batteryVoltage);
         p.writeInt(batteryTemperature);
+        p.writeInt(batteryChargeCounter);
         p.writeString(batteryTechnology);
     }
 
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 878b7a0..3c7c962 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -231,6 +231,18 @@
         mAsynchronous = async;
     }
 
+    /** {@hide} */
+    public String getTraceName(Message message) {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(getClass().getName()).append(": ");
+        if (message.callback != null) {
+            sb.append(message.callback.getClass().getName());
+        } else {
+            sb.append("#").append(message.what);
+        }
+        return sb.toString();
+    }
+
     /**
      * Returns a string representing the name of the specified message.
      * The default implementation will either return the class name of the
@@ -739,8 +751,8 @@
         message.callback.run();
     }
 
-    final MessageQueue mQueue;
     final Looper mLooper;
+    final MessageQueue mQueue;
     final Callback mCallback;
     final boolean mAsynchronous;
     IMessenger mMessenger;
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 34c880f..b58ff1f 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -72,6 +72,7 @@
     final Thread mThread;
 
     private Printer mLogging;
+    private long mTraceTag;
 
      /** Initialize the current thread as a looper.
       * This gives you a chance to create handlers that then reference
@@ -139,13 +140,23 @@
             }
 
             // This must be in a local variable, in case a UI event sets the logger
-            Printer logging = me.mLogging;
+            final Printer logging = me.mLogging;
             if (logging != null) {
                 logging.println(">>>>> Dispatching to " + msg.target + " " +
                         msg.callback + ": " + msg.what);
             }
 
-            msg.target.dispatchMessage(msg);
+            final long traceTag = me.mTraceTag;
+            if (traceTag != 0) {
+                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
+            }
+            try {
+                msg.target.dispatchMessage(msg);
+            } finally {
+                if (traceTag != 0) {
+                    Trace.traceEnd(traceTag);
+                }
+            }
 
             if (logging != null) {
                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
@@ -208,6 +219,11 @@
         mLogging = printer;
     }
 
+    /** {@hide} */
+    public void setTraceTag(long traceTag) {
+        mTraceTag = traceTag;
+    }
+
     /**
      * Quits the looper.
      * <p>
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 720d3f7..ece1228 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -83,6 +83,8 @@
     public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
     /** {@hide} */
     public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe";
+    /** {@hide} */
+    public static final String PROP_SDCARDFS = "persist.sys.sdcardfs";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
@@ -93,6 +95,10 @@
     public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
     /** {@hide} */
     public static final int DEBUG_EMULATE_FBE = 1 << 1;
+    /** {@hide} */
+    public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2;
+    /** {@hide} */
+    public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3;
 
     // NOTE: keep in sync with installd
     /** {@hide} */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f4d63ac..2a3c3fe 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8306,6 +8306,16 @@
         public static final String BOOT_COUNT = "boot_count";
 
         /**
+         * Whether the safe boot is disallowed.
+         *
+         * <p>This setting should have the identical value as the corresponding user restriction.
+         * The purpose of the setting is to make the restriction available in early boot stages
+         * before the user restrictions are loaded.
+         * @hide
+         */
+        public static final String SAFE_BOOT_DISALLOWED = "safe_boot_disallowed";
+
+        /**
          * 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/Adjustment.aidl b/core/java/android/service/notification/Adjustment.aidl
new file mode 100644
index 0000000..8bd814a
--- /dev/null
+++ b/core/java/android/service/notification/Adjustment.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+parcelable Adjustment;
\ No newline at end of file
diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java
new file mode 100644
index 0000000..2e4f48d
--- /dev/null
+++ b/core/java/android/service/notification/Adjustment.java
@@ -0,0 +1,150 @@
+/*
+ * 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.notification;
+
+import android.annotation.SystemApi;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Ranking updates from the Ranker.
+ *
+ * @hide
+ */
+@SystemApi
+public final class Adjustment implements Parcelable {
+    private final String mPackage;
+    private final String mKey;
+    private final int mImportance;
+    private final CharSequence mExplanation;
+    private final Uri mReference;
+    private final Bundle mSignals;
+
+    public static final String GROUP_KEY_OVERRIDE_KEY = "group_key_override";
+    public static final String NEEDS_AUTOGROUPING_KEY = "autogroup_needed";
+
+    /**
+     * Create a notification adjustment.
+     *
+     * @param pkg The package of the notification.
+     * @param key The notification key.
+     * @param importance The recommended importance of the notification.
+     * @param signals A bundle of signals that should inform notification grouping and ordering.
+     * @param explanation A human-readable justification for the adjustment.
+     * @param reference A reference to an external object that augments the
+     *                  explanation, such as a
+     *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+     *                  or null.
+     */
+    public Adjustment(String pkg, String key, int importance, Bundle signals,
+            CharSequence explanation, Uri reference) {
+        mPackage = pkg;
+        mKey = key;
+        mImportance = importance;
+        mSignals = signals;
+        mExplanation = explanation;
+        mReference = reference;
+    }
+
+    protected Adjustment(Parcel in) {
+        if (in.readInt() == 1) {
+            mPackage = in.readString();
+        } else {
+            mPackage = null;
+        }
+        if (in.readInt() == 1) {
+            mKey = in.readString();
+        } else {
+            mKey = null;
+        }
+        mImportance = in.readInt();
+        if (in.readInt() == 1) {
+            mExplanation = in.readCharSequence();
+        } else {
+            mExplanation = null;
+        }
+        mReference = in.readParcelable(Uri.class.getClassLoader());
+        mSignals = in.readBundle();
+    }
+
+    public static final Creator<Adjustment> CREATOR = new Creator<Adjustment>() {
+        @Override
+        public Adjustment createFromParcel(Parcel in) {
+            return new Adjustment(in);
+        }
+
+        @Override
+        public Adjustment[] newArray(int size) {
+            return new Adjustment[size];
+        }
+    };
+
+    public String getPackage() {
+        return mPackage;
+    }
+
+    public String getKey() {
+        return mKey;
+    }
+
+    public int getImportance() {
+        return mImportance;
+    }
+
+    public CharSequence getExplanation() {
+        return mExplanation;
+    }
+
+    public Uri getReference() {
+        return mReference;
+    }
+
+    public Bundle getSignals() {
+        return mSignals;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (mPackage != null) {
+            dest.writeInt(1);
+            dest.writeString(mPackage);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mKey != null) {
+            dest.writeInt(1);
+            dest.writeString(mKey);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeInt(mImportance);
+        if (mExplanation != null) {
+            dest.writeInt(1);
+            dest.writeCharSequence(mExplanation);
+        } else {
+            dest.writeInt(0);
+        }
+        dest.writeParcelable(mReference, flags);
+        dest.writeBundle(mSignals);
+    }
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index afdd1d4..e708b0a 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -69,6 +69,12 @@
  *         &lt;action android:name="android.service.notification.NotificationListenerService" />
  *     &lt;/intent-filter>
  * &lt;/service></pre>
+ *
+ * <p>The service should wait for the {@link #onListenerConnected()} event
+ * before performing any operations. The {@link #requestRebind(ComponentName)}
+ * method is the <i>only</i> one that is safe to call before {@link #onListenerConnected()}
+ * or after {@link #onListenerDisconnected()}.
+ * </p>
  */
 public abstract class NotificationListenerService extends Service {
     // TAG = "NotificationListenerService[MySubclass]"
@@ -117,6 +123,16 @@
      * This does not change the interruption filter, only the effects. **/
     public static final int HINT_HOST_DISABLE_EFFECTS = 1;
 
+    /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
+     * should disable notification sound, but not phone calls.
+     * This does not change the interruption filter, only the effects. **/
+    public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 1 << 1;
+
+    /** {@link #getCurrentListenerHints() Listener hints} constant - the primary device UI
+     * should disable phone call sounds, buyt not notification sound.
+     * This does not change the interruption filter, only the effects. **/
+    public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 1 << 2;
+
     /**
      * Whether notification suppressed by DND should not interruption visually when the screen is
      * off.
@@ -164,6 +180,7 @@
 
     /** @hide */
     protected NotificationListenerWrapper mWrapper = null;
+    private boolean isConnected = false;
 
     @GuardedBy("mLock")
     private RankingMap mRankingMap;
@@ -222,10 +239,10 @@
 
     /**
      * Implement this method to learn when notifications are removed.
-     * <P>
+     * <p>
      * This might occur because the user has dismissed the notification using system UI (or another
      * notification listener) or because the app has withdrawn the notification.
-     * <P>
+     * <p>
      * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
      * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
      * fields such as {@link android.app.Notification#contentView} and
@@ -243,10 +260,10 @@
 
     /**
      * Implement this method to learn when notifications are removed.
-     * <P>
+     * <p>
      * This might occur because the user has dismissed the notification using system UI (or another
      * notification listener) or because the app has withdrawn the notification.
-     * <P>
+     * <p>
      * NOTE: The {@link StatusBarNotification} object you receive will be "light"; that is, the
      * result from {@link StatusBarNotification#getNotification} may be missing some heavyweight
      * fields such as {@link android.app.Notification#contentView} and
@@ -275,6 +292,15 @@
     }
 
     /**
+     * Implement this method to learn about when the listener is disconnected from the
+     * notification manager.You will not receive any events after this call, and may only
+     * call {@link #requestRebind(ComponentName)} at this time.
+     */
+    public void onListenerDisconnected() {
+        // optional
+    }
+
+    /**
      * Implement this method to be notified when the notification ranking changes.
      *
      * @param rankingMap The current ranking map that can be used to retrieve ranking information
@@ -322,12 +348,15 @@
      * It should be called after the user dismisses a single notification using your UI;
      * upon being informed, the notification manager will actually remove the notification
      * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
-     * <P>
+     * <p>
      * <b>Note:</b> If your listener allows the user to fire a notification's
      * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
      * this method at that time <i>if</i> the Notification in question has the
      * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param pkg Package of the notifying app.
      * @param tag Tag of the notification as specified by the notifying app in
      *     {@link android.app.NotificationManager#notify(String, int, android.app.Notification)}.
@@ -357,12 +386,16 @@
      * It should be called after the user dismisses a single notification using your UI;
      * upon being informed, the notification manager will actually remove the notification
      * and you will get an {@link #onNotificationRemoved(StatusBarNotification)} callback.
-     * <P>
+     * <p>
      * <b>Note:</b> If your listener allows the user to fire a notification's
      * {@link android.app.Notification#contentIntent} by tapping/clicking/etc., you should call
      * this method at that time <i>if</i> the Notification in question has the
      * {@link android.app.Notification#FLAG_AUTO_CANCEL} flag set.
      * <p>
+     *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param key Notification to dismiss from {@link StatusBarNotification#getKey()}.
      */
     public final void cancelNotification(String key) {
@@ -384,6 +417,9 @@
      * upon being informed, the notification manager will actually remove all active notifications
      * and you will get multiple {@link #onNotificationRemoved(StatusBarNotification)} callbacks.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * {@see #cancelNotification(String, String, int)}
      */
     public final void cancelAllNotifications() {
@@ -396,6 +432,9 @@
      * Use this if your listener has a user interface that allows the user to dismiss
      * multiple notifications at once.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param keys Notifications to dismiss, or {@code null} to dismiss all.
      *
      * {@see #cancelNotification(String, String, int)}
@@ -414,6 +453,10 @@
      * user. This should only be called when there is sufficient confidence that the user is
      * looking at the notifications, such as when the notifications appear on the screen due to
      * an explicit user interaction.
+     *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param keys Notifications to mark as seen.
      */
     public final void setNotificationsShown(String[] keys) {
@@ -436,6 +479,9 @@
      * <p>
      * Set to {@link #TRIM_FULL} initially.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @hide
      *
      * @param trim trim of the notifications to be passed via {@link #onNotificationPosted}.
@@ -455,6 +501,9 @@
      * Request the list of outstanding notifications (that is, those that are visible to the
      * current user). Useful when you don't know what's already been posted.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @return An array of active notifications, sorted in natural order.
      */
     public StatusBarNotification[] getActiveNotifications() {
@@ -480,6 +529,9 @@
      * notifications but didn't want to retain the bits, and now need to go back and extract
      * more data out of those notifications.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param keys the keys of the notifications to request
      * @return An array of notifications corresponding to the requested keys, in the
      * same order as the key list.
@@ -545,6 +597,9 @@
      * shared across all listeners or a feature the notification host does not support or refuses
      * to grant.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @return Zero or more of the HINT_ constants.
      */
     public final int getCurrentListenerHints() {
@@ -572,6 +627,9 @@
      * <p>
      * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @return One of the INTERRUPTION_FILTER_ constants, or INTERRUPTION_FILTER_UNKNOWN when
      * unavailable.
      */
@@ -594,6 +652,9 @@
      * <p>
      * Listen for updates using {@link #onListenerHintsChanged(int)}.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param hints One or more of the HINT_ constants.
      */
     public final void requestListenerHints(int hints) {
@@ -614,6 +675,9 @@
      * <p>
      * Listen for updates using {@link #onInterruptionFilterChanged(int)}.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @param interruptionFilter One of the INTERRUPTION_FILTER_ constants.
      */
     public final void requestInterruptionFilter(int interruptionFilter) {
@@ -640,6 +704,9 @@
      * such events, for example to retrieve the RankingMap right after
      * initialization.
      *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation.
+     *
      * @return A {@link RankingMap} object providing access to ranking information
      */
     public RankingMap getCurrentRanking() {
@@ -648,6 +715,12 @@
         }
     }
 
+    /**
+     * This is not the lifecycle event you are looking for.
+     *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing any operations.
+     */
     @Override
     public IBinder onBind(Intent intent) {
         if (mWrapper == null) {
@@ -665,6 +738,12 @@
         return true;
     }
 
+    @Override
+    public void onDestroy() {
+        onListenerDisconnected();
+        super.onDestroy();
+    }
+
     /**
      * Directly register this service with the Notification Manager.
      *
@@ -693,7 +772,7 @@
     /**
      * Directly unregister this service from the Notification Manager.
      *
-     * <P>This method will fail for listeners that were not registered
+     * <p>This method will fail for listeners that were not registered
      * with (@link registerAsService).
      * @hide
      */
@@ -708,11 +787,8 @@
     /**
      * Request that the listener be rebound, after a previous call to (@link requestUnbind).
      *
-     * <P>This method will fail for listeners that have
+     * <p>This method will fail for listeners that have
      * not been granted the permission by the user.
-     *
-     * <P>The service should wait for the {@link #onListenerConnected()} event
-     * before performing any operations.
      */
     public static void requestRebind(ComponentName componentName)
             throws RemoteException {
@@ -724,14 +800,19 @@
     /**
      * Request that the service be unbound.
      *
-     * <P>This will no longer receive updates until
+     * <p>This will no longer receive updates until
      * {@link #requestRebind(ComponentName)} is called.
      * The service will likely be kiled by the system after this call.
+     *
+     * <p>The service should wait for the {@link #onListenerConnected()} event
+     * before performing this operation. I know it's tempting, but you must wait.
      */
     public final void requestUnbind() throws RemoteException {
         if (mWrapper != null) {
             INotificationManager noMan = getNotificationInterface();
             noMan.requestUnbindListener(mWrapper);
+            // Disable future messages.
+            isConnected = false;
         }
     }
 
@@ -842,6 +923,7 @@
             synchronized (mLock) {
                 applyUpdateLocked(update);
             }
+            isConnected = true;
             mHandler.obtainMessage(MyHandler.MSG_ON_LISTENER_CONNECTED).sendToTarget();
         }
 
@@ -980,6 +1062,8 @@
         private int mSuppressedVisualEffects;
         private @Importance int mImportance;
         private CharSequence mImportanceExplanation;
+        // System specified group key.
+        private String mOverrideGroupKey;
 
         public Ranking() {}
 
@@ -1058,9 +1142,17 @@
             return mImportanceExplanation;
         }
 
+        /**
+         * If the system has overriden the group key, then this will be non-null, and this
+         * key should be used to bundle notifications.
+         */
+        public String getOverrideGroupKey() {
+            return mOverrideGroupKey;
+        }
+
         private void populate(String key, int rank, boolean matchesInterruptionFilter,
                 int visibilityOverride, int suppressedVisualEffects, int importance,
-                CharSequence explanation) {
+                CharSequence explanation, String overrideGroupKey) {
             mKey = key;
             mRank = rank;
             mIsAmbient = importance < IMPORTANCE_LOW;
@@ -1069,6 +1161,7 @@
             mSuppressedVisualEffects = suppressedVisualEffects;
             mImportance = importance;
             mImportanceExplanation = explanation;
+            mOverrideGroupKey = overrideGroupKey;
         }
 
         /**
@@ -1112,6 +1205,7 @@
         private ArrayMap<String, Integer> mSuppressedVisualEffects;
         private ArrayMap<String, Integer> mImportance;
         private ArrayMap<String, String> mImportanceExplanation;
+        private ArrayMap<String, String> mOverrideGroupKeys;
 
         private RankingMap(NotificationRankingUpdate rankingUpdate) {
             mRankingUpdate = rankingUpdate;
@@ -1138,7 +1232,7 @@
             int rank = getRank(key);
             outRanking.populate(key, rank, !isIntercepted(key),
                     getVisibilityOverride(key), getSuppressedVisualEffects(key),
-                    getImportance(key), getImportanceExplanation(key));
+                    getImportance(key), getImportanceExplanation(key), getOverrideGroupKey(key));
             return rank >= 0;
         }
 
@@ -1209,6 +1303,15 @@
             return mImportanceExplanation.get(key);
         }
 
+        private String getOverrideGroupKey(String key) {
+            synchronized (this) {
+                if (mOverrideGroupKeys == null) {
+                    buildOverrideGroupKeys();
+                }
+            }
+            return mOverrideGroupKeys.get(key);
+        }
+
         // Locked by 'this'
         private void buildRanksLocked() {
             String[] orderedKeys = mRankingUpdate.getOrderedKeys();
@@ -1263,6 +1366,15 @@
             }
         }
 
+        // Locked by 'this'
+        private void buildOverrideGroupKeys() {
+            Bundle overrideGroupKeys = mRankingUpdate.getOverrideGroupKeys();
+            mOverrideGroupKeys = new ArrayMap<>(overrideGroupKeys.size());
+            for (String key: overrideGroupKeys.keySet()) {
+                mOverrideGroupKeys.put(key, overrideGroupKeys.getString(key));
+            }
+        }
+
         // ----------- Parcelable
 
         @Override
@@ -1303,6 +1415,9 @@
 
         @Override
         public void handleMessage(Message msg) {
+            if (!isConnected) {
+                return;
+            }
             switch (msg.what) {
                 case MSG_ON_NOTIFICATION_POSTED: {
                     SomeArgs args = (SomeArgs) msg.obj;
diff --git a/core/java/android/service/notification/NotificationRankerService.java b/core/java/android/service/notification/NotificationRankerService.java
index 47fdac6..ee5361a 100644
--- a/core/java/android/service/notification/NotificationRankerService.java
+++ b/core/java/android/service/notification/NotificationRankerService.java
@@ -22,14 +22,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.util.Log;
 import com.android.internal.os.SomeArgs;
 
+import java.util.List;
+
 /**
  * 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.
@@ -91,27 +96,8 @@
     /** Notification was canceled by the owning managed profile being turned off. */
     public static final int REASON_PROFILE_TURNED_OFF = 15;
 
-    public class Adjustment {
-        int mImportance;
-        CharSequence mExplanation;
-        Uri mReference;
-
-        /**
-         * Create a notification importance adjustment.
-         *
-         * @param importance The final importance of the notification.
-         * @param explanation A human-readable justification for the adjustment.
-         * @param reference A reference to an external object that augments the
-         *                  explanation, such as a
-         *                  {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
-         *                  or null.
-         */
-        public Adjustment(int importance, CharSequence explanation, Uri reference) {
-            mImportance = importance;
-            mExplanation = explanation;
-            mReference = reference;
-        }
-    }
+    /** Autobundled summary notification was canceled because its group was unbundled */
+    public static final int REASON_UNAUTOBUNDLED = 16;
 
     private Handler mHandler;
 
@@ -200,18 +186,32 @@
     }
 
     /**
-     * Change the importance of an existing notification.  N.B. this won’t cause
+     * Updates a notification.  N.B. this won’t cause
      * an existing notification to alert, but might allow a future update to
      * this notification to alert.
      *
-     * @param key the notification key
-     * @param adjustment the new importance with an explanation
+     * @param adjustment the adjustment with an explanation
      */
-    public final void adjustImportance(String key, Adjustment adjustment) {
+    public final void adjustNotification(Adjustment adjustment) {
         if (!isBound()) return;
         try {
-            getNotificationInterface().setImportanceFromRankerService(mWrapper, key,
-                    adjustment.mImportance, adjustment.mExplanation);
+            getNotificationInterface().applyAdjustmentFromRankerService(mWrapper, adjustment);
+        } catch (android.os.RemoteException ex) {
+            Log.v(TAG, "Unable to contact notification manager", ex);
+        }
+    }
+
+    /**
+     * Updates existing notifications. Re-ranking won't occur until all adjustments are applied.
+     * N.B. this won’t cause an existing notification to alert, but might allow a future update to
+     * these notifications to alert.
+     *
+     * @param adjustments a list of adjustments with explanations
+     */
+    public final void adjustNotifications(List<Adjustment> adjustments) {
+        if (!isBound()) return;
+        try {
+            getNotificationInterface().applyAdjustmentsFromRankerService(mWrapper, adjustments);
         } catch (android.os.RemoteException ex) {
             Log.v(TAG, "Unable to contact notification manager", ex);
         }
@@ -299,7 +299,7 @@
                     args.recycle();
                     Adjustment adjustment = onNotificationEnqueued(sbn, importance, user);
                     if (adjustment != null) {
-                        adjustImportance(sbn.getKey(), adjustment);
+                        adjustNotification(adjustment);
                     }
                 } break;
 
diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java
index 79f6fc4..788b5c0 100644
--- a/core/java/android/service/notification/NotificationRankingUpdate.java
+++ b/core/java/android/service/notification/NotificationRankingUpdate.java
@@ -30,16 +30,18 @@
     private final Bundle mSuppressedVisualEffects;
     private final int[] mImportance;
     private final Bundle mImportanceExplanation;
+    private final Bundle mOverrideGroupKeys;
 
     public NotificationRankingUpdate(String[] keys, String[] interceptedKeys,
             Bundle visibilityOverrides, Bundle suppressedVisualEffects,
-            int[] importance, Bundle explanation) {
+            int[] importance, Bundle explanation, Bundle overrideGroupKeys) {
         mKeys = keys;
         mInterceptedKeys = interceptedKeys;
         mVisibilityOverrides = visibilityOverrides;
         mSuppressedVisualEffects = suppressedVisualEffects;
         mImportance = importance;
         mImportanceExplanation = explanation;
+        mOverrideGroupKeys = overrideGroupKeys;
     }
 
     public NotificationRankingUpdate(Parcel in) {
@@ -50,6 +52,7 @@
         mImportance = new int[mKeys.length];
         in.readIntArray(mImportance);
         mImportanceExplanation = in.readBundle();
+        mOverrideGroupKeys = in.readBundle();
     }
 
     @Override
@@ -65,6 +68,7 @@
         out.writeBundle(mSuppressedVisualEffects);
         out.writeIntArray(mImportance);
         out.writeBundle(mImportanceExplanation);
+        out.writeBundle(mOverrideGroupKeys);
     }
 
     public static final Parcelable.Creator<NotificationRankingUpdate> CREATOR
@@ -101,4 +105,8 @@
     public Bundle getImportanceExplanation() {
         return mImportanceExplanation;
     }
+
+    public Bundle getOverrideGroupKeys() {
+        return mOverrideGroupKeys;
+    }
 }
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 198e43d..0221b66 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -33,7 +33,8 @@
     private final int id;
     private final String tag;
     private final String key;
-    private final String groupKey;
+    private String groupKey;
+    private String overrideGroupKey;
 
     private final int uid;
     private final String opPkg;
@@ -51,6 +52,27 @@
                 System.currentTimeMillis());
     }
 
+    /** @hide */
+    public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
+            int initialPid, Notification notification, UserHandle user, String overrideGroupKey,
+            long postTime) {
+        if (pkg == null) throw new NullPointerException();
+        if (notification == null) throw new NullPointerException();
+
+        this.pkg = pkg;
+        this.opPkg = opPkg;
+        this.id = id;
+        this.tag = tag;
+        this.uid = uid;
+        this.initialPid = initialPid;
+        this.notification = notification;
+        this.user = user;
+        this.postTime = postTime;
+        this.overrideGroupKey = overrideGroupKey;
+        this.key = key();
+        this.groupKey = groupKey();
+    }
+
     public StatusBarNotification(String pkg, String opPkg, int id, String tag, int uid,
             int initialPid, int score, Notification notification, UserHandle user,
             long postTime) {
@@ -84,15 +106,27 @@
         this.notification = new Notification(in);
         this.user = UserHandle.readFromParcel(in);
         this.postTime = in.readLong();
+        if (in.readInt() != 0) {
+            this.overrideGroupKey = in.readString();
+        } else {
+            this.overrideGroupKey = null;
+        }
         this.key = key();
         this.groupKey = groupKey();
     }
 
     private String key() {
-        return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
+        String sbnKey = user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
+        if (overrideGroupKey != null && getNotification().isGroupSummary()) {
+            sbnKey = sbnKey + "|" + overrideGroupKey;
+        }
+        return sbnKey;
     }
 
     private String groupKey() {
+        if (overrideGroupKey != null) {
+            return user.getIdentifier() + "|" + pkg + "|" + "g:" + overrideGroupKey;
+        }
         final String group = getNotification().getGroup();
         final String sortKey = getNotification().getSortKey();
         if (group == null && sortKey == null) {
@@ -105,6 +139,17 @@
                         : "g:" + group);
     }
 
+    /**
+     * Returns true if this notification is part of a group.
+     */
+    public boolean isGroup() {
+        if (overrideGroupKey != null || getNotification().getGroup() != null
+                || getNotification().getSortKey() != null) {
+            return true;
+        }
+        return false;
+    }
+
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(this.pkg);
         out.writeString(this.opPkg);
@@ -121,6 +166,12 @@
         user.writeToParcel(out, flags);
 
         out.writeLong(this.postTime);
+        if (this.overrideGroupKey != null) {
+            out.writeInt(1);
+            out.writeString(this.overrideGroupKey);
+        } else {
+            out.writeInt(0);
+        }
     }
 
     public int describeContents() {
@@ -149,22 +200,22 @@
         this.notification.cloneInto(no, false); // light copy
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                0, no, this.user, this.postTime);
+                no, this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public StatusBarNotification clone() {
         return new StatusBarNotification(this.pkg, this.opPkg,
                 this.id, this.tag, this.uid, this.initialPid,
-                0, this.notification.clone(), this.user, this.postTime);
+                this.notification.clone(), this.user, this.overrideGroupKey, this.postTime);
     }
 
     @Override
     public String toString() {
         return String.format(
-                "StatusBarNotification(pkg=%s user=%s id=%d tag=%s score=%d key=%s: %s)",
+                "StatusBarNotification(pkg=%s user=%s id=%d tag=%s key=%s: %s)",
                 this.pkg, this.user, this.id, this.tag,
-                0, this.key, this.notification);
+                this.key, this.notification);
     }
 
     /** Convenience method to check the notification's flags for
@@ -258,6 +309,21 @@
     }
 
     /**
+     * Sets the override group key.
+     */
+    public void setOverrideGroupKey(String overrideGroupKey) {
+        this.overrideGroupKey = overrideGroupKey;
+        groupKey = groupKey();
+    }
+
+    /**
+     * Returns the override group key.
+     */
+    public String getOverrideGroupKey() {
+        return overrideGroupKey;
+    }
+
+    /**
      * @hide
      */
     public Context getPackageContext(Context context) {
diff --git a/core/java/android/service/quicksettings/IQSService.aidl b/core/java/android/service/quicksettings/IQSService.aidl
index 5434e2e..747f185 100644
--- a/core/java/android/service/quicksettings/IQSService.aidl
+++ b/core/java/android/service/quicksettings/IQSService.aidl
@@ -28,7 +28,6 @@
             String contentDescription);
     void onShowDialog(in Tile tile);
     void onStartActivity(in Tile tile);
-    void setTileMode(in ComponentName component, int mode);
     boolean isLocked();
     boolean isSecure();
     void startUnlockAndRun(in Tile tile);
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index 553d539..4e9a075 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -89,28 +89,24 @@
     public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
 
     /**
-     * The tile mode hasn't been set yet.
-     * @hide
-     */
-    public static final int TILE_MODE_UNSET = 0;
-
-    /**
-     * Constant to be returned by {@link #onTileAdded}.
-     * <p>
-     * Passive mode is the default mode for tiles.  The System will tell the tile
-     * when it is most important to update by putting it in the listening state.
-     */
-    public static final int TILE_MODE_PASSIVE = 1;
-
-    /**
-     * Constant to be returned by {@link #onTileAdded}.
+     * Meta-data for tile definition to set a tile into active mode.
      * <p>
      * Active mode is for tiles which already listen and keep track of their state in their
      * own process.  These tiles may request to send an update to the System while their process
      * is alive using {@link #requestListeningState}.  The System will only bind these tiles
      * on its own when a click needs to occur.
+     *
+     * To make a TileService an active tile, set this meta-data to true on the TileService's
+     * manifest declaration.
+     * <pre class="prettyprint">
+     * {@literal
+     * <meta-data android:name="android.service.quicksettings.ACTIVE_TILE"
+     *      android:value="true" />
+     * }
+     * </pre>
      */
-    public static final int TILE_MODE_ACTIVE = 2;
+    public static final String META_DATA_ACTIVE_TILE
+            = "android.service.quicksettings.ACTIVE_TILE";
 
     /**
      * Used to notify SysUI that Listening has be requested.
@@ -147,12 +143,8 @@
      * Note that this is not guaranteed to be called between {@link #onCreate()}
      * and {@link #onStartListening()}, it will only be called when the tile is added
      * and not on subsequent binds.
-     *
-     * @see #TILE_MODE_PASSIVE
-     * @see #TILE_MODE_ACTIVE
      */
-    public int onTileAdded() {
-        return TILE_MODE_PASSIVE;
+    public void onTileAdded() {
     }
 
     /**
@@ -386,15 +378,7 @@
                     }
                     break;
                 case MSG_TILE_ADDED:
-                    int mode = TileService.this.onTileAdded();
-                    if (mService == null) {
-                        return;
-                    }
-                    try {
-                        mService.setTileMode(new ComponentName(TileService.this,
-                                TileService.this.getClass()), mode);
-                    } catch (RemoteException e) {
-                    }
+                    TileService.this.onTileAdded();
                     break;
                 case MSG_TILE_REMOVED:
                     if (mListening) {
@@ -431,8 +415,8 @@
     /**
      * Requests that a tile be put in the listening state so it can send an update.
      *
-     * This method is only applicable to tiles that return {@link #TILE_MODE_ACTIVE} from
-     * {@link #onTileAdded()}, and will do nothing otherwise.
+     * This method is only applicable to tiles that have {@link #META_DATA_ACTIVE_TILE} defined
+     * as true on their TileService Manifest declaration, and will do nothing otherwise.
      */
     public static final void requestListeningState(Context context, ComponentName component) {
         Intent intent = new Intent(ACTION_REQUEST_LISTENING);
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
new file mode 100644
index 0000000..62ecab3
--- /dev/null
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -0,0 +1,46 @@
+/**
+ * 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.service.vr.IVrStateCallbacks;
+
+/** @hide */
+interface IVrManager {
+
+    /**
+     * Add a callback to be notified when VR mode state changes.
+     *
+     * @param cb the callback instance to add.
+     */
+    void registerListener(in IVrStateCallbacks cb);
+
+    /**
+     * Remove the callack from the current set of registered callbacks.
+     *
+     * @param cb the callback to remove.
+     */
+    void unregisterListener(in IVrStateCallbacks cb);
+
+    /**
+     * Return current VR mode state.
+     *
+     * @return {@code true} if VR mode is enabled.
+     */
+    boolean getVrModeState();
+
+}
+
diff --git a/core/java/android/service/vr/IVrStateCallbacks.aidl b/core/java/android/service/vr/IVrStateCallbacks.aidl
new file mode 100644
index 0000000..c4fdcd0
--- /dev/null
+++ b/core/java/android/service/vr/IVrStateCallbacks.aidl
@@ -0,0 +1,24 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.vr;
+
+/** @hide */
+oneway interface IVrStateCallbacks {
+
+    void onVrStateChanged(in boolean enabled);
+
+}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index 328fe99..7f0021d 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -183,8 +183,10 @@
 
         if (includepad) {
             spacing = metrics.bottom - metrics.top;
+            mDesc = metrics.bottom;
         } else {
             spacing = metrics.descent - metrics.ascent;
+            mDesc = metrics.descent;
         }
 
         mBottom = spacing;
@@ -208,8 +210,6 @@
             mTopPadding = metrics.top - metrics.ascent;
             mBottomPadding = metrics.bottom - metrics.descent;
         }
-
-        mDesc = spacing + mBottomPadding + (includepad ? metrics.top : metrics.ascent);
     }
 
     /**
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6a33579..94ce57a 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -1132,22 +1132,12 @@
 
     @Override
     public int getLineTop(int line) {
-        int top = mLines[mColumns * line + TOP];
-        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount &&
-                line != mLineCount) {
-            top += getBottomPadding();
-        }
-        return top;
+        return mLines[mColumns * line + TOP];
     }
 
     @Override
     public int getLineDescent(int line) {
-        int descent = mLines[mColumns * line + DESCENT];
-        if (mMaximumVisibleLineCount > 0 && line >= mMaximumVisibleLineCount - 1 && // -1 intended
-                line != mLineCount) {
-            descent += getBottomPadding();
-        }
-        return descent;
+        return mLines[mColumns * line + DESCENT];
     }
 
     @Override
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
index 29a72fd..f1c8c7d 100644
--- a/core/java/android/util/PathParser.java
+++ b/core/java/android/util/PathParser.java
@@ -31,12 +31,7 @@
             throw new IllegalArgumentException("Path string can not be null.");
         }
         Path path = new Path();
-        boolean hasValidPathData = nParseStringForPath(path.mNativePath, pathString,
-                pathString.length());
-        if (!hasValidPathData) {
-            throw new IllegalArgumentException("Path string: " + pathString +
-                    " does not contain valid path data");
-        }
+        nParseStringForPath(path.mNativePath, pathString, pathString.length());
         return path;
     }
 
@@ -104,7 +99,6 @@
             }
             super.finalize();
         }
-
     }
 
     /**
@@ -123,7 +117,7 @@
     }
 
     // Native functions are defined below.
-    private static native boolean nParseStringForPath(long pathPtr, String pathString,
+    private static native void nParseStringForPath(long pathPtr, String pathString,
             int stringLength);
     private static native void nCreatePathFromPathData(long outPathPtr, long pathData);
     private static native long nCreateEmptyPathData();
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index e0c6770..636384c 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -1870,6 +1870,11 @@
         return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT;
     }
 
+    /** @hide */
+    public static final boolean isAltKey(int keyCode) {
+        return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT;
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index cff9d8e..37da869 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -22,7 +22,6 @@
 import android.util.AttributeSet;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
-import android.widget.TextView;
 
 import java.util.ArrayList;
 
@@ -37,7 +36,7 @@
     private final int mChildMinWidth;
     private final int mContentEndMargin;
     private View mAppName;
-    private View mSubTextView;
+    private View mHeaderText;
     private OnClickListener mExpandClickListener;
     private HeaderTouchListener mTouchListener = new HeaderTouchListener();
     private ImageView mExpandButton;
@@ -73,11 +72,10 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
         mAppName = findViewById(com.android.internal.R.id.app_name_text);
-        mSubTextView = findViewById(com.android.internal.R.id.header_sub_text);
+        mHeaderText = findViewById(com.android.internal.R.id.header_text);
         mExpandButton = (ImageView) findViewById(com.android.internal.R.id.expand_button);
         mIcon = findViewById(com.android.internal.R.id.icon);
         mProfileBadge = findViewById(com.android.internal.R.id.profile_badge);
-        mInfo = findViewById(com.android.internal.R.id.header_content_info);
     }
 
     @Override
@@ -105,15 +103,7 @@
         }
         if (totalWidth > givenWidth) {
             int overFlow = totalWidth - givenWidth;
-            // We are overflowing, lets shrink the info first
-            final int infoWidth = mInfo.getMeasuredWidth();
-            if (mInfo.getVisibility() != GONE && infoWidth > mChildMinWidth) {
-                int newSize = infoWidth - Math.min(infoWidth - mChildMinWidth, overFlow);
-                int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
-                mInfo.measure(childWidthSpec, wrapContentHeightSpec);
-                overFlow -= infoWidth - newSize;
-            }
-            // still overflowing, lets shrink the app name now
+            // We are overflowing, lets shrink the app name first
             final int appWidth = mAppName.getMeasuredWidth();
             if (overFlow > 0 && mAppName.getVisibility() != GONE && appWidth > mChildMinWidth) {
                 int newSize = appWidth - Math.min(appWidth - mChildMinWidth, overFlow);
@@ -121,13 +111,13 @@
                 mAppName.measure(childWidthSpec, wrapContentHeightSpec);
                 overFlow -= appWidth - newSize;
             }
-            // still overflowing, finaly we shrink the subtext
-            if (overFlow > 0 && mSubTextView.getVisibility() != GONE) {
+            // still overflowing, finaly we shrink the header text
+            if (overFlow > 0 && mHeaderText.getVisibility() != GONE) {
                 // we're still too big
-                final int subTextWidth = mSubTextView.getMeasuredWidth();
-                int newSize = Math.max(0, subTextWidth - overFlow);
+                final int textWidth = mHeaderText.getMeasuredWidth();
+                int newSize = Math.max(0, textWidth - overFlow);
                 int childWidthSpec = MeasureSpec.makeMeasureSpec(newSize, MeasureSpec.AT_MOST);
-                mSubTextView.measure(childWidthSpec, wrapContentHeightSpec);
+                mHeaderText.measure(childWidthSpec, wrapContentHeightSpec);
             }
         }
         setMeasuredDimension(givenWidth, givenHeight);
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index df774b4..f44d4c1a 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -485,15 +485,25 @@
     }
 
     /**
-     * Stops any rendering into the surface. Use this if it is unclear whether
+     * Halts any current rendering into the surface. Use this if it is unclear whether
      * or not the surface used by the HardwareRenderer will be changing. It
-     * Suspends any rendering into the surface, but will not do any destruction
+     * Suspends any rendering into the surface, but will not do any destruction.
+     *
+     * Any subsequent draws will override the pause, resuming normal operation.
      */
     boolean pauseSurface(Surface surface) {
         return nPauseSurface(mNativeProxy, surface);
     }
 
     /**
+     * Hard stops or resumes rendering into the surface. This flag is used to
+     * determine whether or not it is safe to use the given surface *at all*
+     */
+    void setStopped(boolean stopped) {
+        nSetStopped(mNativeProxy, stopped);
+    }
+
+    /**
      * Destroys all hardware rendering resources associated with the specified
      * view hierarchy.
      *
@@ -794,8 +804,7 @@
         }
 
         final long[] frameInfo = choreographer.mFrameInfo.mFrameInfo;
-        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length,
-                mRootNode.mNativeRenderNode);
+        int syncResult = nSyncAndDrawFrame(mNativeProxy, frameInfo, frameInfo.length);
         if ((syncResult & SYNC_LOST_SURFACE_REWARD_IF_FOUND) != 0) {
             setEnabled(false);
             attachInfo.mViewRootImpl.mSurface.release();
@@ -989,13 +998,13 @@
     private static native void nInitialize(long nativeProxy, Surface window);
     private static native void nUpdateSurface(long nativeProxy, Surface window);
     private static native boolean nPauseSurface(long nativeProxy, Surface window);
+    private static native void nSetStopped(long nativeProxy, boolean stopped);
     private static native void nSetup(long nativeProxy, int width, int height,
             float lightRadius, int ambientShadowAlpha, int spotShadowAlpha);
     private static native void nSetLightCenter(long nativeProxy,
             float lightX, float lightY, float lightZ);
     private static native void nSetOpaque(long nativeProxy, boolean opaque);
-    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size,
-            long rootRenderNode);
+    private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
     private static native void nDestroy(long nativeProxy, long rootRenderNode);
     private static native void nRegisterAnimatingRenderNode(long rootRenderNode, long animatingNode);
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b95d830..7e51096 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8877,7 +8877,7 @@
      * @hide
      */
     public void clearAccessibilityFocus() {
-        clearAccessibilityFocusNoCallbacks();
+        clearAccessibilityFocusNoCallbacks(0);
 
         // Clear the global reference of accessibility focus if this view or
         // any of its descendants had accessibility focus. This will NOT send
@@ -8920,14 +8920,27 @@
     /**
      * Clears accessibility focus without calling any callback methods
      * normally invoked in {@link #clearAccessibilityFocus()}. This method
-     * is used for clearing accessibility focus when giving this focus to
-     * another view.
+     * is used separately from that one for clearing accessibility focus when
+     * giving this focus to another view.
+     *
+     * @param action The action, if any, that led to focus being cleared. Set to
+     * AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS to specify that focus is moving within
+     * the window.
      */
-    void clearAccessibilityFocusNoCallbacks() {
+    void clearAccessibilityFocusNoCallbacks(int action) {
         if ((mPrivateFlags2 & PFLAG2_ACCESSIBILITY_FOCUSED) != 0) {
             mPrivateFlags2 &= ~PFLAG2_ACCESSIBILITY_FOCUSED;
             invalidate();
-            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+                event.setAction(action);
+                if (mAccessibilityDelegate != null) {
+                    mAccessibilityDelegate.sendAccessibilityEventUnchecked(this, event);
+                } else {
+                    sendAccessibilityEventUnchecked(event);
+                }
+            }
         }
     }
 
@@ -20424,21 +20437,21 @@
          * the touch point.
          * </p>
          *
-         * @param shadowSize A {@link android.graphics.Point} containing the width and height
+         * @param outShadowSize A {@link android.graphics.Point} containing the width and height
          * of the shadow image. Your application must set {@link android.graphics.Point#x} to the
          * desired width and must set {@link android.graphics.Point#y} to the desired height of the
          * image.
          *
-         * @param shadowTouchPoint A {@link android.graphics.Point} for the position within the
+         * @param outShadowTouchPoint A {@link android.graphics.Point} for the position within the
          * shadow image that should be underneath the touch point during the drag and drop
          * operation. Your application must set {@link android.graphics.Point#x} to the
          * X coordinate and {@link android.graphics.Point#y} to the Y coordinate of this position.
          */
-        public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
+        public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
             final View view = mView.get();
             if (view != null) {
-                shadowSize.set(view.getWidth(), view.getHeight());
-                shadowTouchPoint.set(shadowSize.x / 2, shadowSize.y / 2);
+                outShadowSize.set(view.getWidth(), view.getHeight());
+                outShadowTouchPoint.set(outShadowSize.x / 2, outShadowSize.y / 2);
             } else {
                 Log.e(View.VIEW_LOG_TAG, "Asked for drag thumb metrics but no view");
             }
diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java
index 4c75935..3e277eb 100644
--- a/core/java/android/view/ViewAnimationUtils.java
+++ b/core/java/android/view/ViewAnimationUtils.java
@@ -41,6 +41,22 @@
      * As a result {@link AnimatorListener#onAnimationEnd(Animator)}
      * will occur after the animation has ended, but it may be delayed depending
      * on thread responsiveness.
+     * <p>
+     * Note that if any start delay is set on the reveal animator, the start radius
+     * will not be applied to the reveal circle until the start delay has passed.
+     * If it's desired to set a start radius on the reveal circle during the start
+     * delay, one workaround could be adding an animator with the same start and
+     * end radius. For example:
+     * <pre><code>
+     * public static Animator createRevealWithDelay(View view, int centerX, int centerY, float startRadius, float endRadius) {
+     *     Animator delayAnimator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, startRadius);
+     *     delayAnimator.setDuration(delayTimeMS);
+     *     Animator revealAnimator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, startRadius, endRadius);
+     *     AnimatorSet set = new AnimatorSet();
+     *     set.playSequentially(delayAnimator, revealAnimator);
+     *     return set;
+     * }
+     * </code></pre>
      *
      * @param view The View will be clipped to the animating circle.
      * @param centerX The x coordinate of the center of the animating circle, relative to
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 5b2877f..94c4cef 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1079,13 +1079,16 @@
     void setWindowStopped(boolean stopped) {
         if (mStopped != stopped) {
             mStopped = stopped;
+            final ThreadedRenderer renderer = mAttachInfo.mHardwareRenderer;
+            if (renderer != null) {
+                if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle() + " set to " + mStopped);
+                renderer.setStopped(mStopped);
+            }
             if (!mStopped) {
                 scheduleTraversals();
             } else {
-                if (mAttachInfo.mHardwareRenderer != null) {
-                    if (DEBUG_DRAW) Log.d(mTag, "WindowStopped on " + getTitle());
-                    mAttachInfo.mHardwareRenderer.updateSurface(null);
-                    mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
+                if (renderer != null) {
+                    renderer.destroyHardwareResources(mView);
                 }
             }
         }
@@ -3071,7 +3074,8 @@
 
             // Clear accessibility focus on the host after clearing state since
             // this method may be reentrant.
-            focusHost.clearAccessibilityFocusNoCallbacks();
+            focusHost.clearAccessibilityFocusNoCallbacks(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
 
             AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
             if (provider != null) {
@@ -3088,7 +3092,8 @@
         }
         if (mAccessibilityFocusedHost != null) {
             // Clear accessibility focus in the view.
-            mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
+            mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
         }
 
         // Set the new focus host and node.
@@ -6623,7 +6628,7 @@
             // Error state: virtual view with no provider. Clear focus.
             mAccessibilityFocusedHost = null;
             mAccessibilityFocusedVirtualView = null;
-            focusedHost.clearAccessibilityFocusNoCallbacks();
+            focusedHost.clearAccessibilityFocusNoCallbacks(0);
             return;
         }
 
@@ -6673,7 +6678,7 @@
         if (mAccessibilityFocusedVirtualView == null) {
             // Error state: The node no longer exists. Clear focus.
             mAccessibilityFocusedHost = null;
-            focusedHost.clearAccessibilityFocusNoCallbacks();
+            focusedHost.clearAccessibilityFocusNoCallbacks(0);
 
             // This will probably fail, but try to keep the provider's internal
             // state consistent by clearing focus.
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c372c30..584233c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1240,6 +1240,13 @@
         public static final int PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME = 0x00010000;
 
         /**
+         * Flag to indicate that this window is always drawing the status bar background, no matter
+         * what the other flags are.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND = 0x00020000;
+
+        /**
          * Control flags that are private to the platform.
          * @hide
          */
@@ -1701,6 +1708,14 @@
          */
         public int accessibilityIdOfAnchor = -1;
 
+        /**
+         * The window title isn't kept in sync with what is displayed in the title bar, so we
+         * separately track the currently shown title to provide to accessibility.
+         *
+         * @hide
+         */
+        public CharSequence accessibilityTitle;
+
         public LayoutParams() {
             super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
             type = TYPE_APPLICATION;
@@ -1749,6 +1764,7 @@
                 title = "";
 
             mTitle = TextUtils.stringOrSpannedString(title);
+            accessibilityTitle = mTitle;
         }
 
         public final CharSequence getTitle() {
@@ -1808,6 +1824,7 @@
             out.writeInt(hasManualSurfaceInsets ? 1 : 0);
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
+            TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1859,6 +1876,7 @@
             hasManualSurfaceInsets = in.readInt() != 0;
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
+            accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -1900,6 +1918,8 @@
         /** {@hide} */
         public static final int ACCESSIBILITY_ANCHOR_CHANGED = 1 << 24;
         /** {@hide} */
+        public static final int ACCESSIBILITY_TITLE_CHANGED = 1 << 25;
+        /** {@hide} */
         public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
@@ -2065,6 +2085,13 @@
                 changes |= ACCESSIBILITY_ANCHOR_CHANGED;
             }
 
+            if (!Objects.equals(accessibilityTitle, o.accessibilityTitle)
+                    && o.accessibilityTitle != null) {
+                // NOTE: accessibilityTitle only copied if the originator set one.
+                accessibilityTitle = o.accessibilityTitle;
+                changes |= ACCESSIBILITY_TITLE_CHANGED;
+            }
+
             return changes;
         }
 
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 23b0df2..96f179b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1397,4 +1397,9 @@
      *         is allowed, so for example, if DOCKED_RIGHT is not allowed, DOCKED_LEFT is allowed.
      */
     public boolean isDockSideAllowed(int dockSide);
+
+    /**
+     * Called when the configuration has changed, and it's safe to load new values from resources.
+     */
+    public void onConfigurationChanged();
 }
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 1536c29..d89c172 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -93,8 +93,12 @@
      */
     public static final int ZORDER_BOTTOM = -1;
 
-    private static final boolean USE_CLOSEGUARD
-            = SystemProperties.getBoolean("log.closeguard.Animation", false);
+    // Use a preload holder to isolate static initialization into inner class, which allows
+    // Animation and its subclasses to be compile-time initialized.
+    private static class NoImagePreloadHolder {
+        public static final boolean USE_CLOSEGUARD
+                = SystemProperties.getBoolean("log.closeguard.Animation", false);
+    }
 
     /**
      * Set by {@link #getTransformation(long, Transformation)} when the animation ends.
@@ -859,7 +863,7 @@
             if (!mStarted) {
                 fireAnimationStart();
                 mStarted = true;
-                if (USE_CLOSEGUARD) {
+                if (NoImagePreloadHolder.USE_CLOSEGUARD) {
                     guard.open("cancel or detach or getTransformation");
                 }
             }
diff --git a/core/java/android/webkit/WebViewProviderResponse.java b/core/java/android/webkit/WebViewProviderResponse.java
index f5e09e2..c0aeb59 100644
--- a/core/java/android/webkit/WebViewProviderResponse.java
+++ b/core/java/android/webkit/WebViewProviderResponse.java
@@ -21,7 +21,7 @@
 import android.os.Parcelable;
 
 /** @hide */
-public class WebViewProviderResponse implements Parcelable {
+public final class WebViewProviderResponse implements Parcelable {
 
     public WebViewProviderResponse(PackageInfo packageInfo, int status) {
         this.packageInfo = packageInfo;
@@ -56,6 +56,6 @@
         out.writeInt(status);
     }
 
-    PackageInfo packageInfo;
-    int status;
+    public final PackageInfo packageInfo;
+    public final int status;
 }
diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java
index e3357a7..0c5edc5 100644
--- a/core/java/android/widget/DatePicker.java
+++ b/core/java/android/widget/DatePicker.java
@@ -339,7 +339,9 @@
      *
      * @return {@code true} if the calendar view is shown
      * @see #getCalendarView()
+     * @deprecated Not supported by Material-style {@code calendar} mode
      */
+    @Deprecated
     public boolean getCalendarViewShown() {
         return mDelegate.getCalendarViewShown();
     }
@@ -347,13 +349,18 @@
     /**
      * Returns the {@link CalendarView} used by this picker.
      * <p>
-     * <strong>Note:</strong> This method returns {@code null} when the
+     * <strong>Note:</strong> This method throws an
+     * {@link UnsupportedOperationException} when the
      * {@link android.R.styleable#DatePicker_datePickerMode} attribute is set
      * to {@code calendar}.
      *
      * @return the calendar view
      * @see #getCalendarViewShown()
+     * @deprecated Not supported by Material-style {@code calendar} mode
+     * @throws UnsupportedOperationException if called when the picker is
+     *         displayed in {@code calendar} mode
      */
+    @Deprecated
     public CalendarView getCalendarView() {
         return mDelegate.getCalendarView();
     }
@@ -367,7 +374,9 @@
      *
      * @param shown {@code true} to show the calendar view, {@code false} to
      *              hide it
+     * @deprecated Not supported by Material-style {@code calendar} mode
      */
+    @Deprecated
     public void setCalendarViewShown(boolean shown) {
         mDelegate.setCalendarViewShown(shown);
     }
@@ -380,7 +389,9 @@
      * to {@code calendar}.
      *
      * @return {@code true} if the spinners are shown
+     * @deprecated Not supported by Material-style {@code calendar} mode
      */
+    @Deprecated
     public boolean getSpinnersShown() {
         return mDelegate.getSpinnersShown();
     }
@@ -394,7 +405,9 @@
      *
      * @param shown {@code true} to show the spinners, {@code false} to hide
      *              them
+     * @deprecated Not supported by Material-style {@code calendar} mode
      */
+    @Deprecated
     public void setSpinnersShown(boolean shown) {
         mDelegate.setSpinnersShown(shown);
     }
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 8ce2f9c..97936e7 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -148,18 +148,22 @@
 
     void setCalendarTextColor(ColorStateList calendarTextColor) {
         mCalendarTextColor = calendarTextColor;
+        notifyDataSetChanged();
     }
 
     void setDaySelectorColor(ColorStateList selectorColor) {
         mDaySelectorColor = selectorColor;
+        notifyDataSetChanged();
     }
 
     void setMonthTextAppearance(int resId) {
         mMonthTextAppearance = resId;
+        notifyDataSetChanged();
     }
 
     void setDayOfWeekTextAppearance(int resId) {
         mDayOfWeekTextAppearance = resId;
+        notifyDataSetChanged();
     }
 
     int getDayOfWeekTextAppearance() {
@@ -168,6 +172,7 @@
 
     void setDayTextAppearance(int resId) {
         mDayTextAppearance = resId;
+        notifyDataSetChanged();
     }
 
     int getDayTextAppearance() {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 7055f78..440ef21 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1778,6 +1778,18 @@
         if (translate) canvas.translate(0, -cursorOffsetVertical);
     }
 
+    void invalidateHandlesAndActionMode() {
+        if (mSelectionModifierCursorController != null) {
+            mSelectionModifierCursorController.invalidateHandles();
+        }
+        if (mInsertionPointCursorController != null) {
+            mInsertionPointCursorController.invalidateHandle();
+        }
+        if (mTextActionMode != null) {
+            mTextActionMode.invalidate();
+        }
+    }
+
     /**
      * Invalidates all the sub-display lists that overlap the specified character range
      */
@@ -3462,7 +3474,6 @@
                 popupBackground.getPadding(mTempRect);
                 width += mTempRect.left + mTempRect.right;
             }
-            mSuggestionListView.getLayoutParams().width = width;
             mPopupWindow.setWidth(width);
         }
 
@@ -4104,6 +4115,14 @@
             setMeasuredDimension(getPreferredWidth(), getPreferredHeight());
         }
 
+        @Override
+        public void invalidate() {
+            super.invalidate();
+            if (isShowing()) {
+                positionAtCursorOffset(getCurrentCursorOffset(), true);
+            }
+        };
+
         private int getPreferredWidth() {
             return Math.max(mDrawable.getIntrinsicWidth(), mMinSize);
         }
@@ -4170,7 +4189,12 @@
             return mTextView.getOffsetAtCoordinate(line, x);
         }
 
-        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+        /**
+         * @param offset Cursor offset. Must be in [-1, length].
+         * @param forceUpdatePosition whether to force update the position.  This should be true
+         * when If the parent has been scrolled, for example.
+         */
+        protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) {
             // A HandleView relies on the layout, which may be nulled by external methods
             Layout layout = mTextView.getLayout();
             if (layout == null) {
@@ -4181,7 +4205,7 @@
             layout = mTextView.getLayout();
 
             boolean offsetChanged = offset != mPreviousOffset;
-            if (offsetChanged || parentScrolled) {
+            if (offsetChanged || forceUpdatePosition) {
                 if (offsetChanged) {
                     updateSelection(offset);
                     addPositionToTouchUpFilter(offset);
@@ -4782,13 +4806,9 @@
             mPrevX = x;
         }
 
-        /**
-         * @param offset Cursor offset. Must be in [-1, length].
-         * @param parentScrolled If the parent has been scrolled or not.
-         */
         @Override
-        protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
-            super.positionAtCursorOffset(offset, parentScrolled);
+        protected void positionAtCursorOffset(int offset, boolean forceUpdatePosition) {
+            super.positionAtCursorOffset(offset, forceUpdatePosition);
             mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset);
         }
 
@@ -5014,6 +5034,12 @@
         public boolean isActive() {
             return mHandle != null && mHandle.isShowing();
         }
+
+        public void invalidateHandle() {
+            if (mHandle != null) {
+                mHandle.invalidate();
+            }
+        }
     }
 
     class SelectionModifierCursorController implements CursorController {
@@ -5418,6 +5444,15 @@
         public boolean isActive() {
             return mStartHandle != null && mStartHandle.isShowing();
         }
+
+        public void invalidateHandles() {
+            if (mStartHandle != null) {
+                mStartHandle.invalidate();
+            }
+            if (mEndHandle != null) {
+                mEndHandle.invalidate();
+            }
+        }
     }
 
     private class CorrectionHighlighter {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4483b7ba..48fd58b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3350,7 +3350,10 @@
         mShadowColor = color;
 
         // Will change text clip region
-        if (mEditor != null) mEditor.invalidateTextDisplayList();
+        if (mEditor != null) {
+            mEditor.invalidateTextDisplayList();
+            mEditor.invalidateHandlesAndActionMode();
+        }
         invalidate();
     }
 
@@ -8306,6 +8309,7 @@
             if (mEditor != null) {
                 if (oldStart >= 0) mEditor.invalidateTextDisplayList(mLayout, oldStart, oldEnd);
                 if (newStart >= 0) mEditor.invalidateTextDisplayList(mLayout, newStart, newEnd);
+                mEditor.invalidateHandlesAndActionMode();
             }
         }
 
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index a4e489c..ed6ab56 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -41,12 +41,12 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Parcelable;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
-import android.provider.DocumentsContract;
 import android.service.chooser.ChooserTarget;
 import android.service.chooser.ChooserTargetService;
 import android.service.chooser.IChooserTargetResult;
@@ -70,6 +70,7 @@
 import com.android.internal.R;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
+import com.google.android.collect.Lists;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -89,6 +90,7 @@
     private IntentSender mChosenComponentSender;
     private IntentSender mRefinementIntentSender;
     private RefinementResultReceiver mRefinementResultReceiver;
+    private ChooserTarget[] mCallerChooserTargets;
 
     private Intent mReferrerFillInIntent;
 
@@ -97,6 +99,7 @@
 
     private SharedPreferences mPinnedSharedPrefs;
     private static final float PINNED_TARGET_SCORE_BOOST = 1000.f;
+    private static final float CALLER_TARGET_SCORE_BOOST = 900.f;
     private static final String PINNED_SHARED_PREFS_NAME = "chooser_pin_settings";
     private static final String TARGET_DETAILS_FRAGMENT_TAG = "targetDetailsFragment";
 
@@ -219,6 +222,34 @@
                 Intent.EXTRA_CHOOSER_REFINEMENT_INTENT_SENDER);
         setSafeForwardingMode(true);
 
+        pa = intent.getParcelableArrayExtra(Intent.EXTRA_EXCLUDE_COMPONENTS);
+        if (pa != null) {
+            ComponentName[] names = new ComponentName[pa.length];
+            for (int i = 0; i < pa.length; i++) {
+                if (!(pa[i] instanceof ComponentName)) {
+                    Log.w(TAG, "Filtered component #" + i + " not a ComponentName: " + pa[i]);
+                    names = null;
+                    break;
+                }
+                names[i] = (ComponentName) pa[i];
+            }
+            setFilteredComponents(names);
+        }
+
+        pa = intent.getParcelableArrayExtra(Intent.EXTRA_CHOOSER_TARGETS);
+        if (pa != null) {
+            ChooserTarget[] targets = new ChooserTarget[pa.length];
+            for (int i = 0; i < pa.length; i++) {
+                if (!(pa[i] instanceof ChooserTarget)) {
+                    Log.w(TAG, "Chooser target #" + i + " not a ChooserTarget: " + pa[i]);
+                    targets = null;
+                    break;
+                }
+                targets[i] = (ChooserTarget) pa[i];
+            }
+            mCallerChooserTargets = targets;
+        }
+
         mPinnedSharedPrefs = getPinnedSharedPrefs(this);
         super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents,
                 null, false);
@@ -292,6 +323,9 @@
             boolean alwaysUseOption) {
         final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null;
         mChooserListAdapter = (ChooserListAdapter) adapter;
+        if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
+            mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets));
+        }
         mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter);
         mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView));
         adapterView.setAdapter(mChooserRowAdapter);
@@ -427,13 +461,19 @@
                         continue;
                     }
                 } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Could not look up service " + serviceComponent, e);
+                    Log.e(TAG, "Could not look up service " + serviceComponent
+                            + "; component name not found");
                     continue;
                 }
 
                 final ChooserTargetServiceConnection conn =
                         new ChooserTargetServiceConnection(this, dri);
-                if (bindService(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND)) {
+
+                // Explicitly specify Process.myUserHandle instead of calling bindService
+                // to avoid the warning from calling from the system process without an explicit
+                // user handle
+                if (bindServiceAsUser(serviceIntent, conn, BIND_AUTO_CREATE | BIND_NOT_FOREGROUND,
+                        Process.myUserHandle())) {
                     if (DEBUG) {
                         Log.d(TAG, "Binding service connection for target " + dri
                                 + " intent " + serviceIntent);
@@ -635,7 +675,11 @@
             if (mSourceInfo != null) {
                 return mSourceInfo.getResolvedIntent();
             }
-            return getTargetIntent();
+
+            final Intent targetIntent = new Intent(getTargetIntent());
+            targetIntent.setComponent(mChooserTarget.getComponentName());
+            targetIntent.putExtras(mChooserTarget.getIntentExtras());
+            return targetIntent;
         }
 
         @Override
@@ -650,8 +694,7 @@
         }
 
         private Intent getBaseIntentToSend() {
-            Intent result = mSourceInfo != null
-                    ? mSourceInfo.getResolvedIntent() : getTargetIntent();
+            Intent result = getResolvedIntent();
             if (result == null) {
                 Log.e(TAG, "ChooserTargetInfo: no base intent available to send");
             } else {
@@ -677,7 +720,19 @@
             }
             intent.setComponent(mChooserTarget.getComponentName());
             intent.putExtras(mChooserTarget.getIntentExtras());
-            activity.startActivityAsCaller(intent, options, true, userId);
+
+            // Important: we will ignore the target security checks in ActivityManager
+            // if and only if the ChooserTarget's target package is the same package
+            // where we got the ChooserTargetService that provided it. This lets a
+            // ChooserTargetService provide a non-exported or permission-guarded target
+            // to the chooser for the user to pick.
+            //
+            // If mSourceInfo is null, we got this ChooserTarget from the caller or elsewhere
+            // so we'll obey the caller's normal security checks.
+            final boolean ignoreTargetSecurity = mSourceInfo != null
+                    && mSourceInfo.getResolvedComponentName().getPackageName()
+                    .equals(mChooserTarget.getComponentName().getPackageName());
+            activity.startActivityAsCaller(intent, options, ignoreTargetSecurity, userId);
             return true;
         }
 
@@ -810,6 +865,9 @@
 
         @Override
         public float getScore(DisplayResolveInfo target) {
+            if (target == null) {
+                return CALLER_TARGET_SCORE_BOOST;
+            }
             float score = super.getScore(target);
             if (target.isPinned()) {
                 score += PINNED_TARGET_SCORE_BOOST;
@@ -1281,7 +1339,7 @@
     }
 
     static class ChooserTargetServiceConnection implements ServiceConnection {
-        private final DisplayResolveInfo mOriginalTarget;
+        private DisplayResolveInfo mOriginalTarget;
         private ComponentName mConnectedComponent;
         private ChooserActivity mChooserActivity;
         private final Object mLock = new Object();
@@ -1359,6 +1417,7 @@
         public void destroy() {
             synchronized (mLock) {
                 mChooserActivity = null;
+                mOriginalTarget = null;
             }
         }
 
@@ -1366,7 +1425,9 @@
         public String toString() {
             return "ChooserTargetServiceConnection{service="
                     + mConnectedComponent + ", activity="
-                    + mOriginalTarget.getResolveInfo().activityInfo.toString() + "}";
+                    + (mOriginalTarget != null
+                    ? mOriginalTarget.getResolveInfo().activityInfo.toString()
+                    : "<connection destroyed>") + "}";
         }
     }
 
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index ff680e2..f2bf9e1 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -105,6 +105,7 @@
     private final ArrayList<Intent> mIntents = new ArrayList<>();
     private ResolverComparator mResolverComparator;
     private PickTargetOptionRequest mPickOptionRequest;
+    private ComponentName[] mFilteredComponents;
 
     protected ResolverDrawerLayout mResolverDrawerLayout;
 
@@ -332,6 +333,24 @@
         }
     }
 
+    public final void setFilteredComponents(ComponentName[] components) {
+        mFilteredComponents = components;
+    }
+
+    public final boolean isComponentFiltered(ComponentInfo component) {
+        if (mFilteredComponents == null) {
+            return false;
+        }
+
+        final ComponentName checkName = component.getComponentName();
+        for (ComponentName name : mFilteredComponents) {
+            if (name.equals(checkName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Perform any initialization needed for voice interaction.
      */
@@ -1269,7 +1288,8 @@
                                 ai.applicationInfo.uid, ai.exported);
                         boolean suspended = (ai.applicationInfo.flags
                                 & ApplicationInfo.FLAG_SUSPENDED) != 0;
-                        if (granted != PackageManager.PERMISSION_GRANTED || suspended) {
+                        if (granted != PackageManager.PERMISSION_GRANTED || suspended
+                                || isComponentFiltered(ai)) {
                             // Access not allowed!
                             if (mOrigResolveList == currentResolveList) {
                                 mOrigResolveList = new ArrayList<>(mOrigResolveList);
diff --git a/core/java/com/android/internal/app/UnlaunchableAppActivity.java b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
index f6fbaab..27588e9 100644
--- a/core/java/com/android/internal/app/UnlaunchableAppActivity.java
+++ b/core/java/com/android/internal/app/UnlaunchableAppActivity.java
@@ -26,6 +26,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.IntentSender;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -53,6 +54,7 @@
 
     private int mUserId;
     private int mReason;
+    private IntentSender mTarget;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -60,6 +62,7 @@
         Intent intent = getIntent();
         mReason = intent.getIntExtra(EXTRA_UNLAUNCHABLE_REASON, -1);
         mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+        mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
 
         if (mUserId == UserHandle.USER_NULL) {
             Log.wtf(TAG, "Invalid user id: " + mUserId + ". Stopping.");
@@ -105,6 +108,14 @@
     public void onClick(DialogInterface dialog, int which) {
         if (mReason == UNLAUNCHABLE_REASON_QUIET_MODE && which == DialogInterface.BUTTON_POSITIVE) {
             UserManager.get(this).setQuietModeEnabled(mUserId, false);
+
+            if (mTarget != null) {
+                try {
+                    startIntentSenderForResult(mTarget, -1, null, 0, 0, 0);
+                } catch (IntentSender.SendIntentException e) {
+                    /* ignore */
+                }
+            }
         }
     }
 
@@ -121,4 +132,10 @@
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         return intent;
     }
+
+    public static Intent createInQuietModeDialogIntent(int userId, IntentSender target) {
+        Intent intent = createInQuietModeDialogIntent(userId);
+        intent.putExtra(Intent.EXTRA_INTENT, target);
+        return intent;
+    }
 }
diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
index 7252276..cd4f7b6 100644
--- a/core/java/com/android/internal/app/procstats/SparseMappingTable.java
+++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java
@@ -372,29 +372,35 @@
          * Throw an exception if one of a variety of internal consistency checks fails.
          */
         private void assertConsistency() {
-            // Assert that our sequewnce number has been initialized. If it hasn't
-            // that means someone tried to read or write data without allocating it
-            // since we were created or reset.
-            if (mSequence == UNINITIALIZED_SEQUENCE) {
-                logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
-                        + " SparseMappingTable.Table.  -- "
-                        + dumpInternalState());
-                return;
-            }
+            // Something with this checking isn't working and is triggering
+            // more problems than it's helping to debug.
+            //   Original bug: b/27045736
+            //   New bug: b/27960286
+            if (false) {
+                // Assert that our sequence number has been initialized. If it hasn't
+                // that means someone tried to read or write data without allocating it
+                // since we were created or reset.
+                if (mSequence == UNINITIALIZED_SEQUENCE) {
+                    logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in"
+                            + " SparseMappingTable.Table.  -- "
+                            + dumpInternalState());
+                    return;
+                }
 
-            // Assert that our sequence number matches mParent's.  If it isn't that means
-            // we have been reset and our
-            if (mSequence != mParent.mSequence) {
-                if (mSequence < mParent.mSequence) {
-                    logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
-                            + " called but not Table.resetTable() -- "
-                            + dumpInternalState());
-                    return;
-                } else if (mSequence > mParent.mSequence) {
-                    logOrThrow("Sequence mismatch. Table.resetTable()"
-                            + " called but not SparseMappingTable.resetTable() -- "
-                            + dumpInternalState());
-                    return;
+                // Assert that our sequence number matches mParent's.  If it isn't that means
+                // we have been reset and our
+                if (mSequence != mParent.mSequence) {
+                    if (mSequence < mParent.mSequence) {
+                        logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()"
+                                + " called but not Table.resetTable() -- "
+                                + dumpInternalState());
+                        return;
+                    } else if (mSequence > mParent.mSequence) {
+                        logOrThrow("Sequence mismatch. Table.resetTable()"
+                                + " called but not SparseMappingTable.resetTable() -- "
+                                + dumpInternalState());
+                        return;
+                    }
                 }
             }
         }
diff --git a/core/java/com/android/internal/os/BackgroundThread.java b/core/java/com/android/internal/os/BackgroundThread.java
index d6f7b20..cffba01 100644
--- a/core/java/com/android/internal/os/BackgroundThread.java
+++ b/core/java/com/android/internal/os/BackgroundThread.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Trace;
 
 /**
  * Shared singleton background thread for each process.
@@ -34,6 +35,7 @@
         if (sInstance == null) {
             sInstance = new BackgroundThread();
             sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index ea0fbda..f9ac563 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -45,7 +45,6 @@
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
-import android.os.Build;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -81,6 +80,8 @@
 import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Build.VERSION_CODES.M;
+import static android.os.Build.VERSION_CODES.N;
 import static android.view.View.MeasureSpec.AT_MOST;
 import static android.view.View.MeasureSpec.EXACTLY;
 import static android.view.View.MeasureSpec.getMode;
@@ -171,7 +172,7 @@
     private final Interpolator mShowInterpolator;
     private final Interpolator mHideInterpolator;
     private final int mBarEnterExitDuration;
-    private final boolean mForceWindowDrawsStatusBarBackground;
+    final boolean mForceWindowDrawsStatusBarBackground;
     private final int mSemiTransparentStatusBarColor;
 
     private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
@@ -236,7 +237,8 @@
         mBarEnterExitDuration = context.getResources().getInteger(
                 R.integer.dock_enter_exit_duration);
         mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean(
-                R.bool.config_forceWindowDrawsStatusBarBackground);
+                R.bool.config_forceWindowDrawsStatusBarBackground)
+                && context.getApplicationInfo().targetSdkVersion >= N;
         mSemiTransparentStatusBarColor = context.getResources().getColor(
                 R.color.system_bar_background_semi_transparent, null /* theme */);
 
@@ -1458,6 +1460,8 @@
             st.menu.close();
         }
 
+        releaseThreadedRenderer();
+
         if (mWindowResizeCallbacksAdded) {
             getViewRootImpl().removeWindowCallbacks(this);
             mWindowResizeCallbacksAdded = false;
@@ -2208,7 +2212,7 @@
         public void onDestroyActionMode(ActionMode mode) {
             mWrapped.onDestroyActionMode(mode);
             final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
-                    >= Build.VERSION_CODES.M;
+                    >= M;
             final boolean isPrimary;
             final boolean isFloating;
             if (isMncApp) {
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index e330de2..171a264 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -50,9 +50,12 @@
      * Called when the device has finished going to sleep.
      *
      * @param why {@link #OFF_BECAUSE_OF_USER}, {@link #OFF_BECAUSE_OF_ADMIN},
-     * or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     *            or {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     * @param cameraGestureTriggered whether the camera gesture was triggered between
+     *                               {@link #onStartedGoingToSleep} and this method; if it's been
+     *                               triggered, we shouldn't lock the device.
      */
-    void onFinishedGoingToSleep(int reason);
+    void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered);
 
     /**
      * Called when the device has started waking up.
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 0f257d7..d2ff9bc 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -516,8 +516,8 @@
         }
         mTitle = title;
         WindowManager.LayoutParams params = getAttributes();
-        if (!TextUtils.equals(title, params.getTitle())) {
-            params.setTitle(title);
+        if (!TextUtils.equals(title, params.accessibilityTitle)) {
+            params.accessibilityTitle = TextUtils.stringOrSpannedString(title);
             dispatchWindowAttributesChanged(getAttributes());
         }
     }
@@ -2435,6 +2435,8 @@
             mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
         }
 
+        WindowManager.LayoutParams params = getAttributes();
+
         // Non-floating windows on high end devices must put up decor beneath the system bars and
         // therefore must know about visibility changes of those.
         if (!mIsFloating && ActivityManager.isHighEndGfx()) {
@@ -2444,6 +2446,9 @@
                 setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
                         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags());
             }
+            if (mDecor.mForceWindowDrawsStatusBarBackground) {
+                params.privateFlags |= PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
+            }
         }
         if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
             decor.setSystemUiVisibility(
@@ -2459,8 +2464,6 @@
             }
         }
 
-        WindowManager.LayoutParams params = getAttributes();
-
         if (!hasSoftInputMode()) {
             params.softInputMode = a.getInt(
                     R.styleable.Window_windowSoftInputMode,
diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
index 29a9c8e..ddca51f 100644
--- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java
@@ -346,7 +346,15 @@
     private void showMenu(@NonNull MenuBuilder menu) {
         final LayoutInflater inflater = LayoutInflater.from(mContext);
         final MenuAdapter adapter = new MenuAdapter(menu, inflater, mOverflowOnly);
-        adapter.setForceShowIcon(mForceShowIcon);
+
+        // Apply "force show icon" setting; if the menu being shown is the top level menu, apply the
+        // setting set on this CascadingMenuPopup by its creating code. If it's a submenu, or if no
+        // "force" setting was explicitly set, determine the setting by examining the items.
+        if (!isShowing() && mForceShowIcon) {
+            adapter.setForceShowIcon(mForceShowIcon);
+        } else {
+            adapter.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(menu));
+        }
 
         final int menuWidth = measureIndividualMenuWidth(adapter, null, mContext, mMenuMaxWidth);
         final MenuPopupWindow popupWindow = createPopupWindow();
diff --git a/core/java/com/android/internal/view/menu/MenuPopup.java b/core/java/com/android/internal/view/menu/MenuPopup.java
index 42b1a56..16e4156 100644
--- a/core/java/com/android/internal/view/menu/MenuPopup.java
+++ b/core/java/com/android/internal/view/menu/MenuPopup.java
@@ -183,4 +183,25 @@
         }
         return (MenuAdapter) adapter;
     }
+
+    /**
+     * Returns whether icon spacing needs to be preserved for the given menu, based on whether any
+     * of its items contains an icon.
+     * @param menu
+     * @return Whether to preserve icon spacing.
+     */
+    protected static boolean shouldPreserveIconSpacing(MenuBuilder menu) {
+      boolean preserveIconSpacing = false;
+      final int count = menu.size();
+
+      for (int i = 0; i < count; i++) {
+          MenuItem childItem = menu.getItem(i);
+          if (childItem.isVisible() && childItem.getIcon() != null) {
+              preserveIconSpacing = true;
+              break;
+          }
+      }
+
+      return preserveIconSpacing;
+    }
 }
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 8ced36f..339c2bf 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -208,7 +208,7 @@
     @Override
     public void show() {
         if (!tryShow()) {
-            throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
+            throw new IllegalStateException("StandardMenuPopup cannot be used without an anchor");
         }
     }
 
@@ -266,14 +266,14 @@
             final MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu,
                     mShownAnchorView, mOverflowOnly, mPopupStyleAttr, mPopupStyleRes);
             subPopup.setPresenterCallback(mPresenterCallback);
-            subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
+            subPopup.setForceShowIcon(MenuPopup.shouldPreserveIconSpacing(subMenu));
 
             // Pass responsibility for handling onDismiss to the submenu.
             subPopup.setOnDismissListener(mOnDismissListener);
             mOnDismissListener = null;
 
             // Close this menu popup to make room for the submenu popup.
-            dismiss();
+            mMenu.close(false /* closeAllMenus */);
 
             // Show the new sub-menu popup at the same location as this popup.
             if (subPopup.tryShow(mXOffset, mYOffset)) {
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index da223a6..1848fe8 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -1460,6 +1460,7 @@
                 super(Preconditions.checkNotNull(popup).mContext);
                 this.mPopup = popup;
                 setScrollBarDefaultDelayBeforeFade(ViewConfiguration.getScrollDefaultDelay() * 3);
+                setScrollIndicators(View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
             }
 
             @Override
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index b07e36a..21c4d12 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -33,9 +33,12 @@
     void setLockPassword(in String password, in String savedPassword, int userId);
     VerifyCredentialResponse checkPassword(in String password, int userId);
     VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId);
+    VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, int userId);
     boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
+    void setSeparateProfileChallengeEnabled(int userId, boolean enabled, String managedUserPassword);
+    boolean getSeparateProfileChallengeEnabled(int userId);
     void registerStrongAuthTracker(in IStrongAuthTracker tracker);
     void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
     void requireStrongAuth(int strongAuthReason, int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 4880664..713f56f 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -145,6 +145,43 @@
     }
 
     /**
+     * Verify a password asynchronously.
+     *
+     * @param utils The LockPatternUtils instance to use.
+     * @param password The password to check.
+     * @param challenge The challenge to verify against the pattern.
+     * @param userId The user to check against the pattern.
+     * @param callback The callback to be invoked with the verification result.
+     */
+    public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
+            final String password,
+            final boolean isPattern,
+            final long challenge,
+            final int userId,
+            final OnVerifyCallback callback) {
+        AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
+            private int mThrottleTimeout;
+
+            @Override
+            protected byte[] doInBackground(Void... args) {
+                try {
+                    return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId);
+                } catch (RequestThrottledException ex) {
+                    mThrottleTimeout = ex.getTimeoutMs();
+                    return null;
+                }
+            }
+
+            @Override
+            protected void onPostExecute(byte[] result) {
+                callback.onVerified(result, mThrottleTimeout);
+            }
+        };
+        task.execute();
+        return task;
+    }
+
+    /**
      * Checks a password asynchronously.
      *
      * @param utils The LockPatternUtils instance to use.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3d892af..bceeaca 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -137,8 +137,6 @@
     private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
     private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
 
-    private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
-
     // Maximum allowed number of repeated or ordered characters in a sequence before we'll
     // consider it a complex PIN/password.
     public static final int MAX_ALLOWED_SEQUENCE = 3;
@@ -377,6 +375,36 @@
         }
     }
 
+
+    /**
+     * Check to see if a password matches the saved password.
+     * If password matches, return an opaque attestation that the challenge
+     * was verified.
+     *
+     * @param password The password to check.
+     * @param challenge The challenge to verify against the password
+     * @return the attestation that the challenge was verified, or null.
+     */
+    public byte[] verifyTiedProfileChallenge(String password, boolean isPattern, long challenge,
+            int userId) throws RequestThrottledException {
+        throwIfCalledOnMainThread();
+        try {
+            VerifyCredentialResponse response =
+                    getLockSettings().verifyTiedProfileChallenge(password, isPattern, challenge,
+                            userId);
+
+            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+                return response.getPayload();
+            } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
+                throw new RequestThrottledException(response.getTimeout());
+            } else {
+                return null;
+            }
+        } catch (RemoteException re) {
+            return null;
+        }
+    }
+
     /**
      * Check to see if a password matches the saved password.  If no password exists,
      * always returns true.
@@ -785,6 +813,7 @@
             }
 
             getLockSettings().setLockPassword(password, savedPassword, userHandle);
+            getLockSettings().setSeparateProfileChallengeEnabled(userHandle, true, null);
             int computedQuality = computePasswordQuality(password);
 
             // Update the device encryption password.
@@ -919,11 +948,23 @@
     /**
      * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
      * for user handles that do not belong to a managed profile.
+     *
+     * @param userHandle Managed profile user id
+     * @param enabled True if separate challenge is enabled
+     * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
+     *            true
      */
-    public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled) {
+    public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
+            String managedUserPassword) {
         UserInfo info = getUserManager().getUserInfo(userHandle);
         if (info.isManagedProfile()) {
-            setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userHandle);
+            try {
+                getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
+                        managedUserPassword);
+                onAfterChangingPassword(userHandle);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Couldn't update work profile challenge enabled");
+            }
         }
     }
 
@@ -935,7 +976,13 @@
         if (info == null || !info.isManagedProfile()) {
             return false;
         }
-        return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userHandle);
+        try {
+            return getLockSettings().getSeparateProfileChallengeEnabled(userHandle);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't get separate profile challenge enabled");
+            // Default value is false
+            return false;
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
index c4347f8..25b487e 100644
--- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java
+++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java
@@ -17,9 +17,14 @@
 
 package com.android.internal.widget;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -85,6 +90,10 @@
     private final float mMinFlingVelocity;
     private final OverScroller mScroller;
     private final VelocityTracker mVelocityTracker;
+    private final Drawable mScrollIndicatorDrawable;
+    private final Drawable mFakeForeground;
+
+    private View mButtonBar;
 
     private OnDismissedListener mOnDismissedListener;
     private RunOnDismissedListener mRunOnDismissedListener;
@@ -106,6 +115,8 @@
                 }
             };
 
+    private final int[] mTempOffset = new int[2];
+
     public ResolverDrawerLayout(Context context) {
         this(context, null);
     }
@@ -127,6 +138,9 @@
                 mMaxCollapsedHeight);
         a.recycle();
 
+        mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
+        mFakeForeground = new ColorDrawable(Color.TRANSPARENT);
+
         mScroller = new OverScroller(context, AnimationUtils.loadInterpolator(context,
                 android.R.interpolator.decelerate_quint));
         mVelocityTracker = VelocityTracker.obtain();
@@ -138,6 +152,13 @@
         setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
     }
 
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mButtonBar = findViewById(R.id.button_bar);
+    }
+
     public void setSmallCollapsed(boolean smallCollapsed) {
         mSmallCollapsed = smallCollapsed;
         requestLayout();
@@ -202,8 +223,7 @@
             }
             final boolean isCollapsedNew = mCollapseOffset != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                onCollapsedChanged(isCollapsedNew);
             }
         } else {
             // Start out collapsed at first unless we restored state for otherwise
@@ -442,8 +462,7 @@
             mTopOffset += dy;
             final boolean isCollapsedNew = newPos != 0;
             if (isCollapsedOld != isCollapsedNew) {
-                notifyViewAccessibilityStateChangedIfNeeded(
-                        AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+                onCollapsedChanged(isCollapsedNew);
             }
             postInvalidateOnAnimation();
             return dy;
@@ -451,6 +470,14 @@
         return 0;
     }
 
+    private void onCollapsedChanged(boolean isCollapsed) {
+        notifyViewAccessibilityStateChangedIfNeeded(
+                AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
+
+        // Set a fake foreground so that we receive onDrawForeground().
+        setForeground(isCollapsed ? mFakeForeground : null);
+    }
+
     void dispatchOnDismissed() {
         if (mOnDismissedListener != null) {
             mOnDismissedListener.onDismissed();
@@ -709,6 +736,23 @@
     }
 
     @Override
+    public void onDrawForeground(Canvas canvas) {
+        if (isCollapsed() && mButtonBar != null) {
+            // Draw the scroll indicator directly above the button bar.
+            final int height = mScrollIndicatorDrawable.getIntrinsicHeight();
+            mButtonBar.getLocationInWindow(mTempOffset);
+            final int barTop = mTempOffset[1];
+            getLocationInWindow(mTempOffset);
+            final int myTop = mTempOffset[1];
+            final int top = (barTop - myTop) - height;
+            mScrollIndicatorDrawable.setBounds(0, top, getWidth(), top + height);
+            mScrollIndicatorDrawable.draw(canvas);
+        }
+
+        super.onDrawForeground(canvas);
+    }
+
+    @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec);
         int widthSize = sourceWidth;
diff --git a/core/jni/android/graphics/FontFamily.cpp b/core/jni/android/graphics/FontFamily.cpp
index 6dc251c..1c2d13d 100644
--- a/core/jni/android/graphics/FontFamily.cpp
+++ b/core/jni/android/graphics/FontFamily.cpp
@@ -53,36 +53,14 @@
     fontFamily->Unref();
 }
 
-static jboolean addSkTypeface(FontFamily* family, SkTypeface* face) {
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+static jboolean addSkTypeface(FontFamily* family, SkTypeface* face, const void* fontData,
+        size_t fontSize, int ttcIndex) {
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontData, fontSize, ttcIndex);
     bool result = family->addFont(minikinFont);
     minikinFont->Unref();
     return result;
 }
 
-static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jstring path,
-        jint ttcIndex) {
-    NPE_CHECK_RETURN_ZERO(env, path);
-    ScopedUtfChars str(env, path);
-    SkTypeface* face = SkTypeface::CreateFromFile(str.c_str(), ttcIndex);
-    if (face == NULL) {
-        ALOGE("addFont failed to create font %s", str.c_str());
-        return false;
-    }
-    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
-}
-
-static struct {
-    jmethodID mGet;
-    jmethodID mSize;
-} gListClassInfo;
-
-static struct {
-    jfieldID mTag;
-    jfieldID mStyleValue;
-} gAxisClassInfo;
-
 static void release_global_ref(const void* /*data*/, void* context) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     bool needToAttach = (env == NULL);
@@ -106,6 +84,47 @@
     }
 }
 
+static jboolean FontFamily_addFont(JNIEnv* env, jobject clazz, jlong familyPtr, jobject bytebuf,
+        jint ttcIndex) {
+    NPE_CHECK_RETURN_ZERO(env, bytebuf);
+    const void* fontPtr = env->GetDirectBufferAddress(bytebuf);
+    if (fontPtr == NULL) {
+        ALOGE("addFont failed to create font, buffer invalid");
+        return false;
+    }
+    jlong fontSize = env->GetDirectBufferCapacity(bytebuf);
+    if (fontSize < 0) {
+        ALOGE("addFont failed to create font, buffer size invalid");
+        return false;
+    }
+    jobject fontRef = MakeGlobalRefOrDie(env, bytebuf);
+    SkAutoTUnref<SkData> data(SkData::NewWithProc(fontPtr, fontSize,
+            release_global_ref, reinterpret_cast<void*>(fontRef)));
+    std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(data));
+
+    SkFontMgr::FontParameters params;
+    params.setCollectionIndex(ttcIndex);
+
+    SkAutoTUnref<SkFontMgr> fm(SkFontMgr::RefDefault());
+    SkTypeface* face = fm->createFromStream(fontData.release(), params);
+    if (face == NULL) {
+        ALOGE("addFont failed to create font");
+        return false;
+    }
+    FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
+    return addSkTypeface(fontFamily, face, fontPtr, (size_t)fontSize, ttcIndex);
+}
+
+static struct {
+    jmethodID mGet;
+    jmethodID mSize;
+} gListClassInfo;
+
+static struct {
+    jfieldID mTag;
+    jfieldID mStyleValue;
+} gAxisClassInfo;
+
 static jboolean FontFamily_addFontWeightStyle(JNIEnv* env, jobject clazz, jlong familyPtr,
         jobject font, jint ttcIndex, jobject listOfAxis, jint weight, jboolean isItalic) {
     NPE_CHECK_RETURN_ZERO(env, font);
@@ -133,7 +152,7 @@
         }
     }
 
-    void* fontPtr = env->GetDirectBufferAddress(font);
+    const void* fontPtr = env->GetDirectBufferAddress(font);
     if (fontPtr == NULL) {
         ALOGE("addFont failed to create font, buffer invalid");
         return false;
@@ -159,7 +178,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    MinikinFont* minikinFont = new MinikinFontSkia(face);
+    MinikinFont* minikinFont = new MinikinFontSkia(face, fontPtr, (size_t)fontSize, ttcIndex);
     fontFamily->addFont(minikinFont, FontStyle(weight / 100, isItalic));
     minikinFont->Unref();
     return true;
@@ -191,6 +210,7 @@
         return false;
     }
 
+    size_t bufSize = asset->getLength();
     SkAutoTUnref<SkData> data(SkData::NewWithProc(buf, asset->getLength(), releaseAsset, asset));
     SkMemoryStream* stream = new SkMemoryStream(data);
     // CreateFromStream takes ownership of stream.
@@ -200,7 +220,7 @@
         return false;
     }
     FontFamily* fontFamily = reinterpret_cast<FontFamily*>(familyPtr);
-    return addSkTypeface(fontFamily, face);
+    return addSkTypeface(fontFamily, face, buf, bufSize, /* ttcIndex */ 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -208,7 +228,7 @@
 static const JNINativeMethod gFontFamilyMethods[] = {
     { "nCreateFamily",         "(Ljava/lang/String;I)J", (void*)FontFamily_create },
     { "nUnrefFamily",          "(J)V", (void*)FontFamily_unref },
-    { "nAddFont",              "(JLjava/lang/String;I)Z", (void*)FontFamily_addFont },
+    { "nAddFont",              "(JLjava/nio/ByteBuffer;I)Z", (void*)FontFamily_addFont },
     { "nAddFontWeightStyle",   "(JLjava/nio/ByteBuffer;ILjava/util/List;IZ)Z",
             (void*)FontFamily_addFontWeightStyle },
     { "nAddFontFromAsset",     "(JLandroid/content/res/AssetManager;Ljava/lang/String;)Z",
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 1844a98..f46f45c 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -768,6 +768,22 @@
         return false;
     }
 
+    // Returns true if the given string is exact one pair of regional indicators.
+    static bool isFlag(const jchar* str, size_t length) {
+        const jchar RI_LEAD_SURROGATE = 0xD83C;
+        const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+        const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+        if (length != 4) {
+            return false;
+        }
+        if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+            return false;
+        }
+        return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+            RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+    }
+
     static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
             jint bidiFlags, jstring string) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -817,7 +833,26 @@
             // in joining scripts, such as Arabic and Mongolian.
             return false;
         }
-        return nGlyphs > 0 && !layoutContainsNotdef(layout);
+
+        if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
+            return false;  // The collection doesn't have a glyph.
+        }
+
+        if (nChars == 2 && isFlag(str.get(), str.size())) {
+            // Some font may have a special glyph for unsupported regional indicator pairs.
+            // To return false for this case, need to compare the glyph id with the one of ZZ
+            // since ZZ is reserved for unknown or invalid territory.
+            // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
+            static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
+            Layout zzLayout;
+            MinikinUtils::doLayout(&zzLayout, paint, bidiFlags, typeface, ZZ_FLAG_STR, 0, 4, 4);
+            if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
+                // The font collection doesn't have a glyph for unknown flag. Just return true.
+                return true;
+            }
+            return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
+        }
+        return true;
     }
 
     static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
diff --git a/core/jni/android_graphics_drawable_VectorDrawable.cpp b/core/jni/android_graphics_drawable_VectorDrawable.cpp
index b04293e..e5c4a2d 100644
--- a/core/jni/android_graphics_drawable_VectorDrawable.cpp
+++ b/core/jni/android_graphics_drawable_VectorDrawable.cpp
@@ -176,6 +176,9 @@
     PathParser::ParseResult result;
     PathData data;
     PathParser::getPathDataFromString(&data, &result, pathString, stringLength);
+    if (result.failureOccurred) {
+        doThrowIAE(env, result.failureMessage.c_str());
+    }
     path->mutateStagingProperties()->setData(data);
     env->ReleaseStringUTFChars(inputStr, pathString);
 }
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 80ae550..91a3b4f 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -382,6 +382,9 @@
                       char *msg, int msgLen) {
     int retVal;
 
+    //ALOGD("Rcd OS message from hubHandle %" PRIu32 " type %" PRIu32 " length %d",
+    //      hubHandle, msgType, msgLen);
+
     switch(msgType) {
         case CONTEXT_HUB_APPS_ENABLE:
             retVal = 0;
@@ -633,7 +636,7 @@
 
 
     if (numHeaderElements >= MSG_HEADER_SIZE) {
-        int setAddressSuccess;
+        bool setAddressSuccess;
         int hubId;
         hub_message_t msg;
 
@@ -654,7 +657,7 @@
           ALOGD("Could not find app instance %d on hubHandle %d, setAddress %d",
                 header[HEADER_FIELD_APP_INSTANCE],
                 header[HEADER_FIELD_HUB_HANDLE],
-                setAddressSuccess);
+                (int)setAddressSuccess);
         }
     } else {
         ALOGD("Malformed header len");
diff --git a/core/jni/android_util_PathParser.cpp b/core/jni/android_util_PathParser.cpp
index 0927120..0c867f1 100644
--- a/core/jni/android_util_PathParser.cpp
+++ b/core/jni/android_util_PathParser.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "jni.h"
+#include "GraphicsJNI.h"
 
 #include <PathParser.h>
 #include <SkPath.h>
@@ -27,7 +28,7 @@
 
 using namespace uirenderer;
 
-static bool parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
+static void parseStringForPath(JNIEnv* env, jobject, jlong skPathHandle, jstring inputPathStr,
         jint strLength) {
     const char* pathString = env->GetStringUTFChars(inputPathStr, NULL);
     SkPath* skPath = reinterpret_cast<SkPath*>(skPathHandle);
@@ -36,9 +37,8 @@
     PathParser::parseStringForSkPath(skPath, &result, pathString, strLength);
     env->ReleaseStringUTFChars(inputPathStr, pathString);
     if (result.failureOccurred) {
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
     }
-    return !result.failureOccurred;
 }
 
 static long createEmptyPathData(JNIEnv*, jobject) {
@@ -62,7 +62,7 @@
         return reinterpret_cast<jlong>(pathData);
     } else {
         delete pathData;
-        ALOGE(result.failureMessage.c_str());
+        doThrowIAE(env, result.failureMessage.c_str());
         return NULL;
     }
 }
@@ -100,7 +100,7 @@
 }
 
 static const JNINativeMethod gMethods[] = {
-    {"nParseStringForPath", "(JLjava/lang/String;I)Z", (void*)parseStringForPath},
+    {"nParseStringForPath", "(JLjava/lang/String;I)V", (void*)parseStringForPath},
     {"nCreateEmptyPathData", "!()J", (void*)createEmptyPathData},
     {"nCreatePathData", "!(J)J", (void*)createPathData},
     {"nCreatePathDataFromString", "(Ljava/lang/String;I)J", (void*)createPathDataFromStringPath},
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 27e2ee8..4459f32 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -128,9 +128,22 @@
 
 static void android_view_RenderNode_setDisplayList(JNIEnv* env,
         jobject clazz, jlong renderNodePtr, jlong displayListPtr) {
+    class RemovedObserver : public TreeObserver {
+    public:
+        virtual void onMaybeRemovedFromTree(RenderNode* node) override {
+            maybeRemovedNodes.insert(sp<RenderNode>(node));
+        }
+        std::set< sp<RenderNode> > maybeRemovedNodes;
+    };
+
     RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     DisplayList* newData = reinterpret_cast<DisplayList*>(displayListPtr);
-    renderNode->setStagingDisplayList(newData);
+    RemovedObserver observer;
+    renderNode->setStagingDisplayList(newData, &observer);
+    for (auto& node : observer.maybeRemovedNodes) {
+        if (node->hasParents()) continue;
+        onRenderNodeRemoved(env, node.get());
+    }
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 14252dc..21e4d2f 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -525,7 +525,7 @@
     UiFrameInfoBuilder(proxy->frameInfo())
             .setVsync(vsync, vsync)
             .addFlag(FrameInfoFlags::SurfaceCanvas);
-    proxy->syncAndDrawFrame();
+    proxy->syncAndDrawFrame(nullptr);
 }
 
 static void destroy(JNIEnv* env, jclass clazz, jlong rendererPtr) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 8019326..ef45c87 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -72,6 +72,31 @@
     return env;
 }
 
+// TODO: Clean this up, it's a bit odd to need to call over to
+// rendernode's jni layer. Probably means RootRenderNode should be pulled
+// into HWUI with appropriate callbacks for the various JNI hooks so
+// that RenderNode's JNI layer can handle its own thing
+void onRenderNodeRemoved(JNIEnv* env, RenderNode* node);
+
+class ScopedRemovedRenderNodeObserver : public TreeObserver {
+public:
+    ScopedRemovedRenderNodeObserver(JNIEnv* env) : mEnv(env) {}
+    ~ScopedRemovedRenderNodeObserver() {
+        for (auto& node : mMaybeRemovedNodes) {
+            if (node->hasParents()) continue;
+            onRenderNodeRemoved(mEnv, node.get());
+        }
+    }
+
+    virtual void onMaybeRemovedFromTree(RenderNode* node) override {
+        mMaybeRemovedNodes.insert(sp<RenderNode>(node));
+    }
+
+private:
+    JNIEnv* mEnv;
+    std::set< sp<RenderNode> > mMaybeRemovedNodes;
+};
+
 class OnFinishedEvent {
 public:
     OnFinishedEvent(BaseRenderNodeAnimator* animator, AnimationListener* listener)
@@ -120,13 +145,7 @@
     std::string mMessage;
 };
 
-// TODO: Clean this up, it's a bit odd to need to call over to
-// rendernode's jni layer. Probably means RootRenderNode should be pulled
-// into HWUI with appropriate callbacks for the various JNI hooks so
-// that RenderNode's JNI layer can handle its own thing
-void onRenderNodeRemoved(JNIEnv* env, RenderNode* node);
-
-class RootRenderNode : public RenderNode, ErrorHandler, TreeObserver {
+class RootRenderNode : public RenderNode, ErrorHandler {
 public:
     RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
@@ -143,9 +162,6 @@
 
     virtual void prepareTree(TreeInfo& info) override {
         info.errorHandler = this;
-        if (info.mode == TreeInfo::MODE_FULL) {
-            info.observer = this;
-        }
         // TODO: This is hacky
         info.windowInsetLeft = -stagingProperties().getLeft();
         info.windowInsetTop = -stagingProperties().getTop();
@@ -155,7 +171,6 @@
         info.windowInsetLeft = 0;
         info.windowInsetTop = 0;
         info.errorHandler = nullptr;
-        info.observer = nullptr;
     }
 
     void sendMessage(const sp<MessageHandler>& handler) {
@@ -181,27 +196,10 @@
         mPendingAnimatingRenderNodes.clear();
     }
 
-    virtual void onMaybeRemovedFromTree(RenderNode* node) override {
-        mMaybeRemovedNodes.insert(sp<RenderNode>(node));
-    }
-
-    void processMaybeRemovedNodes(JNIEnv* env) {
-        // We can safely access mMaybeRemovedNodes here because
-        // we will only modify it in prepareTree calls that are
-        // MODE_FULL
-
-        for (auto& node : mMaybeRemovedNodes) {
-            if (node->hasParents()) continue;
-            onRenderNodeRemoved(env, node.get());
-        }
-        mMaybeRemovedNodes.clear();
-    }
-
 private:
     sp<Looper> mLooper;
     JavaVM* mVm;
     std::vector< sp<RenderNode> > mPendingAnimatingRenderNodes;
-    std::set< sp<RenderNode> > mMaybeRemovedNodes;
 };
 
 class AnimationContextBridge : public AnimationContext {
@@ -481,6 +479,12 @@
     return proxy->pauseSurface(surface);
 }
 
+static void android_view_ThreadedRenderer_setStopped(JNIEnv* env, jobject clazz,
+        jlong proxyPtr, jboolean stopped) {
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setStopped(stopped);
+}
+
 static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz, jlong proxyPtr,
         jint width, jint height, jfloat lightRadius, jint ambientShadowAlpha, jint spotShadowAlpha) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -500,24 +504,23 @@
 }
 
 static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize, jlong rootNodePtr) {
+        jlong proxyPtr, jlongArray frameInfo, jint frameInfoSize) {
     LOG_ALWAYS_FATAL_IF(frameInfoSize != UI_THREAD_FRAME_INFO_SIZE,
             "Mismatched size expectations, given %d expected %d",
             frameInfoSize, UI_THREAD_FRAME_INFO_SIZE);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
+    ScopedRemovedRenderNodeObserver observer(env);
     env->GetLongArrayRegion(frameInfo, 0, frameInfoSize, proxy->frameInfo());
-    int ret = proxy->syncAndDrawFrame();
-    rootRenderNode->processMaybeRemovedNodes(env);
-    return ret;
+    return proxy->syncAndDrawFrame(&observer);
 }
 
 static void android_view_ThreadedRenderer_destroy(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong rootNodePtr) {
+    ScopedRemovedRenderNodeObserver observer(env);
     RootRenderNode* rootRenderNode = reinterpret_cast<RootRenderNode*>(rootNodePtr);
     rootRenderNode->destroy();
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->destroy();
+    proxy->destroy(&observer);
 }
 
 static void android_view_ThreadedRenderer_registerAnimatingRenderNode(JNIEnv* env, jobject clazz,
@@ -542,9 +545,10 @@
 
 static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jlong nodePtr) {
+    ScopedRemovedRenderNodeObserver observer(env);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
     RenderNode* node = reinterpret_cast<RenderNode*>(nodePtr);
-    proxy->buildLayer(node);
+    proxy->buildLayer(node, &observer);
 }
 
 static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz,
@@ -579,8 +583,9 @@
 
 static void android_view_ThreadedRenderer_destroyHardwareResources(JNIEnv* env, jobject clazz,
         jlong proxyPtr) {
+    ScopedRemovedRenderNodeObserver observer(env);
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->destroyHardwareResources();
+    proxy->destroyHardwareResources(&observer);
 }
 
 static void android_view_ThreadedRenderer_trimMemory(JNIEnv* env, jobject clazz,
@@ -733,10 +738,11 @@
     { "nInitialize", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_initialize },
     { "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
     { "nPauseSurface", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_pauseSurface },
+    { "nSetStopped", "(JZ)V", (void*) android_view_ThreadedRenderer_setStopped },
     { "nSetup", "(JIIFII)V", (void*) android_view_ThreadedRenderer_setup },
     { "nSetLightCenter", "(JFFF)V", (void*) android_view_ThreadedRenderer_setLightCenter },
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
-    { "nSyncAndDrawFrame", "(J[JIJ)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
+    { "nSyncAndDrawFrame", "(J[JI)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroy", "(JJ)V", (void*) android_view_ThreadedRenderer_destroy },
     { "nRegisterAnimatingRenderNode", "(JJ)V", (void*) android_view_ThreadedRenderer_registerAnimatingRenderNode },
     { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9219e81..9a2e39c7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -401,7 +401,6 @@
     <protected-broadcast android:name="android.telephony.action.CARRIER_CONFIG_CHANGED" />
 
     <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
-    <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
     <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
     <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_UPDATED" />
     <protected-broadcast android:name="com.android.server.NetworkTimeUpdateService.action.POLL" />
@@ -461,8 +460,15 @@
     <protected-broadcast android:name="android.net.wifi.PASSPOINT_ICON_RECEIVED" />
     <protected-broadcast android:name="com.android.server.notification.CountdownConditionProvider" />
 
-    <!-- @hide UCE service Notification -->
+    <protected-broadcast android:name="com.android.ims.IMS_SERVICE_UP" />
+    <protected-broadcast android:name="com.android.ims.IMS_INCOMING_CALL" />
     <protected-broadcast android:name="com.android.ims.internal.uce.UCE_SERVICE_UP" />
+    <protected-broadcast android:name="com.android.intent.action.IMS_FEATURE_CHANGED" />
+    <protected-broadcast android:name="com.android.intent.action.IMS_CONFIG_CHANGED" />
+
+    <protected-broadcast android:name="com.android.internal.location.ALARM_WAKEUP" />
+    <protected-broadcast android:name="com.android.internal.location.ALARM_TIMEOUT" />
+    <protected-broadcast android:name="android.intent.action.GLOBAL_BUTTON" />
 
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
@@ -2991,6 +2997,11 @@
     <permission android:name="android.permission.BIND_VR_LISTENER_SERVICE"
         android:protectionLevel="signature" />
 
+    <!-- Required to make calls to {@link android.service.vr.IVrManager}.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_VR_MANAGER"
+            android:protectionLevel="signature" />
+
     <!-- Allows an application to whitelist tasks during lock task mode
          @hide <p>Not for use by third-party applications.</p> -->
     <permission android:name="android.permission.UPDATE_LOCK_TASK_PACKAGES"
diff --git a/core/res/res/anim/slide_in_enter_micro.xml b/core/res/res/anim/slide_in_enter_micro.xml
index 14a5290..c70874c 100644
--- a/core/res/res/anim/slide_in_enter_micro.xml
+++ b/core/res/res/anim/slide_in_enter_micro.xml
@@ -19,7 +19,7 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
      android:zAdjustment="top">
-    <translate android:fromYDelta="5%p" android:toXDelta="0"
+    <translate android:fromYDelta="5%p" android:toYDelta="0"
                android:duration="417"
                android:interpolator="@android:interpolator/launch_task_micro_ydelta" />
     <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
diff --git a/core/res/res/drawable/ic_corp_user_badge.xml b/core/res/res/drawable/ic_corp_user_badge.xml
new file mode 100644
index 0000000..23809d5
--- /dev/null
+++ b/core/res/res/drawable/ic_corp_user_badge.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2016 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="36.0"
+        android:viewportHeight="36.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,0C8.06,-0 0,8.06 0,18C0,27.94 8.06,36 18,36C27.94,36 36,27.94 36,18C36,8.06 27.94,0 18,0zM15.5,10.5L20.5,10.5L21.75,11.75L21.75,13L24.66,13C25.57,13 26.34,13.74 26.34,14.66L26.34,18C26.34,18.92 25.57,19.66 24.66,19.66L19.66,19.66L19.66,18.41L16.34,18.41L16.34,19.66L11.34,19.66C10.43,19.66 9.66,18.92 9.66,18L9.66,14.66C9.66,13.74 10.43,13 11.34,13L14.25,13L14.25,11.78L15.5,10.5zM15.5,11.75L15.5,13L20.5,13L20.5,11.75L15.5,11.75zM10.5,20.5L16.34,20.5L16.34,21.75L19.66,21.75L19.66,20.5L25.5,20.5L25.5,23.84C25.5,24.76 24.76,25.5 23.84,25.5L12.16,25.5C11.24,25.5 10.5,24.76 10.5,23.84L10.5,20.5z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_done.xml b/core/res/res/drawable/ic_input_extract_action_done.xml
new file mode 100644
index 0000000..a0ebf92
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_done.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M18,32.34L9.66,24l-2.83,2.83L18,38l24,-24 -2.83,-2.83z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_go.xml b/core/res/res/drawable/ic_input_extract_action_go.xml
new file mode 100644
index 0000000..c24f5a0
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_go.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M6,22h28.34l-7.17,-7.17L30,12l12,12 -12,12 -2.83,-2.83L34.34,26H6z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_next.xml b/core/res/res/drawable/ic_input_extract_action_next.xml
new file mode 100644
index 0000000..fa0b178
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_next.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M23.17,14.83L30.34,22H2v4h28.34l-7.17,7.17L26,36l12,-12 -12,-12 -2.83,2.83zM40,12v24h4V12h-4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_previous.xml b/core/res/res/drawable/ic_input_extract_action_previous.xml
new file mode 100644
index 0000000..5e1823c
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_previous.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M22.83,14.83L15.66,22H44v4H15.66l7.17,7.17L20,36 8,24l12,-12 2.83,2.83zM6,12v24H2V12h4z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_return.xml b/core/res/res/drawable/ic_input_extract_action_return.xml
new file mode 100644
index 0000000..c46a4a2
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_return.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M38,14v8H11.66l7.17,-7.17L16,12 4,24l12,12 2.83,-2.83L11.66,26H42V14z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_search.xml b/core/res/res/drawable/ic_input_extract_action_search.xml
new file mode 100644
index 0000000..fd1dcea
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_search.xml
@@ -0,0 +1,4 @@
+<vector android:height="24dp" android:viewportHeight="48.0"
+    android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+    <path android:fillColor="#FFFFFF" android:pathData="M31,28h-1.59l-0.55,-0.55C30.82,25.18 32,22.23 32,19c0,-7.18 -5.82,-13 -13,-13S6,11.82 6,19s5.82,13 13,13c3.23,0 6.18,-1.18 8.45,-3.13l0.55,0.55L28,31l10,9.98L40.98,38 31,28zM19,28c-4.97,0 -9,-4.03 -9,-9s4.03,-9 9,-9 9,4.03 9,9 -4.03,9 -9,9z"/>
+</vector>
diff --git a/core/res/res/drawable/ic_input_extract_action_send.xml b/core/res/res/drawable/ic_input_extract_action_send.xml
new file mode 100644
index 0000000..0f3754b
--- /dev/null
+++ b/core/res/res/drawable/ic_input_extract_action_send.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="36dp"
+        android:height="36dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:pathData="M4.02,42L46,24 4.02,6 4,20l30,4 -30,4z"
+        android:fillColor="#FFFFFF"/>
+</vector>
diff --git a/core/res/res/drawable/input_extract_action_bg_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
new file mode 100644
index 0000000..2457bb9
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_material_dark.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/input_extract_action_bg_pressed_material_dark"
+        android:state_pressed="true"/>
+    <item android:drawable="@drawable/input_extract_action_bg_normal_material_dark"/>
+</selector>
diff --git a/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
new file mode 100644
index 0000000..9e36253
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_normal_material_dark.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_200"/>
+</shape>
diff --git a/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
new file mode 100644
index 0000000..2328ce3
--- /dev/null
+++ b/core/res/res/drawable/input_extract_action_bg_pressed_material_dark.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
+    <solid android:color="@color/material_deep_teal_100"/>
+</shape>
diff --git a/core/res/res/layout-watch/input_method_extract_view.xml b/core/res/res/layout-watch/input_method_extract_view.xml
new file mode 100644
index 0000000..cd921f1
--- /dev/null
+++ b/core/res/res/layout-watch/input_method_extract_view.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<android.inputmethodservice.CompactExtractEditLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:gravity="center_vertical"
+    android:baselineAligned="false">
+
+    <android.inputmethodservice.ExtractEditText
+        android:id="@id/inputExtractEditText"
+        android:layout_width="0dp"
+        android:layout_height="24dp"
+        android:background="@null"
+        android:singleLine="true"
+        android:inputType="text"
+        android:layout_weight="1"
+        android:fontFamily="sans-serif-condensed-light"
+        android:textColor="@color/primary_text_default_material_dark"
+        android:textColorHighlight="@color/accent_material_dark"
+        android:textSize="18dp"
+        android:cursorVisible="false"
+        android:gravity="bottom|right"
+        />
+
+    <FrameLayout
+        android:id="@id/inputExtractAccessories"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="8dp"
+        android:visibility="visible">
+        <ImageButton
+            android:id="@id/inputExtractAction"
+            android:layout_width="@dimen/input_extract_action_button_width"
+            android:layout_height="@dimen/input_extract_action_button_width"
+            android:background="@drawable/input_extract_action_bg_material_dark"
+            android:padding="4dp"
+            android:scaleType="centerInside" />
+    </FrameLayout>
+</android.inputmethodservice.CompactExtractEditLayout>
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 0bbaa24..992e88e 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -42,7 +42,7 @@
         android:singleLine="true"
         />
     <TextView
-        android:id="@+id/sub_text_divider"
+        android:id="@+id/header_text_divider"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Material.Notification.Info"
@@ -51,26 +51,7 @@
         android:text="@string/notification_header_divider_symbol"
         android:visibility="gone"/>
     <TextView
-        android:id="@+id/header_sub_text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
-        android:layout_marginStart="2dp"
-        android:layout_marginEnd="2dp"
-        android:visibility="gone"
-        android:singleLine="true"/>
-    <TextView
-        android:id="@+id/content_info_divider"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Material.Notification.Info"
-        android:layout_marginStart="2dp"
-        android:layout_marginEnd="2dp"
-        android:text="@string/notification_header_divider_symbol"
-        android:singleLine="true"
-        android:visibility="gone"/>
-    <TextView
-        android:id="@+id/header_content_info"
+        android:id="@+id/header_text"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:textAppearance="@style/TextAppearance.Material.Notification.Info"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 4b8640c..fe43e1c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -30,33 +30,37 @@
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
         android:elevation="8dp"
-        android:background="@color/white" >
-        <TextView android:id="@+id/profile_button"
-                  android:layout_width="wrap_content"
-                  android:layout_height="48dp"
-                  android:layout_marginEnd="8dp"
-                  android:paddingStart="8dp"
-                  android:paddingEnd="8dp"
-                  android:visibility="gone"
-                  style="?attr/borderlessButtonStyle"
-                  android:textAppearance="?attr/textAppearanceButton"
-                  android:textColor="@color/material_deep_teal_500"
-                  android:gravity="center_vertical"
-                  android:layout_alignParentTop="true"
-                  android:layout_alignParentRight="true"
-                  android:singleLine="true"/>
-        <TextView android:id="@+id/title"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:minHeight="56dp"
-                  android:textAppearance="?attr/textAppearanceMedium"
-                  android:gravity="start|center_vertical"
-                  android:paddingStart="?attr/dialogPreferredPadding"
-                  android:paddingEnd="?attr/dialogPreferredPadding"
-                  android:paddingTop="8dp"
-                  android:layout_below="@id/profile_button"
-                  android:layout_alignParentLeft="true"
-                  android:paddingBottom="8dp" />
+        android:background="@color/white">
+
+        <TextView
+            android:id="@+id/profile_button"
+            android:layout_width="wrap_content"
+            android:layout_height="48dp"
+            android:layout_marginEnd="8dp"
+            android:paddingStart="8dp"
+            android:paddingEnd="8dp"
+            android:visibility="gone"
+            style="?attr/borderlessButtonStyle"
+            android:textAppearance="?attr/textAppearanceButton"
+            android:textColor="@color/material_deep_teal_500"
+            android:gravity="center_vertical"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentRight="true"
+            android:singleLine="true" />
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:minHeight="56dp"
+            android:textAppearance="?attr/textAppearanceMedium"
+            android:gravity="start|center_vertical"
+            android:paddingStart="?attr/dialogPreferredPadding"
+            android:paddingEnd="?attr/dialogPreferredPadding"
+            android:paddingTop="8dp"
+            android:layout_below="@id/profile_button"
+            android:layout_alignParentLeft="true"
+            android:paddingBottom="8dp" />
     </RelativeLayout>
 
     <ListView
@@ -68,21 +72,23 @@
         android:background="@color/white"
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
+        android:scrollIndicators="top|bottom"
         android:divider="@null" />
 
-    <TextView android:id="@+id/empty"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:layout_alwaysShow="true"
-              android:text="@string/noApplications"
-              android:padding="32dp"
-              android:gravity="center"
-              android:visibility="gone" />
+    <TextView
+        android:id="@+id/empty"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_alwaysShow="true"
+        android:text="@string/noApplications"
+        android:padding="32dp"
+        android:gravity="center"
+        android:visibility="gone" />
 
     <LinearLayout
         android:id="@+id/button_bar"
         android:visibility="gone"
-        style="?android:attr/buttonBarStyle"
+        style="?attr/buttonBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_ignoreOffset="true"
@@ -97,26 +103,30 @@
         android:paddingStart="12dp"
         android:paddingEnd="12dp"
         android:elevation="8dp">
-        <Button android:id="@+id/button_once"
-                android:layout_width="wrap_content"
-                android:layout_gravity="start"
-                android:maxLines="2"
-                style="?android:attr/buttonBarNegativeButtonStyle"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_once"
-                android:onClick="onButtonClick" />
-        <Button android:id="@+id/button_always"
-                android:layout_width="wrap_content"
-                android:layout_gravity="end"
-                android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?android:attr/buttonBarPositiveButtonStyle"
-                android:layout_height="wrap_content"
-                android:enabled="false"
-                android:text="@string/activity_resolver_use_always"
-                android:onClick="onButtonClick" />
+
+        <Button
+            android:id="@+id/button_once"
+            android:layout_width="wrap_content"
+            android:layout_gravity="start"
+            android:maxLines="2"
+            style="?attr/buttonBarNegativeButtonStyle"
+            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/activity_resolver_use_once"
+            android:onClick="onButtonClick" />
+
+        <Button
+            android:id="@+id/button_always"
+            android:layout_width="wrap_content"
+            android:layout_gravity="end"
+            android:maxLines="2"
+            android:minHeight="@dimen/alert_dialog_button_bar_height"
+            style="?attr/buttonBarPositiveButtonStyle"
+            android:layout_height="wrap_content"
+            android:enabled="false"
+            android:text="@string/activity_resolver_use_always"
+            android:onClick="onButtonClick" />
     </LinearLayout>
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index 31361e5..ed7ef5e 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -22,8 +22,7 @@
     android:layout_height="match_parent"
     android:maxWidth="@dimen/resolver_max_width"
     android:maxCollapsedHeight="144dp"
-    android:id="@id/contentPanel"
-    >
+    android:id="@id/contentPanel">
 
     <LinearLayout
         android:layout_width="match_parent"
@@ -31,66 +30,75 @@
         android:layout_alwaysShow="true"
         android:orientation="vertical"
         android:background="@color/white"
-        android:elevation="8dp" >
+        android:elevation="8dp">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="64dp"
-            android:orientation="horizontal" >
+            android:orientation="horizontal">
 
-            <ImageView android:id="@+id/icon"
-                       android:layout_width="24dp"
-                       android:layout_height="24dp"
-                       android:layout_gravity="start|top"
-                       android:layout_marginStart="16dp"
-                       android:layout_marginEnd="16dp"
-                       android:layout_marginTop="20dp"
-                       android:scaleType="fitCenter" />
-            <TextView android:id="@+id/title"
-                      android:layout_width="0dp"
-                      android:layout_weight="1"
-                      android:layout_height="?android:attr/listPreferredItemHeight"
-                      android:layout_marginStart="16dp"
-                      android:textAppearance="?android:attr/textAppearanceMedium"
-                      android:gravity="start|center_vertical"
-                      android:paddingEnd="16dp" />
-            <LinearLayout android:id="@+id/profile_button"
-                          android:layout_width="wrap_content"
-                          android:layout_height="48dp"
-                          android:layout_marginTop="4dp"
-                          android:layout_marginEnd="4dp"
-                          android:paddingStart="8dp"
-                          android:paddingEnd="8dp"
-                          android:paddingTop="4dp"
-                          android:paddingBottom="4dp"
-                          android:focusable="true"
-                          android:visibility="gone"
-                          style="?attr/borderlessButtonStyle">
-                <ImageView android:id="@+id/icon"
-                           android:layout_width="24dp"
-                           android:layout_height="24dp"
-                           android:layout_gravity="start|center_vertical"
-                           android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                           android:layout_marginTop="12dp"
-                           android:layout_marginBottom="12dp"
-                           android:scaleType="fitCenter" />
-                <TextView android:id="@id/text1"
-                          android:layout_width="wrap_content"
-                          android:layout_height="wrap_content"
-                          android:layout_gravity="start|center_vertical"
-                          android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
-                          android:textAppearance="?attr/textAppearanceButton"
-                          android:textColor="?attr/textColorPrimary"
-                          android:minLines="1"
-                          android:maxLines="1"
-                          android:ellipsize="marquee" />
+            <ImageView
+                android:id="@+id/icon"
+                android:layout_width="24dp"
+                android:layout_height="24dp"
+                android:layout_gravity="start|top"
+                android:layout_marginStart="16dp"
+                android:layout_marginEnd="16dp"
+                android:layout_marginTop="20dp"
+                android:scaleType="fitCenter" />
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="0dp"
+                android:layout_weight="1"
+                android:layout_height="?attr/listPreferredItemHeight"
+                android:layout_marginStart="16dp"
+                android:textAppearance="?attr/textAppearanceMedium"
+                android:gravity="start|center_vertical"
+                android:paddingEnd="16dp" />
+
+            <LinearLayout
+                android:id="@+id/profile_button"
+                android:layout_width="wrap_content"
+                android:layout_height="48dp"
+                android:layout_marginTop="4dp"
+                android:layout_marginEnd="4dp"
+                android:paddingStart="8dp"
+                android:paddingEnd="8dp"
+                android:paddingTop="4dp"
+                android:paddingBottom="4dp"
+                android:focusable="true"
+                android:visibility="gone"
+                style="?attr/borderlessButtonStyle">
+
+                <ImageView
+                    android:id="@+id/icon"
+                    android:layout_width="24dp"
+                    android:layout_height="24dp"
+                    android:layout_gravity="start|center_vertical"
+                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                    android:layout_marginTop="12dp"
+                    android:layout_marginBottom="12dp"
+                    android:scaleType="fitCenter" />
+
+                <TextView
+                    android:id="@id/text1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="start|center_vertical"
+                    android:layout_marginEnd="?attr/listPreferredItemPaddingEnd"
+                    android:textAppearance="?attr/textAppearanceButton"
+                    android:textColor="?attr/textColorPrimary"
+                    android:minLines="1"
+                    android:maxLines="1"
+                    android:ellipsize="marquee" />
             </LinearLayout>
         </LinearLayout>
 
         <LinearLayout
             android:id="@+id/button_bar"
             android:visibility="gone"
-            style="?android:attr/buttonBarStyle"
+            style="?attr/buttonBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_alwaysShow="true"
@@ -104,30 +112,36 @@
             android:paddingEnd="12dp"
             android:background="@color/white"
             android:elevation="8dp">
-            <Button android:id="@+id/button_once"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="start"
-                    android:maxLines="2"
-                    style="?android:attr/buttonBarNegativeButtonStyle"
-                    android:minHeight="@dimen/alert_dialog_button_bar_height"
-                    android:layout_height="wrap_content"
-                    android:enabled="false"
-                    android:text="@string/activity_resolver_use_once"
-                    android:onClick="onButtonClick" />
-            <Button android:id="@+id/button_always"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="end"
-                    android:maxLines="2"
-                    android:minHeight="@dimen/alert_dialog_button_bar_height"
-                    style="?android:attr/buttonBarPositiveButtonStyle"
-                    android:layout_height="wrap_content"
-                    android:enabled="false"
-                    android:text="@string/activity_resolver_use_always"
-                    android:onClick="onButtonClick" />
+
+            <Button
+                android:id="@+id/button_once"
+                android:layout_width="wrap_content"
+                android:layout_gravity="start"
+                android:maxLines="2"
+                style="?attr/buttonBarNegativeButtonStyle"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_once"
+                android:onClick="onButtonClick" />
+
+            <Button
+                android:id="@+id/button_always"
+                android:layout_width="wrap_content"
+                android:layout_gravity="end"
+                android:maxLines="2"
+                android:minHeight="@dimen/alert_dialog_button_bar_height"
+                style="?attr/buttonBarPositiveButtonStyle"
+                android:layout_height="wrap_content"
+                android:enabled="false"
+                android:text="@string/activity_resolver_use_always"
+                android:onClick="onButtonClick" />
         </LinearLayout>
-        <View android:layout_width="match_parent"
-              android:layout_height="1dp"
-              android:background="?android:attr/dividerVertical" />
+
+        <View
+            android:layout_width="match_parent"
+            android:layout_height="1dp"
+            android:background="?attr/dividerVertical" />
     </LinearLayout>
 
     <ListView
@@ -140,6 +154,6 @@
         android:elevation="8dp"
         android:nestedScrollingEnabled="true"
         android:divider="@null"
-        />
+        android:scrollIndicators="top|bottom" />
 
 </com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 73a8e04..8a23529 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -238,7 +238,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Бяспечны рэжым"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Сістэма Android"</string>
     <string name="user_owner_label" msgid="1119010402169916617">"Пераключыцца на асабісты"</string>
-    <string name="managed_profile_label" msgid="5289992269827577857">"Пераключыцца на рабочы"</string>
+    <string name="managed_profile_label" msgid="5289992269827577857">"Пераключыцца на працоўны"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"Кантакты"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"атрымліваць доступ да вашых кантактаў"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"Месцазнаходжанне"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index a8e0285..ea666d9 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -1063,7 +1063,7 @@
     <string name="select_input_method" msgid="8547250819326693584">"Aldatu teklatua"</string>
     <string name="show_ime" msgid="2506087537466597099">"Erakutsi pantailan teklatu fisikoa aktibo dagoen bitartean"</string>
     <string name="hardware" msgid="194658061510127999">"Erakutsi teklatu birtuala"</string>
-    <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguratu teklatua fisikoa"</string>
+    <string name="select_keyboard_layout_notification_title" msgid="597189518763083494">"Konfiguratu teklatu fisikoa"</string>
     <string name="select_keyboard_layout_notification_message" msgid="8084622969903004900">"Hizkuntza eta diseinua hautatzeko, sakatu hau"</string>
     <string name="fast_scroll_alphabet" msgid="5433275485499039199">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
     <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
diff --git a/core/res/res/values-round-watch/dimens.xml b/core/res/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000..f4b250c
--- /dev/null
+++ b/core/res/res/values-round-watch/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">25.2%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">7.5%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">21.4%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">5.5%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">2.1%</item>
+    <item name="input_extract_action_button_width" type="dimen">32dp</item>
+    <item name="input_extract_action_button_height" type="dimen">32dp</item>
+</resources>
diff --git a/core/res/res/values-w170dp-notround-watch/dimens.xml b/core/res/res/values-w170dp-notround-watch/dimens.xml
new file mode 100644
index 0000000..9f30ac1
--- /dev/null
+++ b/core/res/res/values-w170dp-notround-watch/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_padding_right" type="fraction">7.5%</item>
+</resources>
diff --git a/core/res/res/values-w320dp/dimens.xml b/core/res/res/values-w320dp/dimens.xml
new file mode 100644
index 0000000..ad6d2ec
--- /dev/null
+++ b/core/res/res/values-w320dp/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <!-- The platform's desired fixed width for a dialog along the major axis
+         (the screen is in landscape). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <!-- The platform's desired fixed width for a dialog along the minor axis
+         (the screen is in portrait). This may be either a fraction or a dimension.-->
+    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+</resources>
diff --git a/core/res/res/values-watch/dimens.xml b/core/res/res/values-watch/dimens.xml
new file mode 100644
index 0000000..f79a0a5
--- /dev/null
+++ b/core/res/res/values-watch/dimens.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources>
+    <!-- each of these are relative to the display size -->
+    <item name="input_extract_layout_height" type="fraction">17.5%</item>
+    <item name="input_extract_layout_padding_left" type="fraction">3.6%</item>
+    <item name="input_extract_layout_padding_left_no_action" type="fraction">@fraction/input_extract_layout_padding_right</item>
+    <item name="input_extract_layout_padding_right" type="fraction">2.5%</item>
+    <item name="input_extract_text_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_margin_bottom" type="fraction">0%</item>
+    <item name="input_extract_action_button_width" type="dimen">24dp</item>
+    <item name="input_extract_action_button_height" type="dimen">24dp</item>
+</resources>
diff --git a/core/res/res/values-watch/themes.xml b/core/res/res/values-watch/themes.xml
index 756a94b..6d6065f 100644
--- a/core/res/res/values-watch/themes.xml
+++ b/core/res/res/values-watch/themes.xml
@@ -18,6 +18,7 @@
     <style name="Theme.Dialog.AppError" parent="Theme.Micro.Dialog.AppError" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.InputMethod" parent="Theme.Micro.InputMethod" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
     <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
 </resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 61753b1..66509fb 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -19,14 +19,16 @@
     <style name="Theme.DeviceDefault.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Micro.InputMethod"  />
+    <style name="Theme.DeviceDefault.Panel" parent="Theme.Micro.Panel"  />
     <style name="Theme.DeviceDefault.Light" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Micro.Light" />
     <style name="Theme.DeviceDefault.Light.Dialog" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Micro.Dialog" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Micro.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Light.Panel" parent="Theme.Micro.Light.Panel"  />
     <style name="Theme.DeviceDefault.Settings" parent="Theme.Micro" />
     <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Micro" />
-
 </resources>
 
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index c216be8..3b26e46 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -233,8 +233,8 @@
     <string name="notification_hidden_by_policy_text" msgid="9004631276932584600">"内容已隐藏(根据政策规定)"</string>
     <string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
-    <string name="user_owner_label" msgid="1119010402169916617">"切换为“个人”"</string>
-    <string name="managed_profile_label" msgid="5289992269827577857">"切换为“工作”"</string>
+    <string name="user_owner_label" msgid="1119010402169916617">"切换到“个人”"</string>
+    <string name="managed_profile_label" msgid="5289992269827577857">"切换到“工作”"</string>
     <string name="permgrouplab_contacts" msgid="3657758145679177612">"通讯录"</string>
     <string name="permgroupdesc_contacts" msgid="6951499528303668046">"访问您的通讯录"</string>
     <string name="permgrouplab_location" msgid="7275582855722310164">"位置信息"</string>
@@ -1473,7 +1473,7 @@
     <string name="package_installed_device_owner" msgid="8420696545959087545">"已由管理员安装"</string>
     <string name="package_updated_device_owner" msgid="8856631322440187071">"由您单位的管理员更新"</string>
     <string name="package_deleted_device_owner" msgid="7650577387493101353">"已被管理员删除"</string>
-    <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,节电助手会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n节电助手会在设备充电时自动关闭。"</string>
+    <string name="battery_saver_description" msgid="1960431123816253034">"为了延长电池的续航时间,省电模式会降低设备的性能,并限制振动、位置信息服务和大部分后台流量。对于电子邮件、聊天工具等依赖于同步功能的应用,可能要打开这类应用时才能收到新信息。\n\n省电模式会在设备充电时自动关闭。"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
       <item quantity="other">%1$d 分钟(到<xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
       <item quantity="one">1 分钟(到<xliff:g id="FORMATTEDTIME_0">%2$s</xliff:g>)</item>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 7399fa9..c8ca116 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -75,7 +75,9 @@
     <color name="material_grey_100">#fff5f5f5</color>
     <color name="material_grey_50">#fffafafa</color>
 
+    <color name="material_deep_teal_100">#ffb2dfdb</color>
     <color name="material_deep_teal_200">#ff80cbc4</color>
+    <color name="material_deep_teal_300">#ff4db6ac</color>
     <color name="material_deep_teal_500">#ff009688</color>
 
     <color name="material_blue_grey_800">#ff37474f</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index cae04ad..5cbe672 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -265,19 +265,6 @@
         <item>"0,1"</item>
     </string-array>
 
-    <!-- Set of NetworkInfo.getType() that reflect data usage. -->
-    <integer-array translatable="false" name="config_data_usage_network_types">
-        <item>0</item> <!-- TYPE_MOBILE -->
-        <item>2</item> <!-- TYPE_MOBILE_MMS -->
-        <item>3</item> <!-- TYPE_MOBILE_SUPL -->
-        <item>4</item> <!-- TYPE_MOBILE_DUN -->
-        <item>5</item> <!-- TYPE_MOBILE_HIPRI -->
-        <item>10</item> <!-- TYPE_MOBILE_FOTA -->
-        <item>11</item> <!-- TYPE_MOBILE_IMS -->
-        <item>12</item> <!-- TYPE_MOBILE_CBS -->
-        <item>14</item> <!-- TYPE_MOBILE_IA -->
-    </integer-array>
-
     <!-- The maximum duration (in milliseconds) we expect a network transition to take -->
     <integer name="config_networkTransitionTimeout">60000</integer>
 
@@ -2507,4 +2494,17 @@
 
     <!-- True if the device supports Sustained Performance Mode-->
     <bool name="config_sustainedPerformanceModeSupported">false</bool>
+
+    <!-- Controls how we deal with externally connected physical keyboards.
+         0 - When using this device, it is not clear for users to recognize when the physical
+             keyboard is (should be) connected and when it is (should be) disconnected.  Most of
+             phones and tablets with Bluetooth keyboard would fall into this category because the
+             connected Bluetooth keyboard may or may not be nearby the host device.
+         1 - When using this device, it is clear for users to recognize when the physical
+             keyboard is (should be) connected and when it is (should be) disconnected.
+             Devices with wired USB keyboard is one clear example.  Some 2-in-1 convertible
+             tablets with dedicated keyboards may have the same affordance to wired USB keyboard.
+    -->
+    <integer name="config_externalHardKeyboardBehavior">0</integer>
+
 </resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a21f276..9aec1bb 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -112,10 +112,10 @@
 
     <!-- The platform's desired fixed width for a dialog along the major axis
          (the screen is in landscape). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_major">320dp</item>
+    <item type="dimen" name="dialog_fixed_width_major">100%</item>
     <!-- The platform's desired fixed width for a dialog along the minor axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
-    <item type="dimen" name="dialog_fixed_width_minor">320dp</item>
+    <item type="dimen" name="dialog_fixed_width_minor">100%</item>
     <!-- The platform's desired fixed height for a dialog along the major axis
          (the screen is in portrait). This may be either a fraction or a dimension.-->
     <item type="dimen" name="dialog_fixed_height_major">80%</item>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index b4f95dc..96731cf 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4018,12 +4018,6 @@
     <!-- Lock-to-app unlock password string -->
     <string name="lock_to_app_unlock_password">Ask for password before unpinning</string>
 
-    <!-- Multi-Window strings -->
-    <!-- Warning message when an app that got forced to be resizable gets shown in split-screen -->
-    <string name="dock_forced_resizable">App may not work with split-screen.</string>
-    <!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
-    <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
-
     <!-- Notification shown when device owner silently installs a package [CHAR LIMIT=NONE] -->
     <string name="package_installed_device_owner">Installed by your administrator</string>
     <!-- Notification shown when device owner silently updates a package [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1e10f86..d218509 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -613,8 +613,6 @@
   <java-symbol type="string" name="display_manager_overlay_display_name" />
   <java-symbol type="string" name="display_manager_overlay_display_secure_suffix" />
   <java-symbol type="string" name="display_manager_overlay_display_title" />
-  <java-symbol type="string" name="dock_forced_resizable" />
-  <java-symbol type="string" name="dock_non_resizeble_failed_to_dock_text" />
   <java-symbol type="string" name="double_tap_toast" />
   <java-symbol type="string" name="durationDays" />
   <java-symbol type="string" name="durationDayHours" />
@@ -1130,7 +1128,6 @@
   <java-symbol type="plurals" name="pinpuk_attempts" />
 
   <java-symbol type="array" name="carrier_properties" />
-  <java-symbol type="array" name="config_data_usage_network_types" />
   <java-symbol type="array" name="config_sms_enabled_locking_shift_tables" />
   <java-symbol type="array" name="config_sms_enabled_single_shift_tables" />
   <java-symbol type="array" name="config_twoDigitNumberPattern" />
@@ -1267,6 +1264,7 @@
   <java-symbol type="drawable" name="ic_corp_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_off" />
   <java-symbol type="drawable" name="ic_corp_icon_badge" />
+  <java-symbol type="drawable" name="ic_corp_user_badge" />
   <java-symbol type="drawable" name="ic_corp_badge_no_background" />
   <java-symbol type="drawable" name="ic_corp_icon" />
   <java-symbol type="drawable" name="ic_corp_statusbar_icon" />
@@ -1744,6 +1742,7 @@
   <java-symbol type="integer" name="config_defaultNotificationLedOff" />
   <java-symbol type="integer" name="config_defaultNotificationLedOn" />
   <java-symbol type="integer" name="config_deskDockKeepsScreenOn" />
+  <java-symbol type="integer" name="config_externalHardKeyboardBehavior" />
   <java-symbol type="integer" name="config_lightSensorWarmupTime" />
   <java-symbol type="integer" name="config_lowBatteryCloseWarningBump" />
   <java-symbol type="integer" name="config_lowBatteryWarningLevel" />
@@ -2419,13 +2418,11 @@
   <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="header_sub_text" />
+  <java-symbol type="id" name="header_text" />
   <java-symbol type="id" name="expand_button" />
   <java-symbol type="id" name="notification_header" />
-  <java-symbol type="id" name="header_content_info" />
   <java-symbol type="id" name="time_divider" />
-  <java-symbol type="id" name="sub_text_divider" />
-  <java-symbol type="id" name="content_info_divider" />
+  <java-symbol type="id" name="header_text_divider" />
   <java-symbol type="id" name="text_line_1" />
   <java-symbol type="drawable" name="ic_expand_notification" />
   <java-symbol type="drawable" name="ic_collapse_notification" />
@@ -2544,4 +2541,23 @@
   <java-symbol type="id" name="titleDividerNoCustom" />
 
   <java-symbol type="bool" name="config_sustainedPerformanceModeSupported" />
+
+  <!-- Wearable input extract edit view -->
+  <java-symbol type="drawable" name="ic_input_extract_action_go" />
+  <java-symbol type="drawable" name="ic_input_extract_action_search" />
+  <java-symbol type="drawable" name="ic_input_extract_action_send" />
+  <java-symbol type="drawable" name="ic_input_extract_action_next" />
+  <java-symbol type="drawable" name="ic_input_extract_action_done" />
+  <java-symbol type="drawable" name="ic_input_extract_action_previous" />
+  <java-symbol type="drawable" name="ic_input_extract_action_return" />
+
+  <java-symbol type="fraction" name="input_extract_layout_height" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_left_no_action" />
+  <java-symbol type="fraction" name="input_extract_layout_padding_right" />
+  <java-symbol type="fraction" name="input_extract_text_margin_bottom" />
+  <java-symbol type="fraction" name="input_extract_action_margin_bottom" />
+
+  <java-symbol type="dimen" name="input_extract_action_button_width" />
+  <java-symbol type="dimen" name="input_extract_action_button_height" />
 </resources>
diff --git a/core/res/res/values/themes_micro.xml b/core/res/res/values/themes_micro.xml
index 478d66c..25a6e00 100644
--- a/core/res/res/values/themes_micro.xml
+++ b/core/res/res/values/themes_micro.xml
@@ -83,4 +83,18 @@
         <item name="fontFamily">sans-serif-condensed-light</item>
         <item name="textColor">@color/micro_text_light</item>
     </style>
+
+   <style name="Theme.Micro.Panel" parent="Theme.Material.Panel"  />
+   <style name="Theme.Micro.Light.Panel" parent="Theme.Material.Light.Panel"  />
+
+    <!-- Default theme for material style input methods, which is used by the
+         {@link android.inputmethodservice.InputMethodService} class.
+         This inherits from Theme.Panel, but sets up IME appropriate animations
+         and a few custom attributes. -->
+    <style name="Theme.Micro.InputMethod" parent="Theme.Micro.Panel">
+        <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+        <item name="imeFullscreenBackground">#1e282c</item>
+        <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+        <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
+    </style>
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index b7926cf..2452cfd 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1256,15 +1256,6 @@
         <service android:name="android.os.BinderThreadPriorityService"
                 android:process=":BinderThreadPriorityService" />
 
-        <!-- Used by ApplyOverrideConfigurationTest -->
-        <activity android:name="android.app.activity.ApplyOverrideConfigurationActivity"
-                  android:configChanges="orientation|screenSize">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <!-- Application components used for search manager tests -->
 
         <activity android:name="android.app.activity.SearchableActivity"
diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java
deleted file mode 100644
index 3df522d..0000000
--- a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationActivity.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.app.activity;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Configuration;
-
-public class ApplyOverrideConfigurationActivity extends Activity {
-
-    @Override
-    protected void attachBaseContext(Context newBase) {
-        super.attachBaseContext(newBase);
-
-        Configuration overrideConfig = new Configuration();
-        overrideConfig.smallestScreenWidthDp = ApplyOverrideConfigurationTest.OVERRIDE_WIDTH;
-        applyOverrideConfiguration(overrideConfig);
-    }
-}
diff --git a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java b/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java
deleted file mode 100644
index 15ed77e..0000000
--- a/core/tests/coretests/src/android/app/activity/ApplyOverrideConfigurationTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.app.activity;
-
-import android.app.UiAutomation;
-import android.content.res.Configuration;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.test.ActivityInstrumentationTestCase2;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class ApplyOverrideConfigurationTest extends
-        ActivityInstrumentationTestCase2<ApplyOverrideConfigurationActivity> {
-
-    public static final int OVERRIDE_WIDTH = 9999;
-
-    public ApplyOverrideConfigurationTest() {
-        super(ApplyOverrideConfigurationActivity.class);
-    }
-
-    @Before
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_0);
-    }
-
-    @Test
-    public void testConfigurationIsOverriden() throws Exception {
-        Configuration config = getActivity().getResources().getConfiguration();
-        assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
-
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_FREEZE_90);
-
-        config = getActivity().getResources().getConfiguration();
-        assertEquals(OVERRIDE_WIDTH, config.smallestScreenWidthDp);
-    }
-
-    @After
-    @Override
-    public void tearDown() throws Exception {
-        getInstrumentation().getUiAutomation().setRotation(UiAutomation.ROTATION_UNFREEZE);
-        super.tearDown();
-    }
-}
diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index 59ffd56..eafe427 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -16,13 +16,35 @@
 
 package android.widget;
 
-import android.app.Activity;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.Espresso.pressBack;
+import static android.support.test.espresso.action.ViewActions.clearText;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.replaceText;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static android.widget.espresso.DragHandleUtils.onHandleView;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupContainsItem;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsDisplayed;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.assertSuggestionsPopupIsNotDisplayed;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.clickSuggestionsPopupItem;
+import static android.widget.espresso.SuggestionsPopupwindowUtils.onSuggestionsPopup;
+import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
+import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
+import static org.hamcrest.Matchers.is;
 import android.content.res.TypedArray;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.ViewAssertion;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.text.Selection;
-import android.text.SpannableStringBuilder;
+import android.text.Spannable;
 import android.text.Spanned;
 import android.text.TextPaint;
 import android.text.style.SuggestionSpan;
@@ -42,55 +64,215 @@
         super(TextViewActivity.class);
     }
 
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getActivity();
+    }
+
+    private void setSuggestionSpan(SuggestionSpan span, int start, int end) {
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        textView.post(
+                () -> {
+                    final Spannable text = (Spannable) textView.getText();
+                    text.setSpan(span, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE);
+                    Selection.setSelection(text, (start + end) / 2);
+                });
+        getInstrumentation().waitForIdleSync();
+    }
+
     @SmallTest
-    @Suppress
+    public void testOnTextContextMenuItem() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
+        getInstrumentation().waitForIdleSync();
+
+        assertSuggestionsPopupIsDisplayed();
+    }
+
+    @SmallTest
+    public void testSelectionActionMode() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('e')));
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.replace));
+        sleepForFloatingToolbarPopup();
+        clickFloatingToolbarItem(
+                getActivity().getString(com.android.internal.R.string.replace));
+
+        assertSuggestionsPopupIsDisplayed();
+    }
+
+    @SmallTest
+    public void testInsertionActionMode() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+        onHandleView(com.android.internal.R.id.insertion_handle).perform(click());
+        sleepForFloatingToolbarPopup();
+        assertFloatingToolbarContainsItem(
+                getActivity().getString(com.android.internal.R.string.replace));
+        clickFloatingToolbarItem(
+                getActivity().getString(com.android.internal.R.string.replace));
+
+        assertSuggestionsPopupIsDisplayed();
+    }
+
+    private void showSuggestionsPopup() {
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+        textView.post(() -> textView.onTextContextMenuItem(TextView.ID_REPLACE));
+        getInstrumentation().waitForIdleSync();
+        assertSuggestionsPopupIsDisplayed();
+    }
+
+    @SmallTest
+    public void testSuggestionItems() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_AUTO_CORRECTION);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("DEF");
+        assertSuggestionsPopupContainsItem("Def");
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+
+        // Select an item.
+        clickSuggestionsPopupItem("DEF");
+        assertSuggestionsPopupIsNotDisplayed();
+        onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi")));
+
+        showSuggestionsPopup();
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("def");
+        assertSuggestionsPopupContainsItem("Def");
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+
+        // Delete
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+        assertSuggestionsPopupIsNotDisplayed();
+        onView(withId(R.id.textview)).check(matches(withText("abc ghi")));
+    }
+
+    @SmallTest
+    public void testMisspelled() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"}, SuggestionSpan.FLAG_MISSPELLED);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        showSuggestionsPopup();
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("DEF");
+        assertSuggestionsPopupContainsItem("Def");
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.addToDictionary));
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+
+        // Click "Add to dictionary".
+        clickSuggestionsPopupItem(
+                getActivity().getString(com.android.internal.R.string.addToDictionary));
+        // TODO: Check if add to dictionary dialog is displayed.
+    }
+
+    @SmallTest
+    public void testEasyCorrect() {
+        final String text = "abc def ghi";
+
+        onView(withId(R.id.textview)).perform(click());
+        onView(withId(R.id.textview)).perform(replaceText(text));
+
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                new String[]{"DEF", "Def"},
+                SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
+        setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("DEF");
+        assertSuggestionsPopupContainsItem("Def");
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+
+        // Select an item.
+        clickSuggestionsPopupItem("DEF");
+        assertSuggestionsPopupIsNotDisplayed();
+        onView(withId(R.id.textview)).check(matches(withText("abc DEF ghi")));
+
+        onView(withId(R.id.textview)).perform(clickOnTextAtIndex(text.indexOf('e')));
+        assertSuggestionsPopupIsNotDisplayed();
+
+        showSuggestionsPopup();
+        assertSuggestionsPopupIsDisplayed();
+        assertSuggestionsPopupContainsItem("def");
+        assertSuggestionsPopupContainsItem("Def");
+        assertSuggestionsPopupContainsItem(
+                getActivity().getString(com.android.internal.R.string.delete));
+    }
+
+    @SmallTest
     public void testTextAppearanceInSuggestionsPopup() {
-        final Activity activity = getActivity();
+        final String text = "abc def ghi";
 
-        final String sampleText = "abc def ghi";
         final String[] singleWordCandidates = {"DEF", "Def"};
-        final SuggestionSpan singleWordSuggestionSpan = new SuggestionSpan(activity,
-                singleWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
-        final int singleWordSpanStart = 4;
-        final int singleWordSpanEnd = 7;
-
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(getActivity(),
+                singleWordCandidates, SuggestionSpan.FLAG_MISSPELLED);
         final String[] multiWordCandidates = {"ABC DEF GHI", "Abc Def Ghi"};
-        final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(activity,
-                multiWordCandidates, SuggestionSpan.FLAG_AUTO_CORRECTION);
-        final int multiWordSpanStart = 0;
-        final int multiWordSpanEnd = 11;
+        final SuggestionSpan multiWordSuggestionSpan = new SuggestionSpan(getActivity(),
+                multiWordCandidates, SuggestionSpan.FLAG_MISSPELLED);
 
-        TypedArray array = activity.obtainStyledAttributes(com.android.internal.R.styleable.Theme);
-        int id = array.getResourceId(
+        final TypedArray array =
+                getActivity().obtainStyledAttributes(com.android.internal.R.styleable.Theme);
+        final int id = array.getResourceId(
                 com.android.internal.R.styleable.Theme_textEditSuggestionHighlightStyle, 0);
         array.recycle();
-
-        TextAppearanceSpan expectedSpan = new TextAppearanceSpan(activity, id);
-        TextPaint tmpTp = new TextPaint();
+        final TextAppearanceSpan expectedSpan = new TextAppearanceSpan(getActivity(), id);
+        final TextPaint tmpTp = new TextPaint();
         expectedSpan.updateDrawState(tmpTp);
         final int expectedHighlightTextColor = tmpTp.getColor();
         final float expectedHighlightTextSize = tmpTp.getTextSize();
-
-        final EditText editText = (EditText) activity.findViewById(R.id.textview);
-        final Editor editor = editText.getEditorForTesting();
-        assertNotNull(editor);
-
-        // Request to show SuggestionsPopupWindow.
-        Runnable showSuggestionWindowRunner = new Runnable() {
-            @Override
-            public void run() {
-                SpannableStringBuilder ssb = new SpannableStringBuilder();
-                ssb.append(sampleText);
-                ssb.setSpan(singleWordSuggestionSpan, singleWordSpanStart, singleWordSpanEnd,
-                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                ssb.setSpan(multiWordSuggestionSpan, multiWordSpanStart, multiWordSpanEnd,
-                        Spanned.SPAN_INCLUSIVE_INCLUSIVE);
-                editText.setText(ssb);
-
-                Selection.setSelection(editText.getText(), singleWordSpanStart, singleWordSpanEnd);
-                editText.onTextContextMenuItem(TextView.ID_REPLACE);
-            }
-        };
+        final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
 
         // In this test, the SuggestionsPopupWindow looks like
         //   abc def ghi
@@ -103,96 +285,74 @@
         // | DELETE        |
         // -----------------
         // *XX* means that XX is highlighted.
-        Runnable popupVaridator = new Runnable() {
-            @Override
-            public void run() {
-                Editor.SuggestionsPopupWindow popupWindow =
-                        editor.getSuggestionsPopupWindowForTesting();
-                assertNotNull(popupWindow);
+        for (int i = 0; i < 2; i++) {
+            onView(withId(R.id.textview)).perform(click());
+            onView(withId(R.id.textview)).perform(replaceText(text));
+            setSuggestionSpan(suggestionSpan, text.indexOf('d'), text.indexOf('f') + 1);
+            setSuggestionSpan(multiWordSuggestionSpan, 0, text.length());
 
-                LinearLayout linearLayout = (LinearLayout) popupWindow.getContentViewForTesting();
-                assertNotNull(linearLayout);
+            showSuggestionsPopup();
+            assertSuggestionsPopupIsDisplayed();
+            assertSuggestionsPopupContainsItem("abc DEF ghi");
+            assertSuggestionsPopupContainsItem("abc Def ghi");
+            assertSuggestionsPopupContainsItem("ABC DEF GHI");
+            assertSuggestionsPopupContainsItem("Abc Def Ghi");
+            assertSuggestionsPopupContainsItem(
+                    getActivity().getString(com.android.internal.R.string.delete));
 
-                ListView listView = (ListView)linearLayout.findViewById(
-                        com.android.internal.R.id.suggestionContainer);
-                assertNotNull(listView);
+            onSuggestionsPopup().check(new ViewAssertion() {
+                @Override
+                public void check(View view, NoMatchingViewException e) {
+                    final ListView listView = (ListView) view.findViewById(
+                            com.android.internal.R.id.suggestionContainer);
+                    assertNotNull(listView);
+                    final int childNum = listView.getChildCount();
+                    assertEquals(singleWordCandidates.length + multiWordCandidates.length,
+                            childNum);
 
-                int childNum = listView.getChildCount();
-                assertEquals(singleWordCandidates.length + multiWordCandidates.length, childNum);
+                    for (int j = 0; j < childNum; j++) {
+                        final TextView suggestion = (TextView) listView.getChildAt(j);
+                        assertNotNull(suggestion);
+                        final Spanned spanned = (Spanned) suggestion.getText();
+                        assertNotNull(spanned);
 
-                for (int i = 0; i < singleWordCandidates.length; ++i) {
-                    TextView textView = (TextView) listView.getChildAt(i);
-                    assertNotNull(textView);
+                        // Check that the suggestion item order is kept.
+                        final String expectedText;
+                        if (j < singleWordCandidates.length) {
+                            expectedText = "abc " + singleWordCandidates[j] + " ghi";
+                        } else {
+                            expectedText = multiWordCandidates[j - singleWordCandidates.length];
+                        }
+                        assertEquals(expectedText, spanned.toString());
 
-                    Spanned spanned = (Spanned) textView.getText();
-                    assertNotNull(spanned);
+                        // Check that the text is highlighted with correct color and text size.
+                        final TextAppearanceSpan[] taSpan = spanned.getSpans(
+                                text.indexOf('d'), text.indexOf('f') + 1, TextAppearanceSpan.class);
+                        assertEquals(1, taSpan.length);
+                        TextPaint tp = new TextPaint();
+                        taSpan[0].updateDrawState(tp);
+                        assertEquals(expectedHighlightTextColor, tp.getColor());
+                        assertEquals(expectedHighlightTextSize, tp.getTextSize());
 
-                    // Check that the suggestion item order is kept.
-                    String expectedText = "abc " + singleWordCandidates[i] + " ghi";
-                    assertEquals(expectedText, spanned.toString());
-
-                    // Check that the text is highlighted with correct color and text size.
-                    TextAppearanceSpan[] taSpan = spanned.getSpans(singleWordSpanStart,
-                            singleWordSpanEnd, TextAppearanceSpan.class);
-                    assertEquals(1, taSpan.length);
-                    TextPaint tp = new TextPaint();
-                    taSpan[0].updateDrawState(tp);
-                    assertEquals(expectedHighlightTextColor, tp.getColor());
-                    assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
-                    // Check only center word is highlighted.
-                    assertEquals(singleWordSpanStart, spanned.getSpanStart(taSpan[0]));
-                    assertEquals(singleWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
+                        // Check the correct part of the text is highlighted.
+                        final int expectedStart;
+                        final int expectedEnd;
+                        if (j < singleWordCandidates.length) {
+                            expectedStart = text.indexOf('d');
+                            expectedEnd = text.indexOf('f') + 1;
+                        } else {
+                            expectedStart = 0;
+                            expectedEnd = text.length();
+                        }
+                        assertEquals(expectedStart, spanned.getSpanStart(taSpan[0]));
+                        assertEquals(expectedEnd, spanned.getSpanEnd(taSpan[0]));
+                    }
                 }
-
-                for (int i = 0; i < multiWordCandidates.length; ++i) {
-                    int indexInListView = singleWordCandidates.length + i;
-                    TextView textView = (TextView) listView.getChildAt(indexInListView);
-                    assertNotNull(textView);
-
-                    Spanned spanned = (Spanned) textView.getText();
-                    assertNotNull(spanned);
-
-                    // Check that the suggestion item order is kept.
-                    assertEquals(multiWordCandidates[i], spanned.toString());
-
-                    // Check that the text is highlighted with correct color and text size.
-                    TextAppearanceSpan[] taSpan = spanned.getSpans(
-                            0, multiWordCandidates[i].length(), TextAppearanceSpan.class);
-                    assertEquals(1, taSpan.length);
-                    TextPaint tp = new TextPaint();
-                    taSpan[0].updateDrawState(tp);
-                    assertEquals(expectedHighlightTextColor, tp.getColor());
-                    assertEquals(expectedHighlightTextSize, tp.getTextSize());
-
-                    // Check the whole text is highlighted.
-                    assertEquals(multiWordSpanStart, spanned.getSpanStart(taSpan[0]));
-                    assertEquals(multiWordSpanEnd, spanned.getSpanEnd(taSpan[0]));
-                }
-
-                TextView deleteButton = (TextView)linearLayout.findViewById(
-                        com.android.internal.R.id.deleteButton);
-                assertEquals(View.VISIBLE, deleteButton.getWindowVisibility());
-            }
-        };
-
-        // Show the SuggestionWindow and verify the contents.
-        activity.runOnUiThread(showSuggestionWindowRunner);
-        getInstrumentation().waitForIdleSync();
-        activity.runOnUiThread(popupVaridator);
-
-        // Request to hide the SuggestionPopupWindow and wait until it is hidden.
-        activity.runOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                editText.setText("");
-            }
-        });
-        getInstrumentation().waitForIdleSync();
-
-        // Show and verify the contents again.
-        activity.runOnUiThread(showSuggestionWindowRunner);
-        getInstrumentation().waitForIdleSync();
-        activity.runOnUiThread(popupVaridator);
+            });
+            pressBack();
+            onView(withId(R.id.textview))
+                    .inRoot(withDecorView(is(getActivity().getWindow().getDecorView())))
+                    .perform(clearText());
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 923b829..edb749b95 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -16,6 +16,10 @@
 
 package android.widget;
 
+
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemDisabled;
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuContainsItemEnabled;
+import static android.widget.espresso.ContextMenuUtils.assertContextMenuIsNotDisplayed;
 import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
 import static android.widget.espresso.TextViewActions.mouseClickOnTextAtIndex;
@@ -41,11 +45,9 @@
 
 import com.android.frameworks.coretests.R;
 
-import android.support.test.espresso.Espresso;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.MotionEvent;
-import android.widget.espresso.ContextMenuUtils;
 
 /**
  * Tests mouse interaction of the TextView widget from an Activity
@@ -57,7 +59,8 @@
     }
 
     @Override
-    public void setUp() {
+    public void setUp() throws Exception {
+        super.setUp();
         getActivity();
     }
 
@@ -102,28 +105,28 @@
         onView(withId(R.id.textview)).perform(click());
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text));
 
-        ContextMenuUtils.assertContextMenuIsNotDisplayed();
+        assertContextMenuIsNotDisplayed();
 
         onView(withId(R.id.textview)).perform(
                 mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
 
-        ContextMenuUtils.assertContextMenuContainsItemDisabled(
+        assertContextMenuContainsItemDisabled(
                 getActivity().getString(com.android.internal.R.string.copy));
-        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+        assertContextMenuContainsItemEnabled(
                 getActivity().getString(com.android.internal.R.string.undo));
 
         // Hide context menu.
         pressBack();
-        ContextMenuUtils.assertContextMenuIsNotDisplayed();
+        assertContextMenuIsNotDisplayed();
 
         onView(withId(R.id.textview)).perform(
                 mouseDragOnText(text.indexOf("c"), text.indexOf("h")));
         onView(withId(R.id.textview)).perform(
                 mouseClickOnTextAtIndex(text.indexOf("d"), MotionEvent.BUTTON_SECONDARY));
 
-        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+        assertContextMenuContainsItemEnabled(
                 getActivity().getString(com.android.internal.R.string.copy));
-        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+        assertContextMenuContainsItemEnabled(
                 getActivity().getString(com.android.internal.R.string.undo));
 
         // Hide context menu.
@@ -133,9 +136,9 @@
 
         onView(withId(R.id.textview)).perform(
                 mouseClickOnTextAtIndex(text.indexOf("i"), MotionEvent.BUTTON_SECONDARY));
-        ContextMenuUtils.assertContextMenuContainsItemDisabled(
+        assertContextMenuContainsItemDisabled(
                 getActivity().getString(com.android.internal.R.string.copy));
-        ContextMenuUtils.assertContextMenuContainsItemEnabled(
+        assertContextMenuContainsItemEnabled(
                 getActivity().getString(com.android.internal.R.string.undo));
 
         // Hide context menu.
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index ecf88f1..67ffd2b 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -19,7 +19,6 @@
 import static android.support.test.espresso.action.ViewActions.longClick;
 import static android.widget.espresso.DragHandleUtils.assertNoSelectionHandles;
 import static android.widget.espresso.DragHandleUtils.onHandleView;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.onFloatingToolBarItem;
 import static android.widget.espresso.TextViewActions.clickOnTextAtIndex;
 import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText;
 import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex;
@@ -31,9 +30,10 @@
 import static android.widget.espresso.TextViewAssertions.hasSelection;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
-import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
 import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarDoesNotContainItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.clickFloatingToolbarItem;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.sleepForFloatingToolbarPopup;
 import static android.support.test.espresso.Espresso.onView;
 import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.action.ViewActions.pressKey;
@@ -67,7 +67,8 @@
     }
 
     @Override
-    public void setUp() {
+    public void setUp() throws Exception {
+        super.setUp();
         getActivity();
     }
 
@@ -256,7 +257,8 @@
 
         onView(withId(R.id.textview)).perform(typeTextIntoFocusedView("test"));
         onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(1));
-        onFloatingToolBarItem(withText(com.android.internal.R.string.cut)).perform(click());
+        clickFloatingToolbarItem(
+                getActivity().getString(com.android.internal.R.string.cut));
         onView(withId(R.id.textview)).perform(longClick());
         sleepForFloatingToolbarPopup();
 
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 0f7f359..838f4db 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -17,6 +17,7 @@
 package android.widget.espresso;
 
 import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
 import static android.support.test.espresso.assertion.ViewAssertions.matches;
 import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
 import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
@@ -24,6 +25,7 @@
 import static android.support.test.espresso.matcher.ViewMatchers.isRoot;
 import static android.support.test.espresso.matcher.ViewMatchers.withTagValue;
 import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
 import static org.hamcrest.Matchers.allOf;
 import static org.hamcrest.Matchers.is;
 
@@ -34,8 +36,6 @@
 import android.support.test.espresso.UiController;
 import android.support.test.espresso.ViewAction;
 import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.action.ViewActions;
-import android.support.test.espresso.matcher.ViewMatchers;
 import android.view.View;
 
 import com.android.internal.widget.FloatingToolbar;
@@ -90,7 +90,7 @@
         final int id = com.android.internal.R.id.overflow;
         onView(allOf(withId(id), isDisplayed()))
                 .inRoot(withDecorView(hasDescendant(withId(id))))
-                .perform(ViewActions.click());
+                .perform(click());
         onView(isRoot()).perform(SLEEP);
     }
 
@@ -106,7 +106,7 @@
      */
     public static void assertFloatingToolbarContainsItem(String itemLabel) {
         try{
-            onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+            onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
         } catch (AssertionError e) {
             try{
                 toggleOverflow();
@@ -115,7 +115,7 @@
                 throw e;
             }
             try{
-                onFloatingToolBar().check(matches(hasDescendant(ViewMatchers.withText(itemLabel))));
+                onFloatingToolBar().check(matches(hasDescendant(withText(itemLabel))));
             } finally {
                 toggleOverflow();
             }
@@ -138,6 +138,21 @@
     }
 
     /**
+     * Click specified item on the floating tool bar.
+     *
+     * @param itemLabel label of the item.
+     */
+    public static void clickFloatingToolbarItem(String itemLabel) {
+        try{
+            onFloatingToolBarItem(withText(itemLabel)).check(matches(isDisplayed()));
+        } catch (AssertionError e) {
+            // Try to find the item in the overflow menu.
+            toggleOverflow();
+        }
+        onFloatingToolBarItem(withText(itemLabel)).perform(click());
+    }
+
+    /**
      * ViewAction to sleep to wait floating toolbar's animation.
      */
     private static final ViewAction SLEEP = new ViewAction() {
diff --git a/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
new file mode 100644
index 0000000..b5a96ae
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/espresso/SuggestionsPopupwindowUtils.java
@@ -0,0 +1,121 @@
+/*
+ * 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.widget.espresso;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.RootMatchers.withDecorView;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+
+import org.hamcrest.Matcher;
+
+import android.support.test.espresso.NoMatchingRootException;
+import android.support.test.espresso.NoMatchingViewException;
+import android.support.test.espresso.UiController;
+import android.support.test.espresso.ViewAction;
+import android.support.test.espresso.ViewInteraction;
+import android.support.test.espresso.action.GeneralLocation;
+import android.support.test.espresso.action.Press;
+import android.support.test.espresso.action.Tap;
+import android.view.View;
+
+public final class SuggestionsPopupwindowUtils {
+    private static final int id = com.android.internal.R.id.suggestionWindowContainer;
+
+    private SuggestionsPopupwindowUtils() {};
+
+    public static ViewInteraction onSuggestionsPopup() {
+        return onView(withId(id)).inRoot(withDecorView(hasDescendant(withId(id))));
+    }
+
+    private static ViewInteraction onSuggestionsPopupItem(Matcher<View> matcher) {
+        return onView(matcher).inRoot(withDecorView(hasDescendant(withId(id))));
+    }
+
+    /**
+     * Asserts that the suggestions popup is displayed on screen.
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertSuggestionsPopupIsDisplayed() {
+        onSuggestionsPopup().check(matches(isDisplayed()));
+    }
+
+    /**
+     * Asserts that the suggestions popup is not displayed on screen.
+     *
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertSuggestionsPopupIsNotDisplayed() {
+        try {
+            onSuggestionsPopup().check(matches(isDisplayed()));
+        } catch (NoMatchingRootException | NoMatchingViewException | AssertionError e) {
+            return;
+        }
+        throw new AssertionError("Suggestions popup is displayed");
+    }
+
+    /**
+     * Asserts that the suggestions popup contains the specified item.
+     *
+     * @param itemLabel label of the item.
+     * @throws AssertionError if the assertion fails
+     */
+    public static void assertSuggestionsPopupContainsItem(String itemLabel) {
+        onSuggestionsPopupItem(withText(itemLabel)).check(matches(isDisplayed()));
+    }
+
+    /**
+     * Click on the specified item in the suggestions popup.
+     *
+     * @param itemLabel label of the item.
+     */
+    public static void clickSuggestionsPopupItem(String itemLabel) {
+        onSuggestionsPopupItem(withText(itemLabel)).perform(new SuggestionItemClickAction());
+    }
+
+    /**
+     * Click action to avoid checking ViewClickAction#getConstraints().
+     * TODO: Use Espresso.onData instead of this.
+     */
+    private static final class SuggestionItemClickAction implements ViewAction {
+        private final ViewClickAction mViewClickAction;
+
+        public SuggestionItemClickAction() {
+            mViewClickAction =
+                    new ViewClickAction(Tap.SINGLE, GeneralLocation.VISIBLE_CENTER, Press.FINGER);
+        }
+
+        @Override
+        public Matcher<View> getConstraints() {
+            return isDisplayed();
+        }
+
+        @Override
+        public String getDescription() {
+            return mViewClickAction.getDescription();
+        }
+
+        @Override
+        public void perform(UiController uiController, View view) {
+            mViewClickAction.perform(uiController, view);
+        }
+    }
+}
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index be2e6bf..702218c 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -11,6 +11,9 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := NotificationStressTests
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    ub-uiautomator
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/notificationtests/src/android/app/NotificationStressTest.java b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
index 4cb617e..6e86c37 100644
--- a/core/tests/notificationtests/src/android/app/NotificationStressTest.java
+++ b/core/tests/notificationtests/src/android/app/NotificationStressTest.java
@@ -16,15 +16,19 @@
 
 package android.app;
 
-
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
 import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
 import android.test.InstrumentationTestCase;
 import android.test.RepetitiveTest;
-import android.test.TimedTest;
+import android.util.Log;
 
+import java.lang.InterruptedException;
+import java.lang.reflect.Method;
 import java.util.Random;
 
 /**
@@ -34,51 +38,78 @@
 public class NotificationStressTest extends InstrumentationTestCase {
 
     private static final int NUM_ITERATIONS = 200;
+    private static final int NUM_ITERATIONS_2 = 30;
+    private static final int LONG_TIMEOUT = 2000;
+    // 50 notifications per app: defined as Variable MAX_PACKAGE_NOTIFICATIONS in
+    // NotificationManagerService.java
+    private static final int MAX_NOTIFCATIONS = 50;
     private static final int[] ICONS = new int[] {
-        android.R.drawable.stat_notify_call_mute,
-        android.R.drawable.stat_notify_chat,
-        android.R.drawable.stat_notify_error,
-        android.R.drawable.stat_notify_missed_call,
-        android.R.drawable.stat_notify_more,
-        android.R.drawable.stat_notify_sdcard,
-        android.R.drawable.stat_notify_sdcard_prepare,
-        android.R.drawable.stat_notify_sdcard_usb,
-        android.R.drawable.stat_notify_sync,
-        android.R.drawable.stat_notify_sync_noanim,
-        android.R.drawable.stat_notify_voicemail,
+            android.R.drawable.stat_notify_call_mute,
+            android.R.drawable.stat_notify_chat,
+            android.R.drawable.stat_notify_error,
+            android.R.drawable.stat_notify_missed_call,
+            android.R.drawable.stat_notify_more,
+            android.R.drawable.stat_notify_sdcard,
+            android.R.drawable.stat_notify_sdcard_prepare,
+            android.R.drawable.stat_notify_sdcard_usb,
+            android.R.drawable.stat_notify_sync,
+            android.R.drawable.stat_notify_sync_noanim,
+            android.R.drawable.stat_notify_voicemail,
     };
 
     private final Random mRandom = new Random();
     private Context mContext;
     private NotificationManager mNotificationManager;
-    private int notifyId = 0;
+    private UiDevice mDevice = null;
+    private int mNotifyId = 0;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mDevice = UiDevice.getInstance(getInstrumentation());
         mContext = getInstrumentation().getContext();
         mNotificationManager = (NotificationManager) mContext.getSystemService(
                 Context.NOTIFICATION_SERVICE);
+        mDevice.setOrientationNatural();
+        mNotificationManager.cancelAll();
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
+        mDevice.unfreezeRotation();
         mNotificationManager.cancelAll();
     }
 
-    @RepetitiveTest(numIterations=NUM_ITERATIONS)
+    @RepetitiveTest(numIterations = NUM_ITERATIONS)
     public void testNotificationStress() {
         // Cancel one of every five notifications to vary load on notification manager
-        if (notifyId % 5 == 4) {
-            mNotificationManager.cancel(notifyId - 4);
+        if (mNotifyId % 5 == 4) {
+            mNotificationManager.cancel(mNotifyId - 4);
         }
-        sendNotification(notifyId++, "testNotificationStressNotify");
+        sendNotification(mNotifyId++, "testNotificationStressNotify");
+    }
+
+    @RepetitiveTest(numIterations = NUM_ITERATIONS_2)
+    public void testNotificationsWithShadeStress() throws Exception {
+        mDevice.openNotification();
+        Thread.sleep(LONG_TIMEOUT);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            sendNotification(mNotifyId++, "testNotificationStressNotify");
+        }
+        Thread.sleep(500);
+        assertTrue(mNotificationManager.getActiveNotifications().length == MAX_NOTIFCATIONS);
+        for (int j = 0; j < MAX_NOTIFCATIONS; j++) {
+            mNotificationManager.cancel(--mNotifyId);
+        }
+        if (isLockScreen()) {
+            fail("Notification stress test failed, back to lockscreen");
+        }
     }
 
     private void sendNotification(int id, CharSequence text) {
         // Fill in arbitrary content
-        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
+        Intent intent = new Intent(Intent.ACTION_VIEW);
         PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
         CharSequence title = text + " " + id;
         CharSequence subtitle = String.valueOf(System.currentTimeMillis());
@@ -90,8 +121,19 @@
                 .setContentTitle(title)
                 .setContentText(subtitle)
                 .setContentIntent(pendingIntent)
+                .setPriority(Notification.PRIORITY_HIGH)
                 .build();
         mNotificationManager.notify(id, notification);
         SystemClock.sleep(10);
     }
+
+    private boolean isLockScreen() {
+        KeyguardManager myKM = (KeyguardManager) mContext
+                .getSystemService(Context.KEYGUARD_SERVICE);
+        if (myKM.inKeyguardRestrictedInputMode()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
 }
diff --git a/graphics/java/android/graphics/FontFamily.java b/graphics/java/android/graphics/FontFamily.java
index f741e3c..e48bf79 100644
--- a/graphics/java/android/graphics/FontFamily.java
+++ b/graphics/java/android/graphics/FontFamily.java
@@ -17,8 +17,12 @@
 package android.graphics;
 
 import android.content.res.AssetManager;
+import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
 import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
 import java.util.List;
 
 /**
@@ -27,6 +31,9 @@
  * @hide
  */
 public class FontFamily {
+
+    private static String TAG = "FontFamily";
+
     /**
      * @hide
      */
@@ -62,7 +69,15 @@
     }
 
     public boolean addFont(String path, int ttcIndex) {
-        return nAddFont(mNativePtr, path, ttcIndex);
+        try (FileInputStream file = new FileInputStream(path)) {
+            FileChannel fileChannel = file.getChannel();
+            long fontSize = fileChannel.size();
+            ByteBuffer fontBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fontSize);
+            return nAddFont(mNativePtr, fontBuffer, ttcIndex);
+        } catch (IOException e) {
+            Log.e(TAG, "Error mapping font file " + path);
+            return false;
+        }
     }
 
     public boolean addFontWeightStyle(ByteBuffer font, int ttcIndex, List<FontListParser.Axis> axes,
@@ -76,7 +91,7 @@
 
     private static native long nCreateFamily(String lang, int variant);
     private static native void nUnrefFamily(long nativePtr);
-    private static native boolean nAddFont(long nativeFamily, String path, int ttcIndex);
+    private static native boolean nAddFont(long nativeFamily, ByteBuffer font, int ttcIndex);
     private static native boolean nAddFontWeightStyle(long nativeFamily, ByteBuffer font,
             int ttcIndex, List<FontListParser.Axis> listOfAxis,
             int weight, boolean isItalic);
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index be816f7..0606b0b 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -248,16 +248,17 @@
     tests/unit/FatVectorTests.cpp \
     tests/unit/GlopBuilderTests.cpp \
     tests/unit/GpuMemoryTrackerTests.cpp \
+    tests/unit/GradientCacheTests.cpp \
     tests/unit/LayerUpdateQueueTests.cpp \
     tests/unit/LinearAllocatorTests.cpp \
     tests/unit/MatrixTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/RenderNodeTests.cpp \
     tests/unit/SkiaBehaviorTests.cpp \
+    tests/unit/SnapshotTests.cpp \
     tests/unit/StringUtilsTests.cpp \
     tests/unit/TextDropShadowCacheTests.cpp \
-    tests/unit/VectorDrawableTests.cpp \
-    tests/unit/GradientCacheTests.cpp
+    tests/unit/VectorDrawableTests.cpp
 
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index b70d586..9f98241 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -50,7 +50,7 @@
     }
 
     // resolvedClipRect = intersect(parentMatrix * localClip, parentClip)
-    clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator,
+    clipState = snapshot.serializeIntersectedClip(allocator,
             recordedOp.localClip, *(snapshot.transform));
     LOG_ALWAYS_FATAL_IF(!clipState, "must clip!");
 
@@ -85,8 +85,7 @@
 ResolvedRenderState::ResolvedRenderState(LinearAllocator& allocator, Snapshot& snapshot,
         const Matrix4& localTransform, const ClipBase* localClip) {
     transform.loadMultiply(*snapshot.transform, localTransform);
-    clipState = snapshot.mutateClipArea().serializeIntersectedClip(allocator,
-            localClip, *(snapshot.transform));
+    clipState = snapshot.serializeIntersectedClip(allocator, localClip, *(snapshot.transform));
     clippedBounds = clipState->rect;
     clipSideFlags = OpClipSideFlags::Full;
     localProjectionPathMask = nullptr;
diff --git a/libs/hwui/ClipArea.cpp b/libs/hwui/ClipArea.cpp
index f886dda..35fe06d 100644
--- a/libs/hwui/ClipArea.cpp
+++ b/libs/hwui/ClipArea.cpp
@@ -217,6 +217,7 @@
 
 void ClipArea::clipRectWithTransform(const Rect& r, const mat4* transform,
         SkRegion::Op op) {
+    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
     if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     switch (mMode) {
@@ -233,6 +234,7 @@
 }
 
 void ClipArea::clipRegion(const SkRegion& region, SkRegion::Op op) {
+    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
     if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     enterRegionMode();
@@ -242,6 +244,7 @@
 
 void ClipArea::clipPathWithTransform(const SkPath& path, const mat4* transform,
         SkRegion::Op op) {
+    if (op == SkRegion::kReplace_Op) mReplaceOpObserved = true;
     if (!mPostViewportClipObserved && op == SkRegion::kIntersect_Op) op = SkRegion::kReplace_Op;
     onClipUpdated();
     SkMatrix skTransform;
@@ -379,6 +382,7 @@
             serialization->rect.set(mClipRegion.getBounds());
             break;
         }
+        serialization->intersectWithRoot = mReplaceOpObserved;
         // TODO: this is only done for draw time, should eventually avoid for record time
         serialization->rect.snapToPixelBoundaries();
         mLastSerialization = serialization;
diff --git a/libs/hwui/ClipArea.h b/libs/hwui/ClipArea.h
index 1654eb8..6eb2eef 100644
--- a/libs/hwui/ClipArea.h
+++ b/libs/hwui/ClipArea.h
@@ -103,6 +103,7 @@
             : mode(ClipMode::Rectangle)
             , rect(rect) {}
     const ClipMode mode;
+    bool intersectWithRoot = false;
     // Bounds of the clipping area, used to define the scissor, and define which
     // portion of the stencil is updated/used
     Rect rect;
@@ -173,8 +174,8 @@
         return mMode == ClipMode::RectangleList;
     }
 
-    const ClipBase* serializeClip(LinearAllocator& allocator);
-    const ClipBase* serializeIntersectedClip(LinearAllocator& allocator,
+    WARN_UNUSED_RESULT const ClipBase* serializeClip(LinearAllocator& allocator);
+    WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator,
             const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
     void applyClip(const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
 
@@ -214,6 +215,7 @@
 
     ClipMode mMode;
     bool mPostViewportClipObserved = false;
+    bool mReplaceOpObserved = false;
 
     /**
      * If mLastSerialization is non-null, it represents an already serialized copy
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 0401f2d..f12e523 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -462,7 +462,7 @@
     int count = mCanvasState.save(SaveFlags::MatrixClip);
 
     // apply state from RecordedOp (clip first, since op's clip is transformed by current matrix)
-    mCanvasState.writableSnapshot()->mutateClipArea().applyClip(op.localClip,
+    mCanvasState.writableSnapshot()->applyClip(op.localClip,
             *mCanvasState.currentSnapshot()->transform);
     mCanvasState.concatMatrix(op.localMatrix);
 
diff --git a/libs/hwui/OpDumper.cpp b/libs/hwui/OpDumper.cpp
index c34cfbe..cab93e8 100644
--- a/libs/hwui/OpDumper.cpp
+++ b/libs/hwui/OpDumper.cpp
@@ -33,10 +33,15 @@
     op.localMatrix.mapRect(localBounds);
     output << sOpNameLut[op.opId] << " " << localBounds;
 
-    if (op.localClip && !op.localClip->rect.contains(localBounds)) {
+    if (op.localClip
+            && (!op.localClip->rect.contains(localBounds) || op.localClip->intersectWithRoot)) {
         output << std::fixed << std::setprecision(0)
              << " clip=" << op.localClip->rect
              << " mode=" << (int)op.localClip->mode;
+
+        if (op.localClip->intersectWithRoot) {
+             output << " iwr";
+        }
     }
 }
 
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index 4e9ac9c..7e85333 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -156,6 +156,12 @@
     return;
 }
 
+bool PathParser::isVerbValid(char verb) {
+    verb = tolower(verb);
+    return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q'
+            || verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
+}
+
 void PathParser::getPathDataFromString(PathData* data, ParseResult* result,
         const char* pathStr, size_t strLen) {
     if (pathStr == NULL) {
@@ -171,6 +177,12 @@
         end = nextStart(pathStr, strLen, end);
         std::vector<float> points;
         getFloats(&points, result, pathStr, start, end);
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+        }
+        // If either verb or points is not valid, return immediately.
         if (result->failureOccurred) {
             return;
         }
@@ -182,10 +194,15 @@
     }
 
     if ((end - start) == 1 && start < strLen) {
+        if (!isVerbValid(pathStr[start])) {
+            result->failureOccurred = true;
+            result->failureMessage = "Invalid pathData. Failure occurred at position "
+                    + std::to_string(start) + " of path: " + pathStr;
+            return;
+        }
         data->verbs.push_back(pathStr[start]);
         data->verbSizes.push_back(0);
     }
-    return;
 }
 
 void PathParser::dump(const PathData& data) {
@@ -218,7 +235,8 @@
     // Check if there is valid data coming out of parsing the string.
     if (pathData.verbs.size() == 0) {
         result->failureOccurred = true;
-        result->failureMessage = "No verbs found in the string for pathData";
+        result->failureMessage = "No verbs found in the string for pathData: ";
+        result->failureMessage += pathStr;
         return;
     }
     VectorDrawableUtils::verbsToPath(skPath, pathData);
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index c4bbb74..180a7a3 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -44,6 +44,7 @@
     ANDROID_API static void getPathDataFromString(PathData* outData, ParseResult* result,
             const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
+    static bool isVerbValid(char verb);
 };
 
 }; // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 9578486..ea06fcd 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -81,14 +81,14 @@
 #endif
 }
 
-void RenderNode::setStagingDisplayList(DisplayList* displayList) {
+void RenderNode::setStagingDisplayList(DisplayList* displayList, TreeObserver* observer) {
     mNeedsDisplayListSync = true;
     delete mStagingDisplayList;
     mStagingDisplayList = displayList;
     // If mParentCount == 0 we are the sole reference to this RenderNode,
     // so immediately free the old display list
     if (!mParentCount && !mStagingDisplayList) {
-        deleteDisplayList(nullptr);
+        deleteDisplayList(observer);
     }
 }
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b0136cf..acdc3d8 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -117,7 +117,7 @@
 
     void debugDumpLayers(const char* prefix);
 
-    ANDROID_API void setStagingDisplayList(DisplayList* newData);
+    ANDROID_API void setStagingDisplayList(DisplayList* newData, TreeObserver* observer);
 
     void computeOrdering();
 
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index d784280..2c9c9d9 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -242,6 +242,33 @@
 #endif
 }
 
+static Snapshot* getClipRoot(Snapshot* target) {
+    while (target->previous && target->previous->previous) {
+        target = target->previous;
+    }
+    return target;
+}
+
+const ClipBase* Snapshot::serializeIntersectedClip(LinearAllocator& allocator,
+        const ClipBase* recordedClip, const Matrix4& recordedClipTransform) {
+    auto target = this;
+    if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
+        // Clip must be intersected with root, instead of current clip.
+        target = getClipRoot(this);
+    }
+
+    return target->mClipArea->serializeIntersectedClip(allocator,
+            recordedClip, recordedClipTransform);
+}
+
+void Snapshot::applyClip(const ClipBase* recordedClip, const Matrix4& transform) {
+    if (CC_UNLIKELY(recordedClip && recordedClip->intersectWithRoot)) {
+        // current clip is being replaced, but must intersect with clip root
+        *mClipArea = *(getClipRoot(this)->mClipArea);
+    }
+    mClipArea->applyClip(recordedClip, transform);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Queries
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index 3a01d04..d8f926e 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -170,6 +170,10 @@
     const ClipArea& getClipArea() const { return *mClipArea; }
     ClipArea& mutateClipArea() { return *mClipArea; }
 
+    WARN_UNUSED_RESULT const ClipBase* serializeIntersectedClip(LinearAllocator& allocator,
+            const ClipBase* recordedClip, const Matrix4& recordedClipTransform);
+    void applyClip(const ClipBase* clip, const Matrix4& transform);
+
     /**
      * Resets the clip to the specified rect.
      */
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index b9e3358..b39de26 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -22,8 +22,9 @@
 
 namespace android {
 
-MinikinFontSkia::MinikinFontSkia(SkTypeface *typeface) :
-    mTypeface(typeface) {
+MinikinFontSkia::MinikinFontSkia(SkTypeface* typeface, const void* fontData, size_t fontSize,
+        int ttcIndex) :
+    mTypeface(typeface), mFontData(fontData), mFontSize(fontSize), mTtcIndex(ttcIndex) {
 }
 
 MinikinFontSkia::~MinikinFontSkia() {
@@ -66,22 +67,38 @@
     bounds->mBottom = skBounds.fBottom;
 }
 
-bool MinikinFontSkia::GetTable(uint32_t tag, uint8_t *buf, size_t *size) {
-    if (buf == NULL) {
-        const size_t tableSize = mTypeface->getTableSize(tag);
-        *size = tableSize;
-        return tableSize != 0;
-    } else {
-        const size_t actualSize = mTypeface->getTableData(tag, 0, *size, buf);
-        *size = actualSize;
-        return actualSize != 0;
+const void* MinikinFontSkia::GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy) {
+    // we don't have a buffer to the font data, copy to own buffer
+    const size_t tableSize = mTypeface->getTableSize(tag);
+    *size = tableSize;
+    if (tableSize == 0) {
+        return nullptr;
     }
+    void* buf = malloc(tableSize);
+    if (buf == nullptr) {
+        return nullptr;
+    }
+    mTypeface->getTableData(tag, 0, tableSize, buf);
+    *destroy = free;
+    return buf;
 }
 
 SkTypeface *MinikinFontSkia::GetSkTypeface() const {
     return mTypeface;
 }
 
+const void* MinikinFontSkia::GetFontData() const {
+    return mFontData;
+}
+
+size_t MinikinFontSkia::GetFontSize() const {
+    return mFontSize;
+}
+
+int MinikinFontSkia::GetFontIndex() const {
+    return mTtcIndex;
+}
+
 int32_t MinikinFontSkia::GetUniqueId() const {
     return mTypeface->uniqueID();
 }
diff --git a/libs/hwui/hwui/MinikinSkia.h b/libs/hwui/hwui/MinikinSkia.h
index 1d50168..dbc65f9 100644
--- a/libs/hwui/hwui/MinikinSkia.h
+++ b/libs/hwui/hwui/MinikinSkia.h
@@ -28,7 +28,8 @@
 class ANDROID_API MinikinFontSkia : public MinikinFont {
 public:
     // Note: this takes ownership of the reference (will unref on dtor)
-    explicit MinikinFontSkia(SkTypeface *typeface);
+    explicit MinikinFontSkia(SkTypeface *typeface, const void* fontData, size_t fontSize,
+        int ttcIndex);
 
     ~MinikinFontSkia();
 
@@ -38,20 +39,30 @@
     void GetBounds(MinikinRect* bounds, uint32_t glyph_id,
         const MinikinPaint &paint) const;
 
-    // If buf is NULL, just update size
-    bool GetTable(uint32_t tag, uint8_t *buf, size_t *size);
+    const void* GetTable(uint32_t tag, size_t* size, MinikinDestroyFunc* destroy);
 
     int32_t GetUniqueId() const;
 
     SkTypeface* GetSkTypeface() const;
 
+    // Access to underlying raw font bytes
+    const void* GetFontData() const;
+    size_t GetFontSize() const;
+    int GetFontIndex() const;
+
     static uint32_t packPaintFlags(const SkPaint* paint);
     static void unpackPaintFlags(SkPaint* paint, uint32_t paintFlags);
 
     // set typeface and fake bold/italic parameters
     static void populateSkPaint(SkPaint* paint, const MinikinFont* font, FontFakery fakery);
 private:
-    SkTypeface *mTypeface;
+    SkTypeface* mTypeface;
+
+    // A raw pointer to the font data - it should be owned by some other object with
+    // lifetime at least as long as this object.
+    const void* mFontData;
+    size_t mFontSize;
+    int mTtcIndex;
 };
 
 }  // namespace android
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index fa8ad5d..c583988 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -66,7 +66,10 @@
         ALOGD("makeFontCollection adding %s", fn);
         SkTypeface *skFace = SkTypeface::CreateFromFile(fn);
         if (skFace != NULL) {
-            MinikinFont *font = new MinikinFontSkia(skFace);
+            // TODO: might be a nice optimization to get access to the underlying font
+            // data, but would require us opening the file ourselves and passing that
+            // to the appropriate Create method of SkTypeface.
+            MinikinFont *font = new MinikinFontSkia(skFace, NULL, 0, 0);
             family->addFont(font);
             font->Unref();
         } else {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 63fa788..890d4a1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -76,15 +76,15 @@
 }
 
 CanvasContext::~CanvasContext() {
-    destroy();
+    destroy(nullptr);
     mRenderThread.renderState().unregisterCanvasContext(this);
 }
 
-void CanvasContext::destroy() {
+void CanvasContext::destroy(TreeObserver* observer) {
     stopDrawing();
     setSurface(nullptr);
-    freePrefetechedLayers();
-    destroyHardwareResources();
+    freePrefetchedLayers(observer);
+    destroyHardwareResources(observer);
     mAnimationContext->destroy();
 #if !HWUI_NEW_OPS
     if (mCanvas) {
@@ -113,18 +113,11 @@
         mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
         mHaveNewSurface = true;
         mSwapHistory.clear();
-        makeCurrent();
     } else {
         mRenderThread.removeFrameCallback(this);
     }
 }
 
-void CanvasContext::requireSurface() {
-    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-            "requireSurface() called but no surface set!");
-    makeCurrent();
-}
-
 void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
     mSwapBehavior = swapBehavior;
 }
@@ -146,6 +139,18 @@
     return mRenderThread.removeFrameCallback(this);
 }
 
+void CanvasContext::setStopped(bool stopped) {
+    if (mStopped != stopped) {
+        mStopped = stopped;
+        if (mStopped) {
+            mRenderThread.removeFrameCallback(this);
+            if (mEglManager.isCurrent(mEglSurface)) {
+                mEglManager.makeCurrent(EGL_NO_SURFACE);
+            }
+        }
+    }
+}
+
 // TODO: don't pass viewport size, it's automatic via EGL
 void CanvasContext::setup(int width, int height, float lightRadius,
         uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
@@ -172,7 +177,9 @@
     mOpaque = opaque;
 }
 
-void CanvasContext::makeCurrent() {
+bool CanvasContext::makeCurrent() {
+    if (mStopped) return false;
+
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
     EGLint error = 0;
@@ -180,6 +187,7 @@
     if (error) {
         setSurface(nullptr);
     }
+    return !error;
 }
 
 static bool wasSkipped(FrameInfo* info) {
@@ -222,7 +230,7 @@
     mAnimationContext->runRemainingAnimations(info);
     GL_CHECKPOINT(MODERATE);
 
-    freePrefetechedLayers();
+    freePrefetchedLayers(info.observer);
     GL_CHECKPOINT(MODERATE);
 
     if (CC_UNLIKELY(!mNativeSurface.get())) {
@@ -569,25 +577,24 @@
 }
 
 void CanvasContext::markLayerInUse(RenderNode* node) {
-    if (mPrefetechedLayers.erase(node)) {
+    if (mPrefetchedLayers.erase(node)) {
         node->decStrong(nullptr);
     }
 }
 
-static void destroyPrefetechedNode(RenderNode* node) {
-    ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...", node->getName());
-    node->destroyHardwareResources(nullptr);
-    node->decStrong(nullptr);
-}
-
-void CanvasContext::freePrefetechedLayers() {
-    if (mPrefetechedLayers.size()) {
-        std::for_each(mPrefetechedLayers.begin(), mPrefetechedLayers.end(), destroyPrefetechedNode);
-        mPrefetechedLayers.clear();
+void CanvasContext::freePrefetchedLayers(TreeObserver* observer) {
+    if (mPrefetchedLayers.size()) {
+        for (auto& node : mPrefetchedLayers) {
+            ALOGW("Incorrectly called buildLayer on View: %s, destroying layer...",
+                    node->getName());
+            node->destroyHardwareResources(observer);
+            node->decStrong(observer);
+        }
+        mPrefetchedLayers.clear();
     }
 }
 
-void CanvasContext::buildLayer(RenderNode* node) {
+void CanvasContext::buildLayer(RenderNode* node, TreeObserver* observer) {
     ATRACE_CALL();
     if (!mEglManager.hasEglContext()) return;
 #if !HWUI_NEW_OPS
@@ -599,6 +606,7 @@
 
     TreeInfo info(TreeInfo::MODE_FULL, *this);
     info.damageAccumulator = &mDamageAccumulator;
+    info.observer = observer;
 #if HWUI_NEW_OPS
     info.layerUpdateQueue = &mLayerUpdateQueue;
 #else
@@ -628,7 +636,7 @@
 #endif
 
     node->incStrong(nullptr);
-    mPrefetechedLayers.insert(node);
+    mPrefetchedLayers.insert(node);
 }
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
@@ -636,12 +644,12 @@
     return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
 }
 
-void CanvasContext::destroyHardwareResources() {
+void CanvasContext::destroyHardwareResources(TreeObserver* observer) {
     stopDrawing();
     if (mEglManager.hasEglContext()) {
-        freePrefetechedLayers();
+        freePrefetchedLayers(observer);
         for (const sp<RenderNode>& node : mRenderNodes) {
-            node->destroyHardwareResources(nullptr);
+            node->destroyHardwareResources(observer);
         }
         Caches& caches = Caches::getInstance();
         // Make sure to release all the textures we were owning as there won't
@@ -671,7 +679,7 @@
 }
 
 Layer* CanvasContext::createTextureLayer() {
-    requireSurface();
+    mEglManager.initialize();
     return LayerRenderer::createTextureLayer(mRenderThread.renderState());
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 6d0889e..52df3abe 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -82,27 +82,28 @@
     void initialize(Surface* surface);
     void updateSurface(Surface* surface);
     bool pauseSurface(Surface* surface);
+    void setStopped(bool stopped);
     bool hasSurface() { return mNativeSurface.get(); }
 
     void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     void setLightCenter(const Vector3& lightCenter);
     void setOpaque(bool opaque);
-    void makeCurrent();
+    bool makeCurrent();
     void prepareTree(TreeInfo& info, int64_t* uiFrameInfo,
             int64_t syncQueued, RenderNode* target);
     void draw();
-    void destroy();
+    void destroy(TreeObserver* observer);
 
     // IFrameCallback, Choreographer-driven frame callback entry point
     virtual void doFrame() override;
     void prepareAndDraw(RenderNode* node);
 
-    void buildLayer(RenderNode* node);
+    void buildLayer(RenderNode* node, TreeObserver* observer);
     bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
     void markLayerInUse(RenderNode* node);
 
-    void destroyHardwareResources();
+    void destroyHardwareResources(TreeObserver* observer);
     static void trimMemory(RenderThread& thread, int level);
 
     static void invokeFunctor(RenderThread& thread, Functor* functor);
@@ -172,9 +173,8 @@
     friend class android::uirenderer::RenderState;
 
     void setSurface(Surface* window);
-    void requireSurface();
 
-    void freePrefetechedLayers();
+    void freePrefetchedLayers(TreeObserver* observer);
 
     void waitOnFences();
 
@@ -185,6 +185,7 @@
     EglManager& mEglManager;
     sp<Surface> mNativeSurface;
     EGLSurface mEglSurface = EGL_NO_SURFACE;
+    bool mStopped = false;
     bool mBufferPreserved = false;
     SwapBehavior mSwapBehavior = kSwap_default;
     struct SwapHistory {
@@ -218,7 +219,7 @@
     FrameInfoVisualizer mProfiler;
     std::unique_ptr<FrameMetricsReporter> mFrameMetricsReporter;
 
-    std::set<RenderNode*> mPrefetechedLayers;
+    std::set<RenderNode*> mPrefetchedLayers;
 
     // Stores the bounds of the main content.
     Rect mContentDrawBounds;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index e8b9725..ed472ac 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -65,11 +65,12 @@
     }
 }
 
-int DrawFrameTask::drawFrame() {
+int DrawFrameTask::drawFrame(TreeObserver* observer) {
     LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
 
     mSyncResult = kSync_OK;
     mSyncQueued = systemTime(CLOCK_MONOTONIC);
+    mObserver = observer;
     postAndWait();
 
     return mSyncResult;
@@ -88,6 +89,7 @@
     bool canDrawThisFrame;
     {
         TreeInfo info(TreeInfo::MODE_FULL, *mContext);
+        info.observer = mObserver;
         canUnblockUiThread = syncFrameState(info);
         canDrawThisFrame = info.out.canDrawThisFrame;
     }
@@ -113,7 +115,7 @@
     ATRACE_CALL();
     int64_t vsync = mFrameInfo[static_cast<int>(FrameInfoIndex::Vsync)];
     mRenderThread->timeLord().vsyncReceived(vsync);
-    mContext->makeCurrent();
+    bool canDraw = mContext->makeCurrent();
     Caches::getInstance().textureCache.resetMarkInUse(mContext);
 
     for (size_t i = 0; i < mLayers.size(); i++) {
@@ -124,8 +126,9 @@
 
     // This is after the prepareTree so that any pending operations
     // (RenderNode tree state, prefetched layers, etc...) will be flushed.
-    if (CC_UNLIKELY(!mContext->hasSurface())) {
+    if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
         mSyncResult |= kSync_LostSurfaceRewardIfFound;
+        info.out.canDrawThisFrame = false;
     }
 
     if (info.out.hasAnimations) {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index cae251a9..9bba065 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -62,7 +62,7 @@
     void pushLayerUpdate(DeferredLayerUpdater* layer);
     void removeLayerUpdate(DeferredLayerUpdater* layer);
 
-    int drawFrame();
+    int drawFrame(TreeObserver* observer);
 
     int64_t* frameInfo() { return mFrameInfo; }
 
@@ -87,6 +87,7 @@
 
     int mSyncResult;
     int64_t mSyncQueued;
+    TreeObserver* mObserver;
 
     int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
 };
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 8def7ad..ac6a28f 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -270,12 +270,6 @@
         // Ensure we always have a valid surface & context
         surface = mPBufferSurface;
     }
-    // TODO: Temporary to help diagnose b/27286867
-    if (mCurrentSurface == mPBufferSurface || surface == mPBufferSurface) {
-        ALOGD("Switching from surface %p%s to %p%s", mCurrentSurface,
-                mCurrentSurface == mPBufferSurface ? " (pbuffer)" : "",
-                        surface, surface == mPBufferSurface ? " (pbuffer)" : "");
-    }
     if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
         if (errOut) {
             *errOut = eglGetError();
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 16dd108..c5a2dc7 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,14 +52,6 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-namespace DumpFlags {
-    enum {
-        FrameStats = 1 << 0,
-        Reset      = 1 << 1,
-        JankStats  = 1 << 2,
-    };
-};
-
 CREATE_BRIDGE4(createContext, RenderThread* thread, bool translucent,
         RenderNode* rootRenderNode, IContextFactory* contextFactory) {
     return new CanvasContext(*args->thread, args->translucent,
@@ -175,6 +167,18 @@
     return (bool) postAndWait(task);
 }
 
+CREATE_BRIDGE2(setStopped, CanvasContext* context, bool stopped) {
+    args->context->setStopped(args->stopped);
+    return nullptr;
+}
+
+void RenderProxy::setStopped(bool stopped) {
+    SETUP_TASK(setStopped);
+    args->context = mContext;
+    args->stopped = stopped;
+    postAndWait(task);
+}
+
 CREATE_BRIDGE6(setup, CanvasContext* context, int width, int height,
         float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) {
     args->context->setup(args->width, args->height, args->lightRadius,
@@ -222,18 +226,19 @@
     return mDrawFrameTask.frameInfo();
 }
 
-int RenderProxy::syncAndDrawFrame() {
-    return mDrawFrameTask.drawFrame();
+int RenderProxy::syncAndDrawFrame(TreeObserver* observer) {
+    return mDrawFrameTask.drawFrame(observer);
 }
 
-CREATE_BRIDGE1(destroy, CanvasContext* context) {
-    args->context->destroy();
+CREATE_BRIDGE2(destroy, CanvasContext* context, TreeObserver* observer) {
+    args->context->destroy(args->observer);
     return nullptr;
 }
 
-void RenderProxy::destroy() {
+void RenderProxy::destroy(TreeObserver* observer) {
     SETUP_TASK(destroy);
     args->context = mContext;
+    args->observer = observer;
     // destroyCanvasAndSurface() needs a fence as when it returns the
     // underlying BufferQueue is going to be released from under
     // the render thread.
@@ -287,15 +292,16 @@
     return layer;
 }
 
-CREATE_BRIDGE2(buildLayer, CanvasContext* context, RenderNode* node) {
-    args->context->buildLayer(args->node);
+CREATE_BRIDGE3(buildLayer, CanvasContext* context, RenderNode* node, TreeObserver* observer) {
+    args->context->buildLayer(args->node, args->observer);
     return nullptr;
 }
 
-void RenderProxy::buildLayer(RenderNode* node) {
+void RenderProxy::buildLayer(RenderNode* node, TreeObserver* observer) {
     SETUP_TASK(buildLayer);
     args->context = mContext;
     args->node = node;
+    args->observer = observer;
     postAndWait(task);
 }
 
@@ -332,15 +338,16 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE1(destroyHardwareResources, CanvasContext* context) {
-    args->context->destroyHardwareResources();
+CREATE_BRIDGE2(destroyHardwareResources, CanvasContext* context, TreeObserver* observer) {
+    args->context->destroyHardwareResources(args->observer);
     return nullptr;
 }
 
-void RenderProxy::destroyHardwareResources() {
+void RenderProxy::destroyHardwareResources(TreeObserver* observer) {
     SETUP_TASK(destroyHardwareResources);
     args->context = mContext;
-    post(task);
+    args->observer = observer;
+    postAndWait(task);
 }
 
 CREATE_BRIDGE2(trimMemory, RenderThread* thread, int level) {
@@ -422,6 +429,9 @@
     if (args->dumpFlags & DumpFlags::Reset) {
         args->context->resetFrameStats();
     }
+    if (args->dumpFlags & DumpFlags::JankStats) {
+        args->thread->jankTracker().dump(args->fd);
+    }
     return nullptr;
 }
 
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 8d65a82..32d3283 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -42,6 +42,7 @@
 class DisplayList;
 class Layer;
 class Rect;
+class TreeObserver;
 
 namespace renderthread {
 
@@ -49,6 +50,14 @@
 class RenderThread;
 class RenderProxyBridge;
 
+namespace DumpFlags {
+    enum {
+        FrameStats = 1 << 0,
+        Reset      = 1 << 1,
+        JankStats  = 1 << 2,
+    };
+};
+
 /*
  * RenderProxy is strictly single threaded. All methods must be invoked on the owning
  * thread. It is important to note that RenderProxy may be deleted while it has
@@ -70,26 +79,27 @@
     ANDROID_API void initialize(const sp<Surface>& surface);
     ANDROID_API void updateSurface(const sp<Surface>& surface);
     ANDROID_API bool pauseSurface(const sp<Surface>& surface);
+    ANDROID_API void setStopped(bool stopped);
     ANDROID_API void setup(int width, int height, float lightRadius,
             uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
     ANDROID_API void setLightCenter(const Vector3& lightCenter);
     ANDROID_API void setOpaque(bool opaque);
     ANDROID_API int64_t* frameInfo();
-    ANDROID_API int syncAndDrawFrame();
-    ANDROID_API void destroy();
+    ANDROID_API int syncAndDrawFrame(TreeObserver* observer);
+    ANDROID_API void destroy(TreeObserver* observer);
 
     ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
 
     ANDROID_API void runWithGlContext(RenderTask* task);
 
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
-    ANDROID_API void buildLayer(RenderNode* node);
+    ANDROID_API void buildLayer(RenderNode* node, TreeObserver* observer);
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap);
     ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer);
     ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer);
 
-    ANDROID_API void destroyHardwareResources();
+    ANDROID_API void destroyHardwareResources(TreeObserver* observer);
     ANDROID_API static void trimMemory(int level);
     ANDROID_API static void overrideProperty(const char* name, const char* value);
 
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index 5492035..5f4ebc0 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -146,7 +146,7 @@
         if (setup) {
             TestCanvas canvas(props.getWidth(), props.getHeight());
             setup(props, canvas);
-            node->setStagingDisplayList(canvas.finishRecording());
+            node->setStagingDisplayList(canvas.finishRecording(), nullptr);
         }
         node->setPropertyFieldsDirty(0xFFFFFFFF);
         return node;
@@ -157,7 +157,7 @@
        TestCanvas canvas(node.stagingProperties().getWidth(),
                node.stagingProperties().getHeight());
        contentCallback(canvas);
-       node.setStagingDisplayList(canvas.finishRecording());
+       node.setStagingDisplayList(canvas.finishRecording(), nullptr);
     }
 
     /**
diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
index 63c067b..f184411 100644
--- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp
@@ -59,6 +59,6 @@
                     0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr);
         }
 
-        container->setStagingDisplayList(canvas.finishRecording());
+        container->setStagingDisplayList(canvas.finishRecording(), nullptr);
     }
 };
diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
index ab368c05..8035dc4 100644
--- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp
@@ -76,7 +76,7 @@
             // draw it to parent DisplayList
             canvas.drawRenderNode(cards[ci].get());
         }
-        listView->setStagingDisplayList(canvas.finishRecording());
+        listView->setStagingDisplayList(canvas.finishRecording(), nullptr);
     }
 private:
     SkBitmap createRandomCharIcon() {
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 58c0876..c5af061 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -95,7 +95,7 @@
         testContext.waitForVsync();
         nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
         UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
-        proxy->syncAndDrawFrame();
+        proxy->syncAndDrawFrame(nullptr);
     }
 
     proxy->resetProfileInfo();
@@ -110,7 +110,7 @@
             ATRACE_NAME("UI-Draw Frame");
             UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
             scene->doFrame(i);
-            proxy->syncAndDrawFrame();
+            proxy->syncAndDrawFrame(nullptr);
         }
         if (opts.reportFrametimeWeight) {
             proxy->fence();
@@ -122,5 +122,5 @@
         }
     }
 
-    proxy->dumpProfileInfo(STDOUT_FILENO, 0);
+    proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats);
 }
diff --git a/libs/hwui/tests/unit/ClipAreaTests.cpp b/libs/hwui/tests/unit/ClipAreaTests.cpp
index 822d04f..b864703 100644
--- a/libs/hwui/tests/unit/ClipAreaTests.cpp
+++ b/libs/hwui/tests/unit/ClipAreaTests.cpp
@@ -132,8 +132,7 @@
         auto serializedClip = area.serializeClip(allocator);
         ASSERT_NE(nullptr, serializedClip);
         ASSERT_EQ(ClipMode::Rectangle, serializedClip->mode);
-        auto clipRect = reinterpret_cast<const ClipRect*>(serializedClip);
-        EXPECT_EQ(Rect(200, 200), clipRect->rect);
+        EXPECT_EQ(Rect(200, 200), serializedClip->rect);
         EXPECT_EQ(serializedClip, area.serializeClip(allocator))
                 << "Requery of clip on unmodified ClipArea must return same pointer.";
     }
@@ -192,8 +191,7 @@
         auto resolvedClip = area.serializeIntersectedClip(allocator, &recordedClip, translateScale);
         ASSERT_NE(nullptr, resolvedClip);
         ASSERT_EQ(ClipMode::Rectangle, resolvedClip->mode);
-        EXPECT_EQ(Rect(100, 100, 200, 200),
-                reinterpret_cast<const ClipRect*>(resolvedClip)->rect);
+        EXPECT_EQ(Rect(100, 100, 200, 200), resolvedClip->rect);
 
         EXPECT_EQ(resolvedClip, area.serializeIntersectedClip(allocator, &recordedClip, translateScale))
                 << "Must return previous serialization, since input is same";
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 0ea246f..9877439 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -1946,5 +1946,28 @@
     EXPECT_MATRIX_APPROX_EQ(Matrix4::identity(), observedData.rectMatrix);
 }
 
+RENDERTHREAD_TEST(FrameBuilder, clip_replace) {
+    class ClipReplaceTestRenderer : public TestRendererBase {
+    public:
+        void onColorOp(const ColorOp& op, const BakedOpState& state) override {
+            EXPECT_EQ(0, mIndex++);
+            EXPECT_TRUE(op.localClip->intersectWithRoot);
+            EXPECT_EQ(Rect(20, 10, 30, 40), state.computedState.clipState->rect)
+                    << "Expect resolved clip to be intersection of viewport clip and clip op";
+        }
+    };
+    auto node = TestUtils::createNode(20, 20, 30, 30,
+            [](RenderProperties& props, RecordingCanvas& canvas) {
+        canvas.clipRect(0, -20, 10, 30, SkRegion::kReplace_Op);
+        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+    });
+
+    FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeLTRB(10, 10, 40, 40), 50, 50,
+            TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+    ClipReplaceTestRenderer renderer;
+    frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+    EXPECT_EQ(1, renderer.getIndex());
+}
+
 } // namespace uirenderer
 } // namespace android
diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
index 58376c6..c49ff71 100644
--- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp
+++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp
@@ -569,6 +569,19 @@
     EXPECT_CLIP_RECT(Rect(-100, -100, 300, 300), dl->getOps()[0]->localClip);
 }
 
+TEST(RecordingCanvas, replaceClipIntersectWithRoot) {
+    auto dl = TestUtils::createDisplayList<RecordingCanvas>(100, 100, [](RecordingCanvas& canvas) {
+        canvas.save(SaveFlags::MatrixClip);
+        canvas.clipRect(-10, -10, 110, 110, SkRegion::kReplace_Op);
+        canvas.drawColor(SK_ColorWHITE, SkXfermode::Mode::kSrcOver_Mode);
+        canvas.restore();
+    });
+    ASSERT_EQ(1u, dl->getOps().size()) << "Must have one op";
+    // first clip must be preserved, even if it extends beyond canvas bounds
+    EXPECT_CLIP_RECT(Rect(-10, -10, 110, 110), dl->getOps()[0]->localClip);
+    EXPECT_TRUE(dl->getOps()[0]->localClip->intersectWithRoot);
+}
+
 TEST(RecordingCanvas, insertReorderBarrier) {
     auto dl = TestUtils::createDisplayList<RecordingCanvas>(200, 200, [](RecordingCanvas& canvas) {
         canvas.drawRect(0, 0, 400, 400, SkPaint());
diff --git a/libs/hwui/tests/unit/SnapshotTests.cpp b/libs/hwui/tests/unit/SnapshotTests.cpp
new file mode 100644
index 0000000..11797a8
--- /dev/null
+++ b/libs/hwui/tests/unit/SnapshotTests.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <Snapshot.h>
+
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+
+TEST(Snapshot, serializeIntersectedClip) {
+    auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
+    auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
+    auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
+    root->previous = actualRoot.get();
+    child->previous = root.get();
+
+    LinearAllocator allocator;
+    ClipRect rect(Rect(0, 0, 75, 75));
+    {
+        auto intersectWithChild = child->serializeIntersectedClip(allocator,
+                &rect, Matrix4::identity());
+        ASSERT_NE(nullptr, intersectWithChild);
+        EXPECT_EQ(Rect(50, 50, 75, 75), intersectWithChild->rect) << "Expect intersect with child";
+    }
+
+    rect.intersectWithRoot = true;
+    {
+        auto intersectWithRoot = child->serializeIntersectedClip(allocator,
+                &rect, Matrix4::identity());
+        ASSERT_NE(nullptr, intersectWithRoot);
+        EXPECT_EQ(Rect(10, 10, 75, 75), intersectWithRoot->rect) << "Expect intersect with root";
+    }
+}
+
+TEST(Snapshot, applyClip) {
+    auto actualRoot = TestUtils::makeSnapshot(Matrix4::identity(), Rect(0, 0, 100, 100));
+    auto root = TestUtils::makeSnapshot(Matrix4::identity(), Rect(10, 10, 90, 90));
+    root->previous = actualRoot.get();
+
+    ClipRect rect(Rect(0, 0, 75, 75));
+    {
+        auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
+        child->previous = root.get();
+        child->applyClip(&rect, Matrix4::identity());
+
+        EXPECT_TRUE(child->getClipArea().isSimple());
+        EXPECT_EQ(Rect(50, 50, 75, 75), child->getRenderTargetClip());
+    }
+
+    {
+        rect.intersectWithRoot = true;
+        auto child = TestUtils::makeSnapshot(Matrix4::identity(), Rect(50, 50, 90, 90));
+        child->previous = root.get();
+        child->applyClip(&rect, Matrix4::identity());
+
+        EXPECT_TRUE(child->getClipArea().isSimple());
+        EXPECT_EQ(Rect(10, 10, 75, 75), child->getRenderTargetClip());
+    }
+}
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index 7208547..796169e 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -231,11 +231,12 @@
 };
 
 const StringPath sStringPaths[] = {
-    {"3e...3", false},
-    {"L.M.F.A.O", false},
-    {"m 1 1", true},
-    {"z", true},
-    {"1-2e34567", false}
+    {"3e...3", false}, // Not starting with a verb and ill-formatted float
+    {"L.M.F.A.O", false}, // No floats following verbs
+    {"m 1 1", true}, // Valid path data
+    {"z", true}, // Valid path data
+    {"1-2e34567", false}, // Not starting with a verb and ill-formatted float
+    {"f 4 5", false} // Invalid verb
 };
 
 
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
index b0431ce..b3195c4 100644
--- a/libs/hwui/utils/TestWindowContext.cpp
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -104,8 +104,8 @@
     }
 
     void finishDrawing() {
-        mRootNode->setStagingDisplayList(mCanvas->finishRecording());
-        mProxy->syncAndDrawFrame();
+        mRootNode->setStagingDisplayList(mCanvas->finishRecording(), nullptr);
+        mProxy->syncAndDrawFrame(nullptr);
         // Surprisingly, calling mProxy->fence() here appears to make no difference to
         // the timings we record.
     }
diff --git a/location/java/android/location/GnssClock.java b/location/java/android/location/GnssClock.java
index df42a73..25d247d 100644
--- a/location/java/android/location/GnssClock.java
+++ b/location/java/android/location/GnssClock.java
@@ -22,7 +22,8 @@
 
 /**
  * A class containing a GPS clock timestamp.
- * It represents a measurement of the GPS receiver's clock.
+ *
+ * <p>It represents a measurement of the GPS receiver's clock.
  */
 public final class GnssClock implements Parcelable {
     // The following enumerations must be in sync with the values declared in gps.h
@@ -85,7 +86,7 @@
     }
 
     /**
-     * Returns true if {@link #getLeapSecond()} is available, false otherwise.
+     * Returns {@code true} if {@link #getLeapSecond()} is available, {@code false} otherwise.
      */
     public boolean hasLeapSecond() {
         return isFlagSet(HAS_LEAP_SECOND);
@@ -93,10 +94,12 @@
 
     /**
      * Gets the leap second associated with the clock's time.
-     * The sign of the value is defined by the following equation:
-     *      utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
      *
-     * The value is only available if {@link #hasLeapSecond()} is true.
+     * <p>The sign of the value is defined by the following equation:
+     * <pre>
+     *     UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000</pre>
+     *
+     * <p>The value is only available if {@link #hasLeapSecond()} is {@code true}.
      */
     public int getLeapSecond() {
         return mLeapSecond;
@@ -123,18 +126,15 @@
     }
 
     /**
-     * Gets the GNSS receiver internal clock value in nanoseconds.
+     * Gets the GNSS receiver internal hardware clock value in nanoseconds.
      *
-     * For 'local hardware clock' this value is expected to be monotonically increasing during the
-     * reporting session. The real GPS time can be derived by compensating
-     * {@link #getFullBiasNanos()} (when it is available) from this value.
+     * <p>This value is expected to be monotonically increasing while the hardware clock remains
+     * powered on. For the case of a hardware clock that is not continuously on, see the
+     * {@link #getHardwareClockDiscontinuityCount} field. The GPS time can be derived by subtracting
+     * the sum of {@link #getFullBiasNanos()} and {@link #getBiasNanos()} (when they are available)
+     * from this value. Sub-nanosecond accuracy can be provided by means of {@link #getBiasNanos()}.
      *
-     * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
-     * receiver can achieve. {@link #getTimeUncertaintyNanos()} should be available when GPS time is
-     * specified.
-     *
-     * Sub-nanosecond accuracy can be provided by means of {@link #getBiasNanos()}.
-     * The reported time includes {@link #getTimeUncertaintyNanos()}.
+     * <p>The error estimate for this value (if applicable) is {@link #getTimeUncertaintyNanos()}.
      */
     public long getTimeNanos() {
         return mTimeNanos;
@@ -150,7 +150,8 @@
     }
 
     /**
-     * Returns true if {@link #getTimeUncertaintyNanos()} is available, false otherwise.
+     * Returns {@code true} if {@link #getTimeUncertaintyNanos()} is available, {@code false}
+     * otherwise.
      */
     public boolean hasTimeUncertaintyNanos() {
         return isFlagSet(HAS_TIME_UNCERTAINTY);
@@ -158,9 +159,13 @@
 
     /**
      * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
-     * The uncertainty is represented as an absolute (single sided) value.
      *
-     * The value is only available if {@link #hasTimeUncertaintyNanos()} is true.
+     * <p>The uncertainty is represented as an absolute (single sided) value.
+     *
+     * <p>The value is only available if {@link #hasTimeUncertaintyNanos()} is {@code true}.
+     *
+     * <p>This value is often effectively zero (it is the reference clock by which all other times
+     * and time uncertainties are measured), and thus this field may often be 0, or not provided.
      */
     public double getTimeUncertaintyNanos() {
         return mTimeUncertaintyNanos;
@@ -187,7 +192,7 @@
     }
 
     /**
-     * Returns true if {@link #getFullBiasNanos()} is available, false otherwise.
+     * Returns {@code true} if {@link #getFullBiasNanos()} is available, {@code false} otherwise.
      */
     public boolean hasFullBiasNanos() {
         return isFlagSet(HAS_FULL_BIAS);
@@ -197,14 +202,18 @@
      * Gets the difference between hardware clock ({@link #getTimeNanos()}) inside GPS receiver and
      * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
      *
-     * This value is available if the receiver has estimated GPS time. If the computed time is for a
-     * non-GPS constellation, the time offset of that constellation to GPS has to be applied to fill
-     * this value. The value contains the 'bias uncertainty' {@link #getBiasUncertaintyNanos()} in
-     * it, and it should be used for quality check. The value is only available if
-     * {@link #hasFullBiasNanos()} is true.
+     * <p>This value is available if the receiver has estimated GPS time. If the computed time is
+     * for a non-GPS constellation, the time offset of that constellation to GPS has to be applied
+     * to fill this value. The value is only available if {@link #hasFullBiasNanos()} is
+     * {@code true}.
      *
-     * The sign of the value is defined by the following equation:
-     *      local estimate of GPS time = time_ns + (full_bias_ns + bias_ns)
+     * <p>The error estimate for the sum of this field and {@link #getBiasNanos} is
+     * {@link #getBiasUncertaintyNanos()}.
+     *
+     * <p>The sign of the value is defined by the following equation:
+     *
+     * <pre>
+     *     local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)</pre>
      */
     public long getFullBiasNanos() {
         return mFullBiasNanos;
@@ -231,7 +240,7 @@
     }
 
     /**
-     * Returns true if {@link #getBiasNanos()} is available, false otherwise.
+     * Returns {@code true} if {@link #getBiasNanos()} is available, {@code false} otherwise.
      */
     public boolean hasBiasNanos() {
         return isFlagSet(HAS_BIAS);
@@ -239,9 +248,14 @@
 
     /**
      * Gets the clock's sub-nanosecond bias.
-     * The reported bias includes {@link #getBiasUncertaintyNanos()}.
      *
-     * The value is only available if {@link #hasBiasNanos()} is true.
+     * <p>See the description of how this field is part of converting from hardware clock time, to
+     * GPS time, in {@link #getFullBiasNanos()}.
+     *
+     * <p>The error estimate for the sum of this field and {@link #getFullBiasNanos} is
+     * {@link #getBiasUncertaintyNanos()}.
+     *
+     * <p>The value is only available if {@link #hasBiasNanos()} is {@code true}.
      */
     public double getBiasNanos() {
         return mBiasNanos;
@@ -268,7 +282,8 @@
     }
 
     /**
-     * Returns true if {@link #getBiasUncertaintyNanos()} is available, false otherwise.
+     * Returns {@code true} if {@link #getBiasUncertaintyNanos()} is available, {@code false}
+     * otherwise.
      */
     public boolean hasBiasUncertaintyNanos() {
         return isFlagSet(HAS_BIAS_UNCERTAINTY);
@@ -277,7 +292,10 @@
     /**
      * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
      *
-     * The value is only available if {@link #hasBiasUncertaintyNanos()} is true.
+     * <p>See the description of how this field provides the error estimate in the conversion from
+     * hardware clock time, to GPS time, in {@link #getFullBiasNanos()}.
+     *
+     * <p>The value is only available if {@link #hasBiasUncertaintyNanos()} is {@code true}.
      */
     public double getBiasUncertaintyNanos() {
         return mBiasUncertaintyNanos;
@@ -304,7 +322,8 @@
     }
 
     /**
-     * Returns true if {@link #getDriftNanosPerSecond()} is available, false otherwise.
+     * Returns {@code true} if {@link #getDriftNanosPerSecond()} is available, {@code false}
+     * otherwise.
      */
     public boolean hasDriftNanosPerSecond() {
         return isFlagSet(HAS_DRIFT);
@@ -312,10 +331,12 @@
 
     /**
      * Gets the clock's Drift in nanoseconds per second.
-     * A positive value indicates that the frequency is higher than the nominal frequency.
-     * The reported drift includes {@link #getDriftUncertaintyNanosPerSecond()}.
      *
-     * The value is only available if {@link #hasDriftNanosPerSecond()} is true.
+     * <p>A positive value indicates that the frequency is higher than the nominal (e.g. GPS master
+     * clock) frequency. The error estimate for this reported drift is
+     * {@link #getDriftUncertaintyNanosPerSecond()}.
+     *
+     * <p>The value is only available if {@link #hasDriftNanosPerSecond()} is {@code true}.
      */
     public double getDriftNanosPerSecond() {
         return mDriftNanosPerSecond;
@@ -342,7 +363,8 @@
     }
 
     /**
-     * Returns true if {@link #getDriftUncertaintyNanosPerSecond()} is available, false otherwise.
+     * Returns {@code true} if {@link #getDriftUncertaintyNanosPerSecond()} is available,
+     * {@code false} otherwise.
      */
     public boolean hasDriftUncertaintyNanosPerSecond() {
         return isFlagSet(HAS_DRIFT_UNCERTAINTY);
@@ -351,7 +373,8 @@
     /**
      * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
      *
-     * The value is only available if {@link #hasDriftUncertaintyNanosPerSecond()} is true.
+     * <p>The value is only available if {@link #hasDriftUncertaintyNanosPerSecond()} is
+     * {@code true}.
      */
     public double getDriftUncertaintyNanosPerSecond() {
         return mDriftUncertaintyNanosPerSecond;
@@ -368,7 +391,29 @@
     }
 
     /**
-     * Gets count of last hardware clock discontinuity.
+     * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+     * @hide
+     */
+    @TestApi
+    public void resetDriftUncertaintyNanosPerSecond() {
+        resetFlag(HAS_DRIFT_UNCERTAINTY);
+        mDriftUncertaintyNanosPerSecond = Double.NaN;
+    }
+
+    /**
+     * Gets count of hardware clock discontinuities.
+     *
+     * <p>When this value stays the same, vs. a value in a previously reported {@link GnssClock}, it
+     * can be safely assumed that the {@code TimeNanos} value has been derived from a clock that has
+     * been running continuously - e.g. a single continuously powered crystal oscillator, and thus
+     * the {@code (FullBiasNanos + BiasNanos)} offset can be modelled with traditional clock bias
+     * &amp; drift models.
+     *
+     * <p>Each time this value changes, vs. the value in a previously reported {@link GnssClock},
+     * that suggests the hardware clock may have experienced a discontinuity (e.g. a power cycle or
+     * other anomaly), so that any assumptions about modelling a smoothly changing
+     * {@code (FullBiasNanos + BiasNanos)} offset, and a smoothly growing {@code (TimeNanos)}
+     * between this and the previously reported {@code GnssClock}, should be reset.
      */
     public int getHardwareClockDiscontinuityCount() {
         return mHardwareClockDiscontinuityCount;
@@ -383,16 +428,6 @@
         mHardwareClockDiscontinuityCount = value;
     }
 
-    /**
-     * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
-     * @hide
-     */
-    @TestApi
-    public void resetDriftUncertaintyNanosPerSecond() {
-        resetFlag(HAS_DRIFT_UNCERTAINTY);
-        mDriftUncertaintyNanosPerSecond = Double.NaN;
-    }
-
     public static final Creator<GnssClock> CREATOR = new Creator<GnssClock>() {
         @Override
         public GnssClock createFromParcel(Parcel parcel) {
diff --git a/location/java/android/location/GnssMeasurement.java b/location/java/android/location/GnssMeasurement.java
index d78ccee..761ee22 100644
--- a/location/java/android/location/GnssMeasurement.java
+++ b/location/java/android/location/GnssMeasurement.java
@@ -67,18 +67,21 @@
     public @interface MultipathIndicator {}
 
     /**
-     * The indicator is not available or it is unknown.
+     * The indicator is not available or the presence or absence of multipath is unknown.
      */
     public static final int MULTIPATH_INDICATOR_UNKNOWN = 0;
 
     /**
-     * The measurement has been indicated to use multi-path.
+     * The measurement shows signs of multi-path.
      */
     public static final int MULTIPATH_INDICATOR_DETECTED = 1;
 
     /**
-     * The measurement has been indicated not tu use multi-path.
+     * The measurement shows no signs of multi-path.
      */
+    public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
+
+    /** @removed */
     public static final int MULTIPATH_INDICATOR_NOT_USED = 2;
 
     /** This GNSS measurement's tracking state is invalid or unknown. */
@@ -192,15 +195,17 @@
     }
 
     /**
-     * Gets the Pseudo-random number (PRN).
-     * Range: [1, 32]
+     * Gets the satellite ID.
+     *
+     * <p>Interpretation depends on {@link #getConstellationType()}.
+     * See {@link GnssStatus#getSvid(int)}.
      */
     public int getSvid() {
         return mSvid;
     }
 
     /**
-     * Sets the Pseud-random number (PRN).
+     * Sets the Satellite ID.
      * @hide
      */
     @TestApi
@@ -209,7 +214,10 @@
     }
 
     /**
-     * Getst the constellation type.
+     * Gets the constellation type.
+     *
+     * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+     * {@link GnssStatus}.
      */
     @GnssStatus.ConstellationType
     public int getConstellationType() {
@@ -228,13 +236,14 @@
     /**
      * Gets the time offset at which the measurement was taken in nanoseconds.
      *
-     * The reference receiver's time from which this is offset is specified by
+     * <p>The reference receiver's time from which this is offset is specified by
      * {@link GnssClock#getTimeNanos()}.
      *
-     * The sign of this value is given by the following equation:
-     *      measurement time = time_ns + time_offset_ns
+     * <p>The sign of this value is given by the following equation:
+     * <pre>
+     *      measurement time = TimeNanos + TimeOffsetNanos</pre>
      *
-     * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
+     * <p>The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
      * accuracy.
      */
     public double getTimeOffsetNanos() {
@@ -252,9 +261,10 @@
 
     /**
      * Gets per-satellite sync state.
-     * It represents the current sync state for the associated satellite.
      *
-     * This value helps interpret {@link #getReceivedSvTimeNanos()}.
+     * <p>It represents the current sync state for the associated satellite.
+     *
+     * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
      */
     public int getState() {
         return mState;
@@ -271,7 +281,8 @@
 
     /**
      * Gets a string representation of the 'sync state'.
-     * For internal and logging use only.
+     *
+     * <p>For internal and logging use only.
      */
     private String getStateString() {
         if (mState == STATE_UNKNOWN) {
@@ -335,66 +346,79 @@
     /**
      * Gets the received GNSS satellite time, at the measurement time, in nanoseconds.
      *
-     * For GPS &amp; QZSS, this is:
-     *   Received GPS Time-of-Week at the measurement time, in nanoseconds.
-     *   The value is relative to the beginning of the current GPS week.
+     * <p>For GPS &amp; QZSS, this is:
+     * <ul>
+     * <li>Received GPS Time-of-Week at the measurement time, in nanoseconds.</li>
+     * <li>The value is relative to the beginning of the current GPS week.</li>
+     * </ul>
      *
-     *   Given the highest sync state that can be achieved, per each satellite, valid range
-     *   for this field can be:
+     * <p>Given the highest sync state that can be achieved, per each satellite, valid range
+     * for this field can be:
+     * <pre>
      *     Searching       : [ 0       ]   : STATE_UNKNOWN
      *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
      *     Bit sync        : [ 0  20ms ]   : STATE_BIT_SYNC is set
      *     Subframe sync   : [ 0    6s ]   : STATE_SUBFRAME_SYNC is set
-     *     TOW decoded     : [ 0 1week ]   : STATE_TOW_DECODED is set
+     *     TOW decoded     : [ 0 1week ]   : STATE_TOW_DECODED is set</pre>
      *
-     *   Note well: if there is any ambiguity in integer millisecond,
-     *   STATE_MSEC_AMBIGUOUS should be set accordingly, in the 'state' field.
+     * <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS}
+     * should be set accordingly, in the 'state' field.
      *
-     *   This value must be populated if 'state' != STATE_UNKNOWN.
+     * <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}.
      *
-     * For Glonass, this is:
-     *   Received Glonass time of day, at the measurement time in nanoseconds.
+     * <p>For Glonass, this is:
+     * <ul>
+     * <li>Received Glonass time of day, at the measurement time in nanoseconds.</li>
+     * </ul>
      *
-     *   Given the highest sync state that can be achieved, per each satellite, valid range for
-     *   this field can be:
+     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+     * this field can be:
+     * <pre>
      *     Searching       : [ 0       ]   : STATE_UNKNOWN
      *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
-     *    Symbol sync    : [ 0  10ms ]   : STATE_SYMBOL_SYNC is set
-     *    Bit sync       : [ 0  20ms ]   : STATE_BIT_SYNC is set
-     *     String sync     : [ 0    2s ]   :  STATE_GLO_STRING_SYNC is set
-     *    Time of day      : [ 0  1day ]   : STATE_GLO_TOD_DECODED is set
+     *     Symbol sync     : [ 0  10ms ]   : STATE_SYMBOL_SYNC is set
+     *     Bit sync        : [ 0  20ms ]   : STATE_BIT_SYNC is set
+     *     String sync     : [ 0    2s ]   : STATE_GLO_STRING_SYNC is set
+     *     Time of day     : [ 0  1day ]   : STATE_GLO_TOD_DECODED is set</pre>
      *
-     * For Beidou, this is:
-     *   Received Beidou time of week, at the measurement time in nanoseconds.
+     * <p>For Beidou, this is:
+     * <ul>
+     * <li>Received Beidou time of week, at the measurement time in nanoseconds.</li>
+     * </ul>
      *
-     *   Given the highest sync state that can be achieved, per each satellite, valid range for
-     *   this field can be:
+     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+     * this field can be:
+     * <pre>
      *     Searching       : [ 0       ]   : STATE_UNKNOWN
      *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
      *     Bit sync (D2)   : [ 0   2ms ]   : STATE_BDS_D2_BIT_SYNC is set
      *     Bit sync (D1)   : [ 0  20ms ]   : STATE_BIT_SYNC is set
      *     Subframe (D2)   : [ 0  0.6s ]   : STATE_BDS_D2_SUBFRAME_SYNC is set
      *     Subframe (D1)   : [ 0    6s ]   : STATE_SUBFRAME_SYNC is set
-     *     Time of week    : [ 0 1week ]   : STATE_TOW_DECODED is set
+     *     Time of week    : [ 0 1week ]   : STATE_TOW_DECODED is set</pre>
      *
-     * For Galileo, this is:
-     *   Received Galileo time of week, at the measurement time in nanoseconds.
+     * <p>For Galileo, this is:
+     * <ul>
+     * <li>Received Galileo time of week, at the measurement time in nanoseconds.</li>
+     * </ul>
+     * <pre>
+     *     E1BC code lock   : [ 0   4ms ]  : STATE_GAL_E1BC_CODE_LOCK is set
+     *     E1C 2nd code lock: [ 0 100ms ]  : STATE_GAL_E1C_2ND_CODE_LOCK is set
+     *     E1B page         : [ 0    2s ]  : STATE_GAL_E1B_PAGE_SYNC is set
+     *     Time of week     : [ 0 1week ]  : STATE_GAL_TOW_DECODED is set</pre>
      *
-     *     E1BC code lock  : [ 0   4ms ]   : STATE_GAL_E1BC_CODE_LOCK is set
-     *     E1C 2nd code lock : [ 0   100ms ]   : STATE_GAL_E1C_2ND_CODE_LOCK is set
+     * <p>For SBAS, this is:
+     * <ul>
+     * <li>Received SBAS time, at the measurement time in nanoseconds.</li>
+     * </ul>
      *
-     *     E1B page        : [ 0    2s ]   : STATE_GAL_E1B_PAGE_SYNC is set
-     *     Time of week    : [ 0 1week ]   : STATE_GAL_TOW_DECODED is set
-     *
-     *   For SBAS, this is:
-     *     Received SBAS time, at the measurement time in nanoseconds.
-     *
-     *   Given the highest sync state that can be achieved, per each satellite, valid range for
-     *   this field can be:
+     * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+     * this field can be:
+     * <pre>
      *     Searching       : [ 0       ]   : STATE_UNKNOWN
      *     C/A code lock   : [ 0   1ms ]   : STATE_CODE_LOCK is set
      *     Symbol sync     : [ 0   2ms ]   : STATE_SYMBOL_SYNC is set
-     *     Message         : [ 0    1s ]   : STATE_SBAS_SYNC is set
+     *     Message         : [ 0    1s ]   : STATE_SBAS_SYNC is set</pre>
      */
     public long getReceivedSvTimeNanos() {
         return mReceivedSvTimeNanos;
@@ -410,7 +434,7 @@
     }
 
     /**
-     * Gets the received GNSS time uncertainty (1-Sigma) in nanoseconds.
+     * Gets the error estimate (1-sigma) for the received GNSS time, in nanoseconds.
      */
     public long getReceivedSvTimeUncertaintyNanos() {
         return mReceivedSvTimeUncertaintyNanos;
@@ -427,9 +451,10 @@
 
     /**
      * Gets the Carrier-to-noise density in dB-Hz.
-     * Range: [0, 63].
      *
-     * The value contains the measured C/N0 for the signal at the antenna input.
+     * <p>Typical range: 10-50 db-Hz.
+     *
+     * <p>The value contains the measured C/N0 for the signal at the antenna input.
      */
     public double getCn0DbHz() {
         return mCn0DbHz;
@@ -447,16 +472,18 @@
     /**
      * Gets the Pseudorange rate at the timestamp in m/s.
      *
-     * The reported value includes {@link #getPseudorangeRateUncertaintyMetersPerSecond()}.
+     * <p>The error estimate for this value is
+     * {@link #getPseudorangeRateUncertaintyMetersPerSecond()}.
      *
-     * The value is uncorrected, hence corrections for receiver and satellite clock frequency errors
-     * should not be included.
+     * <p>The value is uncorrected, i.e. corrections for receiver and satellite clock frequency
+     * errors are not included.
      *
-     * A positive 'uncorrected' value indicates that the SV is moving away from the receiver. The
+     * <p>A positive 'uncorrected' value indicates that the SV is moving away from the receiver. The
      * sign of the 'uncorrected' 'pseudorange rate' and its relation to the sign of 'doppler shift'
      * is given by the equation:
      *
-     *      pseudorange rate = -k * doppler shift   (where k is a constant)
+     * <pre>
+     *      pseudorange rate = -k * doppler shift   (where k is a constant)</pre>
      */
     public double getPseudorangeRateMetersPerSecond() {
         return mPseudorangeRateMetersPerSecond;
@@ -473,7 +500,8 @@
 
     /**
      * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
-     * The uncertainty is represented as an absolute (single sided) value.
+     *
+     * <p>The uncertainty is represented as an absolute (single sided) value.
      */
     public double getPseudorangeRateUncertaintyMetersPerSecond() {
         return mPseudorangeRateUncertaintyMetersPerSecond;
@@ -490,7 +518,8 @@
 
     /**
      * Gets 'Accumulated Delta Range' state.
-     * It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
+     *
+     * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
      * cycle slip (indicating 'loss of lock').
      */
     public int getAccumulatedDeltaRangeState() {
@@ -508,7 +537,8 @@
 
     /**
      * Gets a string representation of the 'Accumulated Delta Range state'.
-     * For internal and logging use only.
+     *
+     * <p>For internal and logging use only.
      */
     private String getAccumulatedDeltaRangeStateString() {
         if (mAccumulatedDeltaRangeState == ADR_STATE_UNKNOWN) {
@@ -536,14 +566,17 @@
 
     /**
      * Gets the accumulated delta range since the last channel reset, in meters.
-     * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyMeters()}.
      *
-     * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     * <p>The error estimate for this value is {@link #getAccumulatedDeltaRangeUncertaintyMeters()}.
      *
-     * A positive value indicates that the SV is moving away from the receiver.
+     * <p>The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     *
+     * <p>A positive value indicates that the SV is moving away from the receiver.
      * The sign of {@link #getAccumulatedDeltaRangeMeters()} and its relation to the sign of
      * {@link #getCarrierPhase()} is given by the equation:
-     *          accumulated delta range = -k * carrier phase    (where k is a constant)
+     *
+     * <pre>
+     *          accumulated delta range = -k * carrier phase    (where k is a constant)</pre>
      */
     public double getAccumulatedDeltaRangeMeters() {
         return mAccumulatedDeltaRangeMeters;
@@ -560,9 +593,10 @@
 
     /**
      * Gets the accumulated delta range's uncertainty (1-Sigma) in meters.
-     * The uncertainty is represented as an absolute (single sided) value.
      *
-     * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     * <p>The uncertainty is represented as an absolute (single sided) value.
+     *
+     * <p>The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      */
     public double getAccumulatedDeltaRangeUncertaintyMeters() {
         return mAccumulatedDeltaRangeUncertaintyMeters;
@@ -571,7 +605,7 @@
     /**
      * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
      *
-     * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+     * <p>The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
      *
      * @hide
      */
@@ -581,17 +615,20 @@
     }
 
     /**
-     * Returns true if {@link #getCarrierFrequencyHz()} is available, false otherwise.
+     * Returns {@code true} if {@link #getCarrierFrequencyHz()} is available, {@code false}
+     * otherwise.
      */
     public boolean hasCarrierFrequencyHz() {
         return isFlagSet(HAS_CARRIER_FREQUENCY);
     }
 
     /**
-     * Gets the carrier frequency at which codes and messages are modulated, it can be L1 or L2.
-     * If the field is not set, the carrier frequency corresponds to L1.
+     * Gets the carrier frequency at which codes and messages are modulated.
      *
-     * The value is only available if {@link #hasCarrierFrequencyHz()} is true.
+     * <p>For GPS, e.g., it can be L1 or L2.  If the field is not set, it is the primary common use
+     * frequency, e.g. L1 for GPS.
+     *
+     * <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
      */
     public float getCarrierFrequencyHz() {
         return mCarrierFrequencyHz;
@@ -618,7 +655,7 @@
     }
 
     /**
-     * Returns true if {@link #getCarrierCycles()} is available, false otherwise.
+     * Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
      */
     public boolean hasCarrierCycles() {
         return isFlagSet(HAS_CARRIER_CYCLES);
@@ -626,9 +663,10 @@
 
     /**
      * The number of full carrier cycles between the satellite and the receiver.
-     * The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
      *
-     * The value is only available if {@link #hasCarrierCycles()} is true.
+     * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
+     *
+     * <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
      */
     public long getCarrierCycles() {
         return mCarrierCycles;
@@ -655,7 +693,7 @@
     }
 
     /**
-     * Returns true if {@link #getCarrierPhase()} is available, false otherwise.
+     * Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
      */
     public boolean hasCarrierPhase() {
         return isFlagSet(HAS_CARRIER_PHASE);
@@ -663,13 +701,16 @@
 
     /**
      * Gets the RF phase detected by the receiver.
-     * Range: [0.0, 1.0].
-     * This is usually the fractional part of the complete carrier phase measurement.
      *
-     * The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
-     * The reported carrier-phase includes {@link #getCarrierPhaseUncertainty()}.
+     * <p>Range: [0.0, 1.0].
      *
-     * The value is only available if {@link #hasCarrierPhase()} is true.
+     * <p>This is the fractional part of the complete carrier phase measurement.
+     *
+     * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
+     *
+     * <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
+     *
+     * <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
      */
     public double getCarrierPhase() {
         return mCarrierPhase;
@@ -696,7 +737,8 @@
     }
 
     /**
-     * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise.
+     * Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
+     * otherwise.
      */
     public boolean hasCarrierPhaseUncertainty() {
         return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
@@ -704,9 +746,10 @@
 
     /**
      * Gets the carrier-phase's uncertainty (1-Sigma).
-     * The uncertainty is represented as an absolute (single sided) value.
      *
-     * The value is only available if {@link #hasCarrierPhaseUncertainty()} is true.
+     * <p>The uncertainty is represented as an absolute (single sided) value.
+     *
+     * <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
      */
     public double getCarrierPhaseUncertainty() {
         return mCarrierPhaseUncertainty;
@@ -751,7 +794,8 @@
 
     /**
      * Gets a string representation of the 'multi-path indicator'.
-     * For internal and logging use only.
+     *
+     * <p>For internal and logging use only.
      */
     private String getMultipathIndicatorString() {
         switch(mMultipathIndicator) {
@@ -767,7 +811,7 @@
     }
 
     /**
-     * Returns true if {@link #getSnrInDb()} is available, false otherwise.
+     * Returns {@code true} if {@link #getSnrInDb()} is available, {@code false} otherwise.
      */
     public boolean hasSnrInDb() {
         return isFlagSet(HAS_SNR);
@@ -776,7 +820,7 @@
     /**
      * Gets the Signal-to-Noise ratio (SNR) in dB.
      *
-     * The value is only available if {@link #hasSnrInDb()} is true.
+     * <p>The value is only available if {@link #hasSnrInDb()} is {@code true}.
      */
     public double getSnrInDb() {
         return mSnrInDb;
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index ec252a8..3151694 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -16,6 +16,7 @@
 
 package android.location;
 
+import android.annotation.TestApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.os.Parcel;
@@ -33,29 +34,11 @@
  * Events are delivered to registered instances of {@link Callback}.
  */
 public final class GnssMeasurementsEvent implements Parcelable {
-    /**
-     * The status of the GNSS measurements event.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_GNSS_LOCATION_DISABLED})
-    public @interface GnssMeasurementsStatus {}
-
-    /**
-     * The system does not support tracking of GNSS Measurements. This status will not change in the
-     * future.
-     */
+    /** @removed */
     public static final int STATUS_NOT_SUPPORTED = 0;
-
-    /**
-     * GNSS Measurements are successfully being tracked, it will receive updates once they are
-     * available.
-     */
+    /** @removed */
     public static final int STATUS_READY = 1;
-
-    /**
-     * GNSS provider or Location is disabled, updates will not be received until they are enabled.
-     */
+    /** @removed */
     public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
 
     private final GnssClock mClock;
@@ -68,6 +51,32 @@
      * {@link LocationManager#registerGnssMeasurementsCallback}.
      */
     public static abstract class Callback {
+        /**
+         * The status of the GNSS measurements event.
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+        public @interface GnssMeasurementsStatus {}
+
+        /**
+         * The system does not support tracking of GNSS Measurements.
+         *
+         * <p>This status will not change in the future.
+         */
+        public static final int STATUS_NOT_SUPPORTED = 0;
+
+        /**
+         * GNSS Measurements are successfully being tracked, it will receive updates once they are
+         * available.
+         */
+        public static final int STATUS_READY = 1;
+
+        /**
+         * GPS provider or Location is disabled, updates will not be received until they are
+         * enabled.
+         */
+        public static final int STATUS_LOCATION_DISABLED = 2;
 
         /**
          * Reports the latest collected GNSS Measurements.
@@ -80,6 +89,10 @@
         public void onStatusChanged(@GnssMeasurementsStatus int status) {}
     }
 
+    /**
+     * @hide
+     */
+    @TestApi
     public GnssMeasurementsEvent(GnssClock clock, GnssMeasurement[] measurements) {
         if (clock == null) {
             throw new InvalidParameterException("Parameter 'clock' must not be null.");
@@ -94,6 +107,10 @@
         mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
     }
 
+    /**
+     * Gets the GNSS receiver clock information associated with the measurements for the current
+     * event.
+     */
     @NonNull
     public GnssClock getClock() {
         return mClock;
diff --git a/location/java/android/location/GnssNavigationMessage.aidl b/location/java/android/location/GnssNavigationMessage.aidl
new file mode 100644
index 0000000..1cdd510
--- /dev/null
+++ b/location/java/android/location/GnssNavigationMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+parcelable GnssNavigationMessage;
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index a5eace8..aa26111 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -34,7 +34,7 @@
     private static final byte[] EMPTY_ARRAY = new byte[0];
 
     /**
-     * The type of the GPS Clock.
+     * The type of the GNSS Navigation Message
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -81,6 +81,51 @@
      */
     public static final int STATUS_PARITY_REBUILT = (1<<1);
 
+    /**
+     * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
+     *
+     * <p>You can implement this interface and call
+     * {@link LocationManager#registerGnssNavigationMessageCallback}.
+     */
+    public static abstract class Callback {
+        /**
+         * The status of GNSS measurements event.
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+        public @interface GnssNavigationMessageStatus {}
+
+        /**
+         * The system does not support tracking of GNSS Navigation Messages.
+         *
+         * This status will not change in the future.
+         */
+        public static final int STATUS_NOT_SUPPORTED = 0;
+
+        /**
+         * GNSS Navigation Messages are successfully being tracked, it will receive updates once
+         * they are available.
+         */
+        public static final int STATUS_READY = 1;
+
+        /**
+         * GNSS provider or Location is disabled, updated will not be received until they are
+         * enabled.
+         */
+        public static final int STATUS_LOCATION_DISABLED = 2;
+
+        /**
+         * Returns the latest collected GNSS Navigation Message.
+         */
+        public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {}
+
+        /**
+         * Returns the latest status of the GNSS Navigation Messages sub-system.
+         */
+        public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
+    }
+
     // End enumerations in sync with gps.h
 
     private int mType;
@@ -170,15 +215,16 @@
     }
 
     /**
-     * Gets the Pseudo-random number.
-     * Range: [1, 32].
+     * Gets the satellite ID.
+     *
+     * <p>Range varies by constellation.  See definition at {@code GnssStatus#getSvid(int)}
      */
     public int getSvid() {
         return mSvid;
     }
 
     /**
-     * Sets the Pseud-random number.
+     * Sets the satellite ID.
      * @hide
      */
     @TestApi
@@ -187,10 +233,25 @@
     }
 
     /**
-     * Gets the Message Identifier.
-     * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A
-     * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message.
-     * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1.
+     * Gets the Message identifier.
+     *
+     * <p>This provides an index to help with complete Navigation Message assembly. Similar
+     * identifiers within the data bits themselves often supplement this information, in ways even
+     * more specific to each message type; see the relevant satellite constellation ICDs for
+     * details.
+     *
+     * <ul>
+     * <li> For GPS L1 C/A subframe 4 and 5, this value corresponds to the 'frame id' of the
+     * navigation message, in the range of 1-25 (Subframe 1, 2, 3 does not contain a 'frame id' and
+     * this value can be set to -1.)</li>
+     * <li> For Glonass L1 C/A, this refers to the frame ID, in the range of 1-5.</li>
+     * <li> For BeiDou D1, this refers to the frame number in the range of 1-24</li>
+     * <li> For Beidou D2, this refers to the frame number, in the range of 1-120</li>
+     * <li> For Galileo F/NAV nominal frame structure, this refers to the subframe number, in the
+     * range of 1-12</li>
+     * <li> For Galileo I/NAV nominal frame structure, this refers to the subframe number in the
+     * range of 1-24</li>
+     * </ul>
      */
     public int getMessageId() {
         return mMessageId;
@@ -206,10 +267,18 @@
     }
 
     /**
-     * Gets the Sub-message Identifier.
-     * If required by {@link #getType()}, this value contains a sub-index within the current message
-     * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds
-     * to the sub-frame Id of the navigation message.
+     * Gets the sub-message identifier, relevant to the {@link #getType()} of the message.
+     *
+     * <ul>
+     * <li> For GPS L1 C/A, BeiDou D1 &amp; BeiDou D2, the submessage id corresponds to the subframe
+     * number of the navigation message, in the range of 1-5.</li>
+     * <li>For Glonass L1 C/A, this refers to the String number, in the range from 1-15</li>
+     * <li>For Galileo F/NAV, this refers to the page type in the range 1-6</li>
+     * <li>For Galileo I/NAV, this refers to the word type in the range 1-10+</li>
+     * <li>For Galileo in particular, the type information embedded within the data bits may be even
+     * more useful in interpretation, than the nominal page and word types provided in this
+     * field.</li>
+     * </ul>
      */
     public int getSubmessageId() {
         return mSubmessageId;
@@ -225,8 +294,25 @@
     }
 
     /**
-     * Gets the data associated with the Navigation Message.
-     * The bytes (or words) specified using big endian format (MSB first).
+     * Gets the data of the reported GPS message.
+     *
+     * <p>The bytes (or words) specified using big endian format (MSB first).
+     *
+     * <ul>
+     * <li>For GPS L1 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
+     * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
+     * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
+     * respectively.</li>
+     * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum.  These
+     * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
+     * seconds.</li>
+     * <li>For Galileo F/NAV, each word consists of 238-bit (sync &amp; tail symbols excluded). Each
+     * word should be fit into 30-bytes, with MSB first (skip B239, B240), covering a time period of
+     * 10 seconds.</li>
+     * <li>For Galileo I/NAV, each page contains 2 page parts, even and odd, with a total of 2x114 =
+     * 228 bits, (sync &amp; tail excluded) that should be fit into 29 bytes, with MSB first (skip
+     * B229-B232).</li>
+     * </ul>
      */
     @NonNull
     public byte[] getData() {
diff --git a/location/java/android/location/GnssNavigationMessageCallbackTransport.java b/location/java/android/location/GnssNavigationMessageCallbackTransport.java
index 4204b99..1eafd02 100644
--- a/location/java/android/location/GnssNavigationMessageCallbackTransport.java
+++ b/location/java/android/location/GnssNavigationMessageCallbackTransport.java
@@ -20,12 +20,12 @@
 import android.os.RemoteException;
 
 /**
- * A handler class to manage transport callback for {@link GnssNavigationMessageEvent.Callback}.
+ * A handler class to manage transport callback for {@link GnssNavigationMessage.Callback}.
  *
  * @hide
  */
 class GnssNavigationMessageCallbackTransport
-        extends LocalListenerHelper<GnssNavigationMessageEvent.Callback> {
+        extends LocalListenerHelper<GnssNavigationMessage.Callback> {
     private final ILocationManager mLocationManager;
 
     private final IGnssNavigationMessageListener mListenerTransport = new ListenerTransport();
@@ -51,11 +51,11 @@
 
     private class ListenerTransport extends IGnssNavigationMessageListener.Stub {
         @Override
-        public void onGnssNavigationMessageReceived(final GnssNavigationMessageEvent event) {
-            ListenerOperation<GnssNavigationMessageEvent.Callback> operation =
-                    new ListenerOperation<GnssNavigationMessageEvent.Callback>() {
+        public void onGnssNavigationMessageReceived(final GnssNavigationMessage event) {
+            ListenerOperation<GnssNavigationMessage.Callback> operation =
+                    new ListenerOperation<GnssNavigationMessage.Callback>() {
                 @Override
-                public void execute(GnssNavigationMessageEvent.Callback callback)
+                public void execute(GnssNavigationMessage.Callback callback)
                         throws RemoteException {
                     callback.onGnssNavigationMessageReceived(event);
                 }
@@ -65,10 +65,10 @@
 
         @Override
         public void onStatusChanged(final int status) {
-            ListenerOperation<GnssNavigationMessageEvent.Callback> operation =
-                    new ListenerOperation<GnssNavigationMessageEvent.Callback>() {
+            ListenerOperation<GnssNavigationMessage.Callback> operation =
+                    new ListenerOperation<GnssNavigationMessage.Callback>() {
                 @Override
-                public void execute(GnssNavigationMessageEvent.Callback callback)
+                public void execute(GnssNavigationMessage.Callback callback)
                         throws RemoteException {
                     callback.onStatusChanged(status);
                 }
diff --git a/location/java/android/location/GnssNavigationMessageEvent.java b/location/java/android/location/GnssNavigationMessageEvent.java
index 992dfc3..f7e5665 100644
--- a/location/java/android/location/GnssNavigationMessageEvent.java
+++ b/location/java/android/location/GnssNavigationMessageEvent.java
@@ -28,10 +28,11 @@
 /**
  * A class implementing a container for data associated with a navigation message event.
  * Events are delivered to registered instances of {@link Callback}.
+ * @removed
  */
 public final class GnssNavigationMessageEvent implements Parcelable {
     /**
-     * The status of GPS measurements event.
+     * The status of GNSS measurements event.
      * @hide
      */
     @Retention(RetentionPolicy.SOURCE)
@@ -39,38 +40,40 @@
     public @interface GnssNavigationMessageStatus {}
 
     /**
-     * The system does not support tracking of GPS Navigation Messages. This status will not change
-     * in the future.
+     * The system does not support tracking of GNSS Navigation Messages.
+     *
+     * This status will not change in the future.
      */
     public static final int STATUS_NOT_SUPPORTED = 0;
 
     /**
-     * GPS Navigation Messages are successfully being tracked, it will receive updates once they are
-     * available.
+     * GNSS Navigation Messages are successfully being tracked, it will receive updates once they
+     * are available.
      */
     public static final int STATUS_READY = 1;
 
     /**
-     * GPS provider or Location is disabled, updated will not be received until they are enabled.
+     * GNSS provider or Location is disabled, updated will not be received until they are enabled.
      */
     public static final int STATUS_GNSS_LOCATION_DISABLED = 2;
 
     private final GnssNavigationMessage mNavigationMessage;
 
     /**
-     * Used for receiving GPS satellite Navigation Messages from the GPS engine.
-     * You can implement this interface and call
+     * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
+     *
+     * <p>You can implement this interface and call
      * {@link LocationManager#registerGnssNavigationMessageCallback}.
      */
     public static abstract class Callback {
 
         /**
-         * Returns the latest collected GPS Navigation Message.
+         * Returns the latest collected GNSS Navigation Message.
          */
         public void onGnssNavigationMessageReceived(GnssNavigationMessageEvent event) {}
 
         /**
-         * Returns the latest status of the GPS Navigation Messages sub-system.
+         * Returns the latest status of the GNSS Navigation Messages sub-system.
          */
         public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
     }
diff --git a/location/java/android/location/GnssNmeaListener.java b/location/java/android/location/GnssNmeaListener.java
index 6c9b08a..756ae49 100644
--- a/location/java/android/location/GnssNmeaListener.java
+++ b/location/java/android/location/GnssNmeaListener.java
@@ -23,8 +23,9 @@
 * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
 * You can implement this interface and call {@link LocationManager#addNmeaListener}
 * to receive NMEA data from the GNSS engine.
+* @removed
 */
 public interface GnssNmeaListener {
     /** Called when an NMEA message is received. */
     void onNmeaReceived(long timestamp, String nmea);
-}
\ No newline at end of file
+}
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index d76feec..e834c30 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -23,9 +23,11 @@
 
 /**
  * This class represents the current state of the GNSS engine.
- * This class is used in conjunction with the {@link GnssStatusCallback}.
+ * This class is used in conjunction with the {@link GnssStatus.Callback}.
  */
 public final class GnssStatus {
+    // these must match the definitions in gps.h
+
     /** Unknown constellation type. */
     public static final int CONSTELLATION_UNKNOWN = 0;
     /** Constellation type constant for GPS. */
@@ -41,16 +43,6 @@
     /** Constellation type constant for Galileo. */
     public static final int CONSTELLATION_GALILEO = 6;
 
-    /**
-     * Constellation type.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({CONSTELLATION_UNKNOWN, CONSTELLATION_GPS, CONSTELLATION_SBAS, CONSTELLATION_GLONASS,
-            CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO})
-    public @interface ConstellationType {}
-
-    // these must match the definitions in gps.h
     /** @hide */
     public static final int GNSS_SV_FLAGS_NONE = 0;
     /** @hide */
@@ -67,6 +59,42 @@
     /** @hide */
     public static final int CONSTELLATION_TYPE_MASK = 0xf;
 
+    /**
+     * Used for receiving notifications when GNSS events happen.
+     */
+    public static abstract class Callback {
+        /**
+         * Called when GNSS system has started.
+         */
+        public void onStarted() {}
+
+        /**
+         * Called when GNSS system has stopped.
+         */
+        public void onStopped() {}
+
+        /**
+         * Called when the GNSS system has received its first fix since starting.
+         * @param ttffMillis the time from start to first fix in milliseconds.
+         */
+        public void onFirstFix(int ttffMillis) {}
+
+        /**
+         * Called periodically to report GNSS satellite status.
+         * @param status the current status of all satellites.
+         */
+        public void onSatelliteStatusChanged(GnssStatus status) {}
+    }
+
+    /**
+     * Constellation type.
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({CONSTELLATION_UNKNOWN, CONSTELLATION_GPS, CONSTELLATION_SBAS, CONSTELLATION_GLONASS,
+            CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO})
+    public @interface ConstellationType {}
+
     /* These package private values are modified by the LocationManager class */
     /* package */ int[] mSvidWithFlags;
     /* package */ float[] mCn0DbHz;
@@ -83,15 +111,21 @@
         mAzimuths = azimuths;
     }
 
+    /** @removed */
+    public int getNumSatellites() {
+        return getSatelliteCount();
+    }
+
     /**
      * Gets the total number of satellites in satellite list.
      */
-    public int getNumSatellites() {
+    public int getSatelliteCount() {
         return mSvCount;
     }
 
     /**
-     * Retrieves the constellation type of the satellite at the specified position.
+     * Retrieves the constellation type of the satellite at the specified index.
+     *
      * @param satIndex the index of the satellite in the list.
      */
     @ConstellationType
@@ -101,7 +135,30 @@
     }
 
     /**
-     * Retrieves the pseudo-random number of the satellite at the specified position.
+     * Gets the identification number for the satellite at the specific index.
+     *
+     * <p>This svid is pseudo-random number for most constellations. It is FCN &amp; OSN number for
+     * Glonass.
+     *
+     * <p>The distinction is made by looking at constellation field
+     * {@link #getConstellationType(int)} Expected values are in the range of:
+     *
+     * <ul>
+     * <li>GPS: 1-32</li>
+     * <li>SBAS: 120-151, 183-192</li>
+     * <li>GLONASS:
+     * <ul>
+     *   <li>The least significant 8 bits, signed, are the orbital slot number (OSN) in the range
+     *   from 1-24, if known, or -127 if unknown</li>
+     *   <li>The next least signficant 8 bits, signed, are the frequency channel number (FCN) in the
+     *   range from -7 to +6, if known, and -127, if unknown</li>
+     *   <li>At least one of the two (FCN &amp; OSN) shall be set to a known value</li>
+     * </ul></li>
+     * <li>QZSS: 193-200</li>
+     * <li>Galileo: 1-36</li>
+     * <li>Beidou: 1-37</li>
+     * </ul>
+     *
      * @param satIndex the index of the satellite in the list.
      */
     public int getSvid(int satIndex) {
@@ -109,7 +166,9 @@
     }
 
     /**
-     * Retrieves the signal-noise ration of the satellite at the specified position.
+     * Retrieves the carrier-to-noise density at the antenna of the satellite at the specified index
+     * in dB-Hz.
+     *
      * @param satIndex the index of the satellite in the list.
      */
     public float getCn0DbHz(int satIndex) {
@@ -117,7 +176,8 @@
     }
 
     /**
-     * Retrieves the elevation of the satellite at the specified position.
+     * Retrieves the elevation of the satellite at the specified index.
+     *
      * @param satIndex the index of the satellite in the list.
      */
     public float getElevationDegrees(int satIndex) {
@@ -125,31 +185,46 @@
     }
 
     /**
-     * Retrieves the azimuth the satellite at the specified position.
+     * Retrieves the azimuth the satellite at the specified index.
+     *
      * @param satIndex the index of the satellite in the list.
      */
     public float getAzimuthDegrees(int satIndex) {
         return mAzimuths[satIndex];
     }
 
-    /**
-     * Detects whether the satellite at the specified position has ephemeris data.
-     * @param satIndex the index of the satellite in the list.
-     */
+    /** @removed */
     public boolean hasEphemeris(int satIndex) {
-        return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+        return hasEphemerisData(satIndex);
     }
 
     /**
-     * Detects whether the satellite at the specified position has almanac data.
+     * Reports whether the satellite at the specified index has ephemeris data.
+     *
      * @param satIndex the index of the satellite in the list.
      */
+    public boolean hasEphemerisData(int satIndex) {
+        return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+    }
+
+    /** @removed */
     public boolean hasAlmanac(int satIndex) {
+        return hasAlmanacData(satIndex);
+    }
+
+    /**
+     * Reports whether the satellite at the specified index has almanac data.
+     *
+     * @param satIndex the index of the satellite in the list.
+     */
+    public boolean hasAlmanacData(int satIndex) {
         return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
     }
 
     /**
-     * Detects whether the satellite at the specified position is used in fix.
+     * Reports whether the satellite at the specified index was used in the calculation of the most
+     * recent position fix.
+     *
      * @param satIndex the index of the satellite in the list.
      */
     public boolean usedInFix(int satIndex) {
diff --git a/location/java/android/location/GnssStatusCallback.java b/location/java/android/location/GnssStatusCallback.java
index 0d2955a..bf295ef 100644
--- a/location/java/android/location/GnssStatusCallback.java
+++ b/location/java/android/location/GnssStatusCallback.java
@@ -18,6 +18,7 @@
 
 /**
  * Used for receiving notifications when GNSS events happen.
+ * @removed
  */
 public abstract class GnssStatusCallback {
     /**
diff --git a/location/java/android/location/GpsSatellite.java b/location/java/android/location/GpsSatellite.java
index 820f5746..788d01e 100644
--- a/location/java/android/location/GpsSatellite.java
+++ b/location/java/android/location/GpsSatellite.java
@@ -18,8 +18,12 @@
 
 /**
  * This class represents the current state of a GPS satellite.
+ *
  * This class is used in conjunction with the {@link GpsStatus} class.
+ *
+ * @deprecated use {@link GnssStatus} and {@link GnssStatus.Callback}.
  */
+@Deprecated
 public final class GpsSatellite {
     /* These package private values are modified by the GpsStatus class */
     boolean mValid;
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
index bc518f9..038247b 100644
--- a/location/java/android/location/GpsStatus.java
+++ b/location/java/android/location/GpsStatus.java
@@ -24,8 +24,12 @@
 
 /**
  * This class represents the current state of the GPS engine.
- * This class is used in conjunction with the {@link Listener} interface.
+ *
+ * <p>This class is used in conjunction with the {@link Listener} interface.
+ *
+ * @deprecated use {@link GnssStatus} and {@link GnssStatus.Callback}.
  */
+@Deprecated
 public final class GpsStatus {
     private static final int NUM_SATELLITES = 255;
 
@@ -102,7 +106,9 @@
 
     /**
      * Used for receiving notifications when GPS status has changed.
+     * @deprecated use {@link GnssStatus.Callback} instead.
      */
+    @Deprecated
     public interface Listener {
         /**
          * Called to report changes in the GPS status.
@@ -130,7 +136,9 @@
      * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
      * You can implement this interface and call {@link LocationManager#addNmeaListener}
      * to receive NMEA data from the GPS engine.
+     * @deprecated use {@link OnNmeaMessageListener} instead.
      */
+    @Deprecated
     public interface NmeaListener {
         void onNmeaReceived(long timestamp, String nmea);
     }
diff --git a/location/java/android/location/IGnssNavigationMessageListener.aidl b/location/java/android/location/IGnssNavigationMessageListener.aidl
index de6129c..3e49b5b 100644
--- a/location/java/android/location/IGnssNavigationMessageListener.aidl
+++ b/location/java/android/location/IGnssNavigationMessageListener.aidl
@@ -16,12 +16,12 @@
 
 package android.location;
 
-import android.location.GnssNavigationMessageEvent;
+import android.location.GnssNavigationMessage;
 
 /**
  * {@hide}
  */
 oneway interface IGnssNavigationMessageListener {
-    void onGnssNavigationMessageReceived(in GnssNavigationMessageEvent event);
+    void onGnssNavigationMessageReceived(in GnssNavigationMessage event);
     void onStatusChanged(in int status);
 }
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 28db099..b246360 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -70,10 +70,16 @@
             new HashMap<>();
     private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
             new HashMap<>();
-    private final HashMap<GnssStatusCallback, GnssStatusListenerTransport> mGnssStatusListeners =
+    private final HashMap<GnssStatusCallback, GnssStatusListenerTransport>
+            mOldGnssStatusListeners = new HashMap<>();
+    private final HashMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
             new HashMap<>();
-    private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mGnssNmeaListeners =
+    private final HashMap<GnssNmeaListener, GnssStatusListenerTransport> mOldGnssNmeaListeners =
             new HashMap<>();
+    private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
+            new HashMap<>();
+    private final HashMap<GnssNavigationMessageEvent.Callback, GnssNavigationMessage.Callback>
+            mNavigationMessageBridge = new HashMap<>();
     private GnssStatus mGnssStatus;
     private int mTimeToFirstFix;
 
@@ -1392,8 +1398,10 @@
 
         private final GpsStatus.Listener mGpsListener;
         private final GpsStatus.NmeaListener mGpsNmeaListener;
-        private final GnssStatusCallback mGnssCallback;
-        private final GnssNmeaListener mGnssNmeaListener;
+        private final GnssStatusCallback mOldGnssCallback;
+        private final GnssStatus.Callback mGnssCallback;
+        private final GnssNmeaListener mOldGnssNmeaListener;
+        private final OnNmeaMessageListener mGnssNmeaListener;
 
         private class GnssHandler extends Handler {
             public GnssHandler(Handler handler) {
@@ -1408,7 +1416,7 @@
                             int length = mNmeaBuffer.size();
                             for (int i = 0; i < length; i++) {
                                 Nmea nmea = mNmeaBuffer.get(i);
-                                mGnssNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
+                                mGnssNmeaListener.onNmeaMessage(nmea.mNmea, nmea.mTimestamp);
                             }
                             mNmeaBuffer.clear();
                         }
@@ -1456,7 +1464,8 @@
             mGnssHandler = new GnssHandler(handler);
             mGpsNmeaListener = null;
             mNmeaBuffer = null;
-            mGnssCallback = new GnssStatusCallback() {
+            mOldGnssCallback = null;
+            mGnssCallback = new GnssStatus.Callback() {
                 @Override
                 public void onStarted() {
                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
@@ -1477,6 +1486,7 @@
                     mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
                 }
             };
+            mOldGnssNmeaListener = null;
             mGnssNmeaListener = null;
         }
 
@@ -1489,10 +1499,12 @@
             mGnssHandler = new GnssHandler(handler);
             mGpsNmeaListener = listener;
             mNmeaBuffer = new ArrayList<Nmea>();
+            mOldGnssCallback = null;
             mGnssCallback = null;
-            mGnssNmeaListener = new GnssNmeaListener() {
+            mOldGnssNmeaListener = null;
+            mGnssNmeaListener = new OnNmeaMessageListener() {
                 @Override
-                public void onNmeaReceived(long timestamp, String nmea) {
+                public void onNmeaMessage(String nmea, long timestamp) {
                     mGpsNmeaListener.onNmeaReceived(timestamp, nmea);
                 }
             };
@@ -1503,8 +1515,45 @@
         }
 
         GnssStatusListenerTransport(GnssStatusCallback callback, Handler handler) {
+            mOldGnssCallback = callback;
+            mGnssCallback = new GnssStatus.Callback() {
+                @Override
+                public void onStarted() {
+                    mOldGnssCallback.onStarted();
+                }
+
+                @Override
+                public void onStopped() {
+                    mOldGnssCallback.onStopped();
+                }
+
+                @Override
+                public void onFirstFix(int ttff) {
+                    mOldGnssCallback.onFirstFix(ttff);
+                }
+
+                @Override
+                public void onSatelliteStatusChanged(GnssStatus status) {
+                    mOldGnssCallback.onSatelliteStatusChanged(status);
+                }
+            };
+            mGnssHandler = new GnssHandler(handler);
+            mOldGnssNmeaListener = null;
+            mGnssNmeaListener = null;
+            mNmeaBuffer = null;
+            mGpsListener = null;
+            mGpsNmeaListener = null;
+        }
+
+        GnssStatusListenerTransport(GnssStatus.Callback callback) {
+            this(callback, null);
+        }
+
+        GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
+            mOldGnssCallback = null;
             mGnssCallback = callback;
             mGnssHandler = new GnssHandler(handler);
+            mOldGnssNmeaListener = null;
             mGnssNmeaListener = null;
             mNmeaBuffer = null;
             mGpsListener = null;
@@ -1517,7 +1566,29 @@
 
         GnssStatusListenerTransport(GnssNmeaListener listener, Handler handler) {
             mGnssCallback = null;
+            mOldGnssCallback = null;
             mGnssHandler = new GnssHandler(handler);
+            mOldGnssNmeaListener = listener;
+            mGnssNmeaListener = new OnNmeaMessageListener() {
+                @Override
+                public void onNmeaMessage(String message, long timestamp) {
+                    mOldGnssNmeaListener.onNmeaReceived(timestamp, message);
+                }
+            };
+            mGpsListener = null;
+            mGpsNmeaListener = null;
+            mNmeaBuffer = new ArrayList<Nmea>();
+        }
+
+        GnssStatusListenerTransport(OnNmeaMessageListener listener) {
+            this(listener, null);
+        }
+
+        GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
+            mOldGnssCallback = null;
+            mGnssCallback = null;
+            mGnssHandler = new GnssHandler(handler);
+            mOldGnssNmeaListener = null;
             mGnssNmeaListener = listener;
             mGpsListener = null;
             mGpsNmeaListener = null;
@@ -1589,7 +1660,7 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @deprecated use {@link #registerGnssStatusCallback(GnssStatusCallback)} instead.
+     * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead.
      */
     @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1617,6 +1688,7 @@
      * Removes a GPS status listener.
      *
      * @param listener GPS status listener object to remove
+     * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead.
      */
     @Deprecated
     public void removeGpsStatusListener(GpsStatus.Listener listener) {
@@ -1630,7 +1702,6 @@
         }
     }
 
-
     /**
      * Registers a GNSS status listener.
      *
@@ -1639,6 +1710,7 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @removed
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssStatusCallback(GnssStatusCallback callback) {
@@ -1654,10 +1726,73 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @removed
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssStatusCallback(GnssStatusCallback callback, Handler handler) {
         boolean result;
+        if (mOldGnssStatusListeners.get(callback) != null) {
+            // listener is already registered
+            return true;
+        }
+        try {
+            GnssStatusListenerTransport transport =
+                    new GnssStatusListenerTransport(callback, handler);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+            if (result) {
+                mOldGnssStatusListeners.put(callback, transport);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return result;
+    }
+
+    /**
+     * Removes a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to remove
+     * @removed
+     */
+    public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
+        try {
+            GnssStatusListenerTransport transport = mOldGnssStatusListeners.remove(callback);
+            if (transport != null) {
+                mService.unregisterGnssStatusCallback(transport);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Registers a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to register
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGnssStatusCallback(GnssStatus.Callback callback) {
+        return registerGnssStatusCallback(callback, null);
+    }
+
+    /**
+     * Registers a GNSS status listener.
+     *
+     * @param callback GNSS status listener object to register
+     * @param handler the handler that the callback runs on.
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGnssStatusCallback(GnssStatus.Callback callback, Handler handler) {
+        boolean result;
         if (mGnssStatusListeners.get(callback) != null) {
             // listener is already registered
             return true;
@@ -1681,7 +1816,7 @@
      *
      * @param callback GNSS status listener object to remove
      */
-    public void unregisterGnssStatusCallback(GnssStatusCallback callback) {
+    public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
         try {
             GnssStatusListenerTransport transport = mGnssStatusListeners.remove(callback);
             if (transport != null) {
@@ -1700,7 +1835,7 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
-     * @deprecated use {@link #addNmeaListener(GnssNmeaListener)} instead.
+     * @deprecated use {@link #addNmeaListener(OnNmeaMessageListener)} instead.
      */
     @Deprecated
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1728,6 +1863,7 @@
      * Removes an NMEA listener.
      *
      * @param listener a {@link GpsStatus.NmeaListener} object to remove
+     * @deprecated use {@link #removeNmeaListener(OnNmeaMessageListener)} instead.
      */
     @Deprecated
     public void removeNmeaListener(GpsStatus.NmeaListener listener) {
@@ -1749,6 +1885,7 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @removed
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean addNmeaListener(GnssNmeaListener listener) {
@@ -1764,6 +1901,7 @@
      * @return true if the listener was successfully added
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     * @removed
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean addNmeaListener(GnssNmeaListener listener, Handler handler) {
@@ -1778,6 +1916,69 @@
                     new GnssStatusListenerTransport(listener, handler);
             result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
             if (result) {
+                mOldGnssNmeaListeners.put(listener, transport);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return result;
+    }
+
+    /**
+     * Removes an NMEA listener.
+     *
+     * @param listener a {@link GnssNmeaListener} object to remove
+     * @removed
+     */
+    public void removeNmeaListener(GnssNmeaListener listener) {
+        try {
+            GnssStatusListenerTransport transport = mOldGnssNmeaListeners.remove(listener);
+            if (transport != null) {
+                mService.unregisterGnssStatusCallback(transport);
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Adds an NMEA listener.
+     *
+     * @param listener a {@link OnNmeaMessageListener} object to register
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean addNmeaListener(OnNmeaMessageListener listener) {
+        return addNmeaListener(listener, null);
+    }
+
+    /**
+     * Adds an NMEA listener.
+     *
+     * @param listener a {@link OnNmeaMessageListener} object to register
+     * @param handler the handler that the listener runs on.
+     *
+     * @return true if the listener was successfully added
+     *
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean addNmeaListener(OnNmeaMessageListener listener, Handler handler) {
+        boolean result;
+
+        if (mGpsNmeaListeners.get(listener) != null) {
+            // listener is already registered
+            return true;
+        }
+        try {
+            GnssStatusListenerTransport transport =
+                    new GnssStatusListenerTransport(listener, handler);
+            result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+            if (result) {
                 mGnssNmeaListeners.put(listener, transport);
             }
         } catch (RemoteException e) {
@@ -1790,9 +1991,9 @@
     /**
      * Removes an NMEA listener.
      *
-     * @param listener a {@link GnssNmeaListener} object to remove
+     * @param listener a {@link OnNmeaMessageListener} object to remove
      */
-    public void removeNmeaListener(GnssNmeaListener listener) {
+    public void removeNmeaListener(OnNmeaMessageListener listener) {
         try {
             GnssStatusListenerTransport transport = mGnssNmeaListeners.remove(listener);
             if (transport != null) {
@@ -1843,7 +2044,8 @@
      * No-op method to keep backward-compatibility.
      * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead.
      * @hide
-     * @deprecated
+     * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
+     * instead.
      */
     @Deprecated
     @SystemApi
@@ -1872,10 +2074,23 @@
     }
 
     /**
-     * Registers a GPS Navigation Message callback.
+     * No-op method to keep backward-compatibility.
+     * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
+     * @hide
+     * @deprecated use {@link #unregisterGnssNavigationMessageCallback(GnssMeasurements.Callback)}
+     * instead
+     */
+    @Deprecated
+    @SystemApi
+    public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
+    }
+
+    /**
+     * Registers a GNSS Navigation Message callback.
      *
      * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * @removed
      */
     public boolean registerGnssNavigationMessageCallback(
             GnssNavigationMessageEvent.Callback callback) {
@@ -1883,40 +2098,80 @@
     }
 
     /**
-     * Registers a GPS Navigation Message callback.
+     * Registers a GNSS Navigation Message callback.
      *
      * @param callback a {@link GnssNavigationMessageEvent.Callback} object to register.
      * @param handler the handler that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * @removed
+     */
+    @RequiresPermission(ACCESS_FINE_LOCATION)
+    public boolean registerGnssNavigationMessageCallback(
+            final GnssNavigationMessageEvent.Callback callback, Handler handler) {
+        GnssNavigationMessage.Callback bridge = new GnssNavigationMessage.Callback() {
+            @Override
+            public void onGnssNavigationMessageReceived(GnssNavigationMessage message) {
+                GnssNavigationMessageEvent event = new GnssNavigationMessageEvent(message);
+                callback.onGnssNavigationMessageReceived(event);
+            }
+
+            @Override
+            public void onStatusChanged(int status) {
+                callback.onStatusChanged(status);
+            }
+        };
+        mNavigationMessageBridge.put(callback, bridge);
+        return mGnssNavigationMessageCallbackTransport.add(bridge, handler);
+    }
+
+    /**
+     * Unregisters a GNSS Navigation Message callback.
+     *
+     * @param callback a {@link GnssNavigationMessageEvent.Callback} object to remove.
+     * @removed
+     */
+    public void unregisterGnssNavigationMessageCallback(
+            GnssNavigationMessageEvent.Callback callback) {
+        mGnssNavigationMessageCallbackTransport.remove(
+                mNavigationMessageBridge.remove(
+                        callback));
+    }
+
+    /**
+     * Registers a GNSS Navigation Message callback.
+     *
+     * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     */
+    public boolean registerGnssNavigationMessageCallback(
+            GnssNavigationMessage.Callback callback) {
+        return registerGnssNavigationMessageCallback(callback, null);
+    }
+
+    /**
+     * Registers a GNSS Navigation Message callback.
+     *
+     * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+     * @param handler the handler that the callback runs on.
+     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssNavigationMessageCallback(
-            GnssNavigationMessageEvent.Callback callback, Handler handler) {
+            GnssNavigationMessage.Callback callback, Handler handler) {
         return mGnssNavigationMessageCallbackTransport.add(callback, handler);
     }
 
     /**
-     * Unregisters a GPS Navigation Message callback.
+     * Unregisters a GNSS Navigation Message callback.
      *
-     * @param callback a {@link GnssNavigationMessageEvent.Callback} object to remove.
+     * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
      */
     public void unregisterGnssNavigationMessageCallback(
-            GnssNavigationMessageEvent.Callback callback) {
+            GnssNavigationMessage.Callback callback) {
         mGnssNavigationMessageCallbackTransport.remove(callback);
     }
 
     /**
-     * No-op method to keep backward-compatibility.
-     * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
-     * @hide
-     * @deprecated
-     */
-    @Deprecated
-    @SystemApi
-    public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
-    }
-
-    /**
      * Retrieves information about the current status of the GPS engine.
      * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
      * callback to ensure that the data is copied atomically.
diff --git a/location/java/android/location/OnNmeaMessageListener.java b/location/java/android/location/OnNmeaMessageListener.java
new file mode 100644
index 0000000..ccf6ce8
--- /dev/null
+++ b/location/java/android/location/OnNmeaMessageListener.java
@@ -0,0 +1,34 @@
+/*
+ * 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.location;
+
+/**
+* Used for receiving NMEA sentences from the GNSS.
+* NMEA 0183 is a standard for communicating with marine electronic devices
+* and is a common method for receiving data from a GNSS, typically over a serial port.
+* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+* You can implement this interface and call {@link LocationManager#addNmeaListener}
+* to receive NMEA data from the GNSS engine.
+*/
+public interface OnNmeaMessageListener {
+    /**
+     * Called when an NMEA message is received.
+     * @param message NMEA message
+     * @param timestamp milliseconds since January 1, 1970.
+     */
+    void onNmeaMessage(String message, long timestamp);
+}
diff --git a/media/java/android/media/DrmInitData.java b/media/java/android/media/DrmInitData.java
index 06fe6ff..170d9de 100644
--- a/media/java/android/media/DrmInitData.java
+++ b/media/java/android/media/DrmInitData.java
@@ -28,6 +28,12 @@
 public abstract class DrmInitData {
 
     /**
+     * Prevent public constuctor access
+     */
+    /* package private */ DrmInitData() {
+    }
+
+    /**
      * Retrieves initialization data for a given DRM scheme, specified by its UUID.
      *
      * @param schemeUuid The DRM scheme's UUID.
diff --git a/media/java/android/media/tv/TvRecordingClient.java b/media/java/android/media/tv/TvRecordingClient.java
index a5ff29f..2613376 100644
--- a/media/java/android/media/tv/TvRecordingClient.java
+++ b/media/java/android/media/tv/TvRecordingClient.java
@@ -158,7 +158,7 @@
      * <p>The application may supply the URI for a TV program for filling in program specific data
      * fields in the {@link android.media.tv.TvContract.RecordedPrograms} table.
      * A non-null {@code programHint} implies the started recording should be of that specific
-     * program, whereas null {@code programHint} does not impose such a requirement and the
+     * program, whereas null {@code programUri} does not impose such a requirement and the
      * recording can span across multiple TV programs. In either case, the application must call
      * {@link TvRecordingClient#stopRecording()} to stop the recording.
      *
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index 3814630..61fbfb9 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -23,12 +23,14 @@
 public class MtpServer implements Runnable {
 
     private long mNativeContext; // accessed by native methods
+    private final MtpDatabase mDatabase;
 
     static {
         System.loadLibrary("media_jni");
     }
 
     public MtpServer(MtpDatabase database, boolean usePtp) {
+        mDatabase = database;
         native_setup(database, usePtp);
         database.setServer(this);
     }
@@ -42,6 +44,7 @@
     public void run() {
         native_run();
         native_cleanup();
+        mDatabase.close();
     }
 
     public void sendObjectAdded(int handle) {
diff --git a/media/jni/android_media_MediaDataSource.cpp b/media/jni/android_media_MediaDataSource.cpp
index 537b56d..d07942b 100644
--- a/media/jni/android_media_MediaDataSource.cpp
+++ b/media/jni/android_media_MediaDataSource.cpp
@@ -155,4 +155,8 @@
     return 0;
 }
 
+String8 JMediaDataSource::toString() {
+    return String8::format("JMediaDataSource(pid %d, uid %d)", getpid(), getuid());
+}
+
 }  // namespace android
diff --git a/media/jni/android_media_MediaDataSource.h b/media/jni/android_media_MediaDataSource.h
index 34624eb..378baf4 100644
--- a/media/jni/android_media_MediaDataSource.h
+++ b/media/jni/android_media_MediaDataSource.h
@@ -46,6 +46,7 @@
     virtual status_t getSize(off64_t* size);
     virtual void close();
     virtual uint32_t getFlags();
+    virtual String8 toString();
 
 private:
     // Protect all member variables with mLock because this object will be
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 5722cb0..39f2a32 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -882,7 +882,7 @@
                 break;
             }
 
-            info.mThumbCompressedSize = image_data.thumbnail_length;
+            info.mThumbCompressedSize = image_data.thumbnail.length;
             info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
             info.mImagePixWidth = image_data.full_width;
             info.mImagePixHeight = image_data.full_height;
@@ -932,19 +932,19 @@
                     break;
                 }
 
-                if (image_data.thumbnail_length == 0) {
+                if (image_data.thumbnail.length == 0) {
                     // No thumbnail.
                     break;
                 }
 
-                result = malloc(image_data.thumbnail_length);
+                result = malloc(image_data.thumbnail.length);
                 if (result) {
                     piex::Error err = stream.get()->GetData(
-                            image_data.thumbnail_offset,
-                            image_data.thumbnail_length,
+                            image_data.thumbnail.offset,
+                            image_data.thumbnail.length,
                             (std::uint8_t *)result);
                     if (err == piex::Error::kOk) {
-                        outThumbSize = image_data.thumbnail_length;
+                        outThumbSize = image_data.thumbnail.length;
                     } else {
                         free(result);
                     }
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 14609b2..69912ab 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -43,9 +43,9 @@
 
         <activity
             android:name=".LauncherActivity"
-            android:theme="@android:style/Theme.NoDisplay"
-            android:icon="@mipmap/ic_launcher_download"
-            android:label="@string/downloads_label">
+            android:label="@string/downloads_label"
+            android:icon="@mipmap/ic_launcher_downloads"
+            android:theme="@android:style/Theme.NoDisplay">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
@@ -54,23 +54,23 @@
 
         <activity
             android:name=".FilesActivity"
-            android:theme="@style/DocumentsTheme"
-            android:icon="@mipmap/ic_launcher_download"
             android:label="@string/downloads_label"
-            android:documentLaunchMode="intoExisting">
+            android:icon="@mipmap/ic_launcher_downloads"
+            android:documentLaunchMode="intoExisting"
+            android:theme="@style/DocumentsTheme">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
             </intent-filter>
             <intent-filter>
-                <action android:name="android.intent.action.VIEW_DOWNLOADS" />
-                <category android:name="android.intent.category.DEFAULT" />
-            </intent-filter>
-            <intent-filter>
                 <action android:name="android.provider.action.BROWSE" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="vnd.android.document/root" />
             </intent-filter>
             <intent-filter>
+                <action android:name="android.intent.action.VIEW_DOWNLOADS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="application/zip"
diff --git a/packages/DocumentsUI/res/drawable/drag_shadow_background.xml b/packages/DocumentsUI/res/drawable/drag_shadow_background.xml
new file mode 100644
index 0000000..49465cb
--- /dev/null
+++ b/packages/DocumentsUI/res/drawable/drag_shadow_background.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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+  <solid android:color="@color/item_doc_background" />
+  <stroke
+      android:width="1dp"
+      android:color="#ff9f9f9f" />
+  <corners
+      android:bottomRightRadius="3dp"
+      android:bottomLeftRadius="3dp"
+      android:topLeftRadius="3dp"
+      android:topRightRadius="3dp"/>
+</shape>
diff --git a/packages/DocumentsUI/res/layout/drag_shadow_layout.xml b/packages/DocumentsUI/res/layout/drag_shadow_layout.xml
new file mode 100644
index 0000000..26613ef
--- /dev/null
+++ b/packages/DocumentsUI/res/layout/drag_shadow_layout.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingStart="8dp"
+    android:paddingEnd="8dp"
+    android:orientation="horizontal"
+    android:gravity="center_vertical|left"
+    android:background="@drawable/drag_shadow_background">
+
+    <ImageView
+        android:id="@android:id/icon"
+        android:layout_width="@dimen/root_icon_size"
+        android:layout_height="@dimen/root_icon_size"
+        android:scaleType="centerInside"
+        android:contentDescription="@null"
+        android:duplicateParentState="true"/>
+
+    <TextView
+        android:id="@android:id/title"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:textAlignment="viewStart"
+        android:textColor="@color/item_title"
+        android:paddingStart="8dp"/>
+
+</LinearLayout>
diff --git a/packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-hdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-mdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png b/packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_downloads.png
similarity index 100%
rename from packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_download.png
rename to packages/DocumentsUI/res/mipmap-xxxhdpi/ic_launcher_downloads.png
Binary files differ
diff --git a/packages/DocumentsUI/res/values/colors.xml b/packages/DocumentsUI/res/values/colors.xml
index 1660e26..cf0643d 100644
--- a/packages/DocumentsUI/res/values/colors.xml
+++ b/packages/DocumentsUI/res/values/colors.xml
@@ -31,6 +31,7 @@
     <color name="accent">@*android:color/accent_material_light</color>
     <color name="accent_dark">@*android:color/accent_material_dark</color>
     <color name="action_mode">@color/material_grey_400</color>
+    <color name="status_bar_color">@*android:color/material_blue_grey_950</color>
 
     <color name="band_select_background">#88ffffff</color>
     <color name="band_select_border">#44000000</color>
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index 6590bbe..f0cab08 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -20,13 +20,15 @@
 
     <!-- Intentionally unset. Vendors should set this in an overlay. -->
     <string name="trusted_quick_viewer_package" translatable="false"></string>
+
+    <!-- overridden for RTL langs -->
     <bool name="list_divider_inset_left">true</bool>
-    <!-- Indicates if the home directory should be hidden in the roots list, that is presented
-         in the drawer/left side panel ) -->
-    <bool name="home_root_hidden">true</bool>
-    <!-- Indicates if the advanced roots like internal storage should be shown in the roots list.
-         When enabled there is no menu option to toggle internal storage visibility. -->
-    <bool name="advanced_roots_shown">false</bool>
+
+    <!-- Flags setup as productivity oriented in which case Downloads app will be presented
+             as Files app. Including showing of the Documents and "advanced" roots. -->
+    <bool name="productivity_device">false</bool>
+
     <!-- Indicates if search view is taking the whole toolbar space -->
     <bool name="full_bar_search_view">true</bool>
+
 </resources>
diff --git a/packages/DocumentsUI/res/values/dimens.xml b/packages/DocumentsUI/res/values/dimens.xml
index 5af7da3..e682994 100644
--- a/packages/DocumentsUI/res/values/dimens.xml
+++ b/packages/DocumentsUI/res/values/dimens.xml
@@ -38,4 +38,8 @@
     <dimen name="drag_shadow_size">120dp</dimen>
     <dimen name="grid_item_elevation">2dp</dimen>
     <dimen name="max_drawer_width">280dp</dimen>
+
+    <dimen name="drag_shadow_width">160dp</dimen>
+    <dimen name="drag_shadow_height">48dp</dimen>
+
 </resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index fb557ca..eb99a0d 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -18,9 +18,6 @@
     <!-- Title of the documents application [CHAR LIMIT=32] -->
     <string name="app_label">Documents</string>
 
-    <!-- Title of the standalone files activity. [CHAR LIMIT=32] -->
-    <string name="files_label">Files</string>
-
     <!-- Title of the standalone downloads activity. [CHAR LIMIT=32] -->
     <string name="downloads_label">Downloads</string>
 
@@ -223,6 +220,12 @@
         <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> selected</item>
     </plurals>
 
+    <!-- Label text showing user how many items are being dragged. Can be one or more elements. -->
+    <plurals name="elements_dragged">
+        <item quantity="one"><xliff:g id="count" example="1">%1$d</xliff:g> item</item>
+        <item quantity="other"><xliff:g id="count" example="3">%1$d</xliff:g> items</item>
+    </plurals>
+
     <!-- Dialog text shown to users when asking if they want to delete a file (a confirmation) -->
     <string name="delete_filename_confirmation_message">Delete \"<xliff:g id="name" example="cat.jpg">%1$s</xliff:g>\"?</string>
     <!-- Dialog text shown to users when asking if they want to delete a folder (a confirmation) -->
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index b0996aa..9f09ebc 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -30,6 +30,7 @@
         <item name="android:colorAccent">@color/accent</item>
         <item name="colorActionMode">@color/action_mode</item>
         <item name="android:queryBackground">@color/menu_search_background</item>
+        <item name="android:statusBarColor">@color/status_bar_color</item>
 
         <item name="android:listDivider">@*android:drawable/list_divider_material</item>
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
index d6c742a..3b0be57 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/BaseActivity.java
@@ -19,10 +19,9 @@
 import static com.android.documentsui.Shared.DEBUG;
 import static com.android.documentsui.Shared.EXTRA_BENCHMARK;
 import static com.android.documentsui.State.ACTION_CREATE;
+import static com.android.documentsui.State.ACTION_GET_CONTENT;
 import static com.android.documentsui.State.ACTION_OPEN;
 import static com.android.documentsui.State.ACTION_OPEN_TREE;
-import static com.android.documentsui.State.ACTION_GET_CONTENT;
-import static com.android.documentsui.State.ACTION_PICK_COPY_DESTINATION;
 import static com.android.documentsui.State.MODE_GRID;
 
 import android.app.Activity;
@@ -37,14 +36,12 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.MessageQueue;
 import android.os.MessageQueue.IdleHandler;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Root;
 import android.support.annotation.CallSuper;
 import android.support.annotation.LayoutRes;
 import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -104,16 +101,6 @@
     @CallSuper
     @Override
     public void onCreate(Bundle icicle) {
-        // This flag is being set here as a result of the bug. When the flag was set in the
-        // styles.xml keyboard was messing the layout of dialogs (create dir, rename).
-        // Attempts were made to keep the flag in the main theme and to override it in the dialog
-        // layout xml or to create separate style for dialog and assign it in styles.xml.
-        // None of this brought successful results.
-        // Setting the flag works here most probably because of the timing when it is set. Also the
-        // setting might not affect the dialogs that are created in new windows or it affects them
-        // in the different way that having this in the style.
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
-
         // Record the time when onCreate is invoked for metric.
         mStartTime = new Date().getTime();
 
@@ -143,7 +130,6 @@
         mSearchManager = new SearchViewManager(this, icicle);
 
         DocumentsToolbar toolbar = (DocumentsToolbar) findViewById(R.id.toolbar);
-        Display.adjustToolbar(toolbar, this);
         setActionBar(toolbar);
         mNavigator = new NavigationView(
                 mDrawer,
@@ -228,8 +214,7 @@
         includeState(state);
 
         // Advanced roots are shown by deafult without menu option if forced by config or intent.
-        state.showAdvanced = getResources().getBoolean(R.bool.advanced_roots_shown)
-                || intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+        state.showAdvanced = Shared.shouldShowDeviceRoot(this, intent);
         // Menu option is shown for whitelisted intents if advanced roots are not shown by default.
         state.showAdvancedOption = !state.showAdvanced &&
                 (state.action == ACTION_OPEN ||
@@ -272,7 +257,6 @@
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        Metrics.logMenuAction(this, item.getItemId());
 
         switch (item.getItemId()) {
             case android.R.id.home:
@@ -294,6 +278,7 @@
             case R.id.menu_sort_date:
                 setUserSortOrder(State.SORT_ORDER_LAST_MODIFIED);
                 return true;
+
             case R.id.menu_sort_size:
                 setUserSortOrder(State.SORT_ORDER_SIZE);
                 return true;
@@ -322,6 +307,8 @@
                 return true;
 
             case R.id.menu_settings:
+                Metrics.logUserAction(this, Metrics.USER_ACTION_SETTINGS);
+
                 final RootInfo root = getCurrentRoot();
                 final Intent intent = new Intent(DocumentsContract.ACTION_DOCUMENT_ROOT_SETTINGS);
                 intent.setDataAndType(root.getUri(), DocumentsContract.Root.MIME_TYPE_ITEM);
@@ -338,6 +325,8 @@
     }
 
     void showCreateDirectoryDialog() {
+        Metrics.logUserAction(this, Metrics.USER_ACTION_CREATE_DIR);
+
         CreateDirectoryFragment.show(getFragmentManager());
     }
 
@@ -478,19 +467,31 @@
      * Method can be overridden if the change of the behavior of the the child activity is needed.
      */
     public Uri getDefaultRoot() {
-        return Shared.isHomeRootHidden(this)
-                ? DocumentsContract.buildRootUri("com.android.providers.downloads.documents",
-                        "downloads")
-                : DocumentsContract.buildHomeUri();
-            }
+        return Shared.shouldShowDocumentsRoot(this, getIntent())
+                ? DocumentsContract.buildHomeUri()
+                : DocumentsContract.buildRootUri(
+                        "com.android.providers.downloads.documents", "downloads");
+    }
 
+    /**
+     * Set internal storage visible based on explicit user action.
+     */
     void setDisplayAdvancedDevices(boolean display) {
+        Metrics.logUserAction(this,
+                display ? Metrics.USER_ACTION_SHOW_ADVANCED : Metrics.USER_ACTION_HIDE_ADVANCED);
+
         mState.showAdvanced = display;
         RootsFragment.get(getFragmentManager()).onDisplayStateChanged();
         invalidateOptionsMenu();
     }
 
+    /**
+     * Set file size visible based on explicit user action.
+     */
     void setDisplayFileSize(boolean display) {
+        Metrics.logUserAction(this,
+                display ? Metrics.USER_ACTION_SHOW_SIZE : Metrics.USER_ACTION_HIDE_SIZE);
+
         LocalPreferences.setDisplayFileSize(this, display);
         mState.showSize = display;
         DirectoryFragment dir = getDirectoryFragment();
@@ -504,6 +505,18 @@
      * Set state sort order based on explicit user action.
      */
     void setUserSortOrder(int sortOrder) {
+        switch(sortOrder) {
+            case State.SORT_ORDER_DISPLAY_NAME:
+                Metrics.logUserAction(this, Metrics.USER_ACTION_SORT_NAME);
+                break;
+            case State.SORT_ORDER_LAST_MODIFIED:
+                Metrics.logUserAction(this, Metrics.USER_ACTION_SORT_DATE);
+                break;
+            case State.SORT_ORDER_SIZE:
+                Metrics.logUserAction(this, Metrics.USER_ACTION_SORT_SIZE);
+                break;
+        }
+
         mState.userSortOrder = sortOrder;
         DirectoryFragment dir = getDirectoryFragment();
         if (dir != null) {
@@ -515,6 +528,12 @@
      * Set mode based on explicit user action.
      */
     void setViewMode(@ViewMode int mode) {
+        if (mode == State.MODE_GRID) {
+            Metrics.logUserAction(this, Metrics.USER_ACTION_GRID);
+        } else if (mode == State.MODE_LIST) {
+            Metrics.logUserAction(this, Metrics.USER_ACTION_LIST);
+        }
+
         LocalPreferences.setViewMode(this, getCurrentRoot(), mode);
         mState.derivedMode = mode;
 
@@ -636,12 +655,10 @@
                 return true;
             }
         } else if (keyCode == KeyEvent.KEYCODE_TAB) {
-            Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_SWITCH_FOCUS);
             // Tab toggles focus on the navigation drawer.
             toggleNavDrawerFocus();
             return true;
         } else if (keyCode == KeyEvent.KEYCODE_DEL) {
-            Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_BACK);
             popDir();
             return true;
         }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Display.java b/packages/DocumentsUI/src/com/android/documentsui/Display.java
index d46a3ea..8b13222 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Display.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Display.java
@@ -20,8 +20,6 @@
 import android.content.Context;
 import android.graphics.Point;
 import android.util.TypedValue;
-import android.view.WindowManager;
-import android.widget.Toolbar;
 
 /*
  * Convenience class for getting display related attributes
@@ -47,41 +45,12 @@
      * Returns action bar height in raw pixels.
      */
     public static float actionBarHeight(Context context) {
-        int height = 0;
+        int actionBarHeight = 0;
         TypedValue tv = new TypedValue();
         if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
-            height = TypedValue.complexToDimensionPixelSize(tv.data,
+            actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,
                     context.getResources().getDisplayMetrics());
         }
-        return height;
-    }
-
-    /*
-     * Returns status bar height in raw pixels.
-     */
-    private static int statusBarHeight(Context context) {
-        int height = 0;
-        int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen",
-                "android");
-        if (resourceId > 0) {
-            height = context.getResources().getDimensionPixelSize(resourceId);
-        }
-        return height;
-    }
-
-    /*
-     * Adjusts toolbar for the layout with translucent status bar. Increases the
-     * height of the toolbar and adds padding at the top to accommodate status bar visible above
-     * toolbar.
-     */
-    public static void adjustToolbar(Toolbar toolbar, Activity activity) {
-        if ((activity.getWindow().getAttributes().flags
-                & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) != 0) {
-            int statusBarHeight = Display.statusBarHeight(activity);
-            toolbar.getLayoutParams().height = (int) (Display.actionBarHeight(activity)
-                    + statusBarHeight);
-            toolbar.setPadding(toolbar.getPaddingLeft(), statusBarHeight, toolbar.getPaddingRight(),
-                    toolbar.getPaddingBottom());
-        }
+        return actionBarHeight;
     }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
index 5788420..40b54d3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DocumentsActivity.java
@@ -203,8 +203,8 @@
                        mState.action == ACTION_PICK_COPY_DESTINATION) {
                 title = getResources().getString(R.string.title_save);
             } else {
-                // If all else fails, just call it "Downloads".
-                title = getResources().getString(R.string.downloads_label);
+                // If all else fails, just call it "Documents".
+                title = getResources().getString(R.string.app_label);
             }
         }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
index 7a4099a..14e6b69 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/DrawerController.java
@@ -84,7 +84,7 @@
 
         View drawer = activity.findViewById(R.id.drawer_roots);
         Toolbar toolbar = (Toolbar) activity.findViewById(R.id.roots_toolbar);
-        Display.adjustToolbar(toolbar, activity);
+
         drawer.getLayoutParams().width = calculateDrawerWidth(activity);
 
         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
diff --git a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
index f072011..b34af0b 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/FilesActivity.java
@@ -180,7 +180,10 @@
 
     @Override
     public String getDrawerTitle() {
-        return getResources().getString(R.string.downloads_label);
+        Intent intent = getIntent();
+        return (intent != null && intent.hasExtra(Intent.EXTRA_TITLE))
+                ? intent.getStringExtra(Intent.EXTRA_TITLE)
+                : getTitle().toString();
     }
 
     @Override
@@ -225,13 +228,12 @@
             default:
                 return super.onOptionsItemSelected(item);
         }
-
-        Metrics.logMenuAction(this, item.getItemId());
         return true;
     }
 
     private void createNewWindow() {
-        Metrics.logMultiWindow(this);
+        Metrics.logUserAction(this, Metrics.USER_ACTION_NEW_WINDOW);
+
         Intent intent = LauncherActivity.createLaunchIntent(this);
         intent.putExtra(Shared.EXTRA_STACK, (Parcelable) mState.stack);
 
@@ -349,21 +351,18 @@
             case KeyEvent.KEYCODE_A:
                 dir = getDirectoryFragment();
                 if (dir != null) {
-                    Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_SELECT_ALL);
                     dir.selectAllFiles();
                 }
                 return true;
             case KeyEvent.KEYCODE_C:
                 dir = getDirectoryFragment();
                 if (dir != null) {
-                    Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_COPY);
                     dir.copySelectedToClipboard();
                 }
                 return true;
             case KeyEvent.KEYCODE_V:
                 dir = getDirectoryFragment();
                 if (dir != null) {
-                    Metrics.logKeyboardAction(this, Metrics.ACTION_KEYBOARD_PASTE);
                     dir.pasteFromClipboard();
                 }
                 return true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
index 7930c28..5cb6ca3 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/LauncherActivity.java
@@ -87,9 +87,24 @@
         startActivity(intent);
     }
 
-    static final Intent createLaunchIntent(Context context) {
-        Intent intent = new Intent(context, FilesActivity.class);
+    static final Intent createLaunchIntent(Activity activity) {
+        Intent intent = new Intent(activity, FilesActivity.class);
         intent.setData(buildLaunchUri());
+
+        // Relay any config overrides bits present in the original intent.
+        Intent original = activity.getIntent();
+        if (original != null) {
+            if (original.hasExtra(Shared.EXTRA_PRODUCTIVITY_MODE)) {
+                intent.putExtra(
+                        Shared.EXTRA_PRODUCTIVITY_MODE,
+                        original.getBooleanExtra(Shared.EXTRA_PRODUCTIVITY_MODE, false));
+            }
+            if (original.hasExtra(Intent.EXTRA_TITLE)) {
+                intent.putExtra(
+                        Intent.EXTRA_TITLE,
+                        original.getStringExtra(Intent.EXTRA_TITLE));
+            }
+        }
         return intent;
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index 05cc7e6..69a6e1f 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -62,16 +62,13 @@
     private static final String COUNT_GET_CONTENT_MIME = "docsui_get_content_mime";
     private static final String COUNT_BROWSE_ROOT = "docsui_browse_root";
     @Deprecated private static final String COUNT_MANAGE_ROOT = "docsui_manage_root";
-    private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
+    @Deprecated private static final String COUNT_MULTI_WINDOW = "docsui_multi_window";
     private static final String COUNT_FILEOP_SYSTEM = "docsui_fileop_system";
     private static final String COUNT_FILEOP_EXTERNAL = "docsui_fileop_external";
     private static final String COUNT_FILEOP_CANCELED = "docsui_fileop_canceled";
     private static final String COUNT_STARTUP_MS = "docsui_startup_ms";
     private static final String COUNT_DRAWER_OPENED = "docsui_drawer_opened";
-    private static final String COUNT_DRAG_N_DROP = "docsui_drag_n_drop";
-    private static final String COUNT_SEARCH = "docsui_search";
-    private static final String COUNT_MENU_ACTION = "docsui_menu_action";
-    private static final String COUNT_KEYBOARD_ACTION = "docsui_keyboard_action";
+    private static final String COUNT_USER_ACTION = "docsui_menu_action";
 
     // Indices for bucketing roots in the roots histogram. "Other" is the catch-all index for any
     // root that is not explicitly recognized by the Metrics code (see {@link
@@ -215,54 +212,67 @@
     public @interface Provider {}
 
 
-    // Codes representing different menu actions. These are used for bucketing stats in the
-    // COUNT_MENU_ACTION histogram.
-    // Both regular toolbar menu and action mode menu operations are included.
+    // Codes representing different user actions. These are used for bucketing stats in the
+    // COUNT_USER_ACTION histogram.
+    // The historgram includes action triggered from menu or invoked by keyboard shortcut.
     // Do not change or rearrange these values, that will break historical data. Only add to the
     // list.
     // Do not use negative numbers or zero; clearcut only handles positive integers.
-    private static final int ACTION_MENU_OTHER = 1;
-    private static final int ACTION_MENU_GRID = 2;
-    private static final int ACTION_MENU_LIST = 3;
-    private static final int ACTION_MENU_SORT = 4;
-    private static final int ACTION_MENU_SORT_NAME = 5;
-    private static final int ACTION_MENU_SORT_DATE = 6;
-    private static final int ACTION_MENU_SORT_SIZE = 7;
-    private static final int ACTION_MENU_SEARCH = 8;
-    private static final int ACTION_MENU_SHOW_SIZE = 9;
-    private static final int ACTION_MENU_SETTINGS = 10;
-    private static final int ACTION_MENU_COPY_TO = 11;
-    private static final int ACTION_MENU_MOVE_TO = 12;
-    private static final int ACTION_MENU_DELETE = 13;
-    private static final int ACTION_MENU_RENAME = 14;
-    private static final int ACTION_MENU_CREATE_DIR = 15;
-    private static final int ACTION_MENU_SELECT_ALL = 16;
-    private static final int ACTION_MENU_SHARE = 17;
-    private static final int ACTION_MENU_OPEN = 18;
-    private static final int ACTION_MENU_ADVANCED = 19;
+    public static final int USER_ACTION_OTHER = 1;
+    public static final int USER_ACTION_GRID = 2;
+    public static final int USER_ACTION_LIST = 3;
+    public static final int USER_ACTION_SORT_NAME = 4;
+    public static final int USER_ACTION_SORT_DATE = 5;
+    public static final int USER_ACTION_SORT_SIZE = 6;
+    public static final int USER_ACTION_SEARCH = 7;
+    public static final int USER_ACTION_SHOW_SIZE = 8;
+    public static final int USER_ACTION_HIDE_SIZE = 9;
+    public static final int USER_ACTION_SETTINGS = 10;
+    public static final int USER_ACTION_COPY_TO = 11;
+    public static final int USER_ACTION_MOVE_TO = 12;
+    public static final int USER_ACTION_DELETE = 13;
+    public static final int USER_ACTION_RENAME = 14;
+    public static final int USER_ACTION_CREATE_DIR = 15;
+    public static final int USER_ACTION_SELECT_ALL = 16;
+    public static final int USER_ACTION_SHARE = 17;
+    public static final int USER_ACTION_OPEN = 18;
+    public static final int USER_ACTION_SHOW_ADVANCED = 19;
+    public static final int USER_ACTION_HIDE_ADVANCED = 20;
+    public static final int USER_ACTION_NEW_WINDOW = 21;
+    public static final int USER_ACTION_PASTE_CLIPBOARD = 22;
+    public static final int USER_ACTION_COPY_CLIPBOARD = 23;
+    public static final int USER_ACTION_DRAG_N_DROP = 24;
+    public static final int USER_ACTION_DRAG_N_DROP_MULTI_WINDOW = 25;
 
     @IntDef(flag = false, value = {
-            ACTION_MENU_OTHER,
-            ACTION_MENU_GRID,
-            ACTION_MENU_LIST,
-            ACTION_MENU_SORT,
-            ACTION_MENU_SORT_NAME,
-            ACTION_MENU_SORT_DATE,
-            ACTION_MENU_SORT_SIZE,
-            ACTION_MENU_SHOW_SIZE,
-            ACTION_MENU_SETTINGS,
-            ACTION_MENU_COPY_TO,
-            ACTION_MENU_MOVE_TO,
-            ACTION_MENU_DELETE,
-            ACTION_MENU_RENAME,
-            ACTION_MENU_CREATE_DIR,
-            ACTION_MENU_SELECT_ALL,
-            ACTION_MENU_SHARE,
-            ACTION_MENU_OPEN,
-            ACTION_MENU_ADVANCED
+            USER_ACTION_OTHER,
+            USER_ACTION_GRID,
+            USER_ACTION_LIST,
+            USER_ACTION_SORT_NAME,
+            USER_ACTION_SORT_DATE,
+            USER_ACTION_SORT_SIZE,
+            USER_ACTION_SEARCH,
+            USER_ACTION_SHOW_SIZE,
+            USER_ACTION_HIDE_SIZE,
+            USER_ACTION_SETTINGS,
+            USER_ACTION_COPY_TO,
+            USER_ACTION_MOVE_TO,
+            USER_ACTION_DELETE,
+            USER_ACTION_RENAME,
+            USER_ACTION_CREATE_DIR,
+            USER_ACTION_SELECT_ALL,
+            USER_ACTION_SHARE,
+            USER_ACTION_OPEN,
+            USER_ACTION_SHOW_ADVANCED,
+            USER_ACTION_HIDE_ADVANCED,
+            USER_ACTION_NEW_WINDOW,
+            USER_ACTION_PASTE_CLIPBOARD,
+            USER_ACTION_COPY_CLIPBOARD,
+            USER_ACTION_DRAG_N_DROP,
+            USER_ACTION_DRAG_N_DROP_MULTI_WINDOW
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface MenuAction {}
+    public @interface UserAction {}
 
     // Codes representing different menu actions. These are used for bucketing stats in the
     // COUNT_MENU_ACTION histogram.
@@ -291,31 +301,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface MetricsAction {}
 
-    // Codes representing different keyboard shortcut triggered actions. These are used for
-    // bucketing stats in the COUNT_KEYBOARD_ACTION histogram.
-    // Do not change or rearrange these values, that will break historical data. Only add to the
-    // list.
-    // Do not use negative numbers or zero; clearcut only handles positive integers.
-    public static final int ACTION_KEYBOARD_OTHER = 1;
-    public static final int ACTION_KEYBOARD_PASTE = 2;
-    public static final int ACTION_KEYBOARD_COPY = 3;
-    public static final int ACTION_KEYBOARD_DELETE = 4;
-    public static final int ACTION_KEYBOARD_SELECT_ALL = 5;
-    public static final int ACTION_KEYBOARD_BACK = 6;
-    public static final int ACTION_KEYBOARD_SWITCH_FOCUS = 7;
-
-    @IntDef(flag = false, value = {
-            ACTION_KEYBOARD_OTHER,
-            ACTION_KEYBOARD_PASTE,
-            ACTION_KEYBOARD_COPY,
-            ACTION_KEYBOARD_DELETE,
-            ACTION_KEYBOARD_SELECT_ALL,
-            ACTION_KEYBOARD_BACK,
-            ACTION_KEYBOARD_SWITCH_FOCUS
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface KeyboardAction {}
-
     // Codes representing different actions to open the drawer. They are used for bucketing stats in
     // the COUNT_DRAWER_OPENED histogram.
     // Do not change or rearrange these values, that will break historical data. Only add to the
@@ -382,15 +367,6 @@
     }
 
     /**
-     * Logs a multi-window start. Call this when the user spawns a new DocumentsUI window.
-     *
-     * @param context
-     */
-    public static void logMultiWindow(Context context) {
-        logCount(context, COUNT_MULTI_WINDOW);
-    }
-
-    /**
      * Logs a drawer opened event. Call this when the user opens drawer by swipe or by clicking the
      * hamburger icon.
      * @param context
@@ -496,7 +472,7 @@
      * @param context
      */
     public static void logCreateDirError(Context context) {
-        logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR);
+        logHistogram(context, COUNT_FILEOP_SYSTEM, FILEOP_CREATE_DIR_ERROR);
     }
 
     /**
@@ -520,16 +496,6 @@
     }
 
     /**
-     * Logs keyboard shortcut actions. Since keyboard shortcuts have their corresponding menu items,
-     * they are identified by menu item resource id for convenience.
-     * @param context
-     * @param keyCode
-     */
-    public static void logKeyboardAction(Context context, @KeyboardAction int action) {
-        logHistogram(context, COUNT_KEYBOARD_ACTION, action);
-    }
-
-    /**
      * Logs startup time in milliseconds.
      * @param context
      * @param startupMs Startup time in milliseconds.
@@ -538,25 +504,6 @@
         logHistogram(context, COUNT_STARTUP_MS, startupMs);
     }
 
-    /**
-     * Logs a drag and drop action. Call this when the user drops the content triggering copy.
-     * operation.
-     *
-     * @param context
-     */
-    public static void logDragNDrop(Context context) {
-        logCount(context, COUNT_DRAG_N_DROP);
-    }
-
-    /**
-     * Logs a search. Call this when the search operation is finished.
-     *
-     * @param context
-     */
-    public static void logSearch(Context context) {
-        logCount(context, COUNT_SEARCH);
-    }
-
     private static void logInterProviderFileOps(
             Context context,
             String histogram,
@@ -677,71 +624,12 @@
     }
 
     /**
-     * Logs menu action that was selected by user.
+     * Logs the action that was started by user.
      * @param context
-     * @param id Resource id of the menu item.
+     * @param userAction
      */
-    public static void logMenuAction(Context context, int id) {
-        @MenuAction int menuAction = ACTION_MENU_OTHER;
-        switch (id) {
-            case R.id.menu_grid:
-                menuAction = ACTION_MENU_GRID;
-                break;
-            case R.id.menu_list:
-                menuAction = ACTION_MENU_LIST;
-                break;
-            case R.id.menu_sort:
-                menuAction = ACTION_MENU_SORT;
-                break;
-            case R.id.menu_sort_name:
-                menuAction = ACTION_MENU_SORT_NAME;
-                break;
-            case R.id.menu_sort_date:
-                menuAction = ACTION_MENU_SORT_DATE;
-                break;
-            case R.id.menu_sort_size:
-                menuAction = ACTION_MENU_SORT_SIZE;
-                break;
-            case R.id.menu_search:
-                menuAction = ACTION_MENU_SEARCH;
-                break;
-            case R.id.menu_file_size:
-                menuAction = ACTION_MENU_SHOW_SIZE;
-                break;
-            case R.id.menu_settings:
-                menuAction = ACTION_MENU_SETTINGS;
-                break;
-            case R.id.menu_copy_to:
-                menuAction = ACTION_MENU_COPY_TO;
-                break;
-            case R.id.menu_move_to:
-                menuAction = ACTION_MENU_MOVE_TO;
-                break;
-            case R.id.menu_delete:
-                menuAction = ACTION_MENU_DELETE;
-                break;
-            case R.id.menu_rename:
-                menuAction = ACTION_MENU_RENAME;
-                break;
-            case R.id.menu_create_dir:
-                menuAction = ACTION_MENU_CREATE_DIR;
-                break;
-            case R.id.menu_select_all:
-                menuAction = ACTION_MENU_SELECT_ALL;
-                break;
-            case R.id.menu_share:
-                menuAction = ACTION_MENU_SHARE;
-                break;
-            case R.id.menu_open:
-                menuAction = ACTION_MENU_OPEN;
-                break;
-            case R.id.menu_advanced:
-                menuAction = ACTION_MENU_ADVANCED;
-                break;
-            default:
-                break;
-        }
-        logHistogram(context, COUNT_MENU_ACTION, menuAction);
+    public static void logUserAction(Context context, @UserAction int userAction) {
+        logHistogram(context, COUNT_USER_ACTION, userAction);
     }
 
     /**
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 5f665c0..8bbcc30 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -17,8 +17,8 @@
 package com.android.documentsui;
 
 import static com.android.documentsui.Shared.DEBUG;
-import static com.android.documentsui.State.ACTION_OPEN_TREE;
 
+import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -319,7 +319,8 @@
             for (final RootInfo root : roots) {
                 final RootItem item = new RootItem(root);
 
-                if (root.isHome() && Shared.isHomeRootHidden(context)) {
+                if (root.isHome() &&
+                        !Shared.shouldShowDocumentsRoot(context, ((Activity) context).getIntent())) {
                     continue;
                 } else if (root.isLibrary()) {
                     if (DEBUG) Log.d(TAG, "Adding " + root + " as library.");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
index 945ed34..11b8891 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/SearchViewManager.java
@@ -185,9 +185,6 @@
         if(mFullBar) {
             Menu menu = mActionBar.getMenu();
             menu.setGroupVisible(R.id.group_hide_when_searching, false);
-        } else {
-            // If search in full-bar mode it will be logged in FilesActivity#onOptionsItemSelected
-            Metrics.logMenuAction(mActionBar.getContext(), R.id.menu_search);
         }
     }
 
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index 2c60d4a..1ba836a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -16,19 +16,16 @@
 
 package com.android.documentsui;
 
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.Intent;
 import android.content.res.Configuration;
+import android.provider.DocumentsContract;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
 import android.view.WindowManager;
 
-import com.android.documentsui.State.ActionType;
-
-import static com.android.documentsui.State.ACTION_OPEN_TREE;
-
-import android.app.AlertDialog;
-
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.List;
@@ -45,10 +42,21 @@
             "com.android.documentsui.PICK_COPY_DESTINATION";
 
     /**
+     * Extra flag allowing app to be opened in productivity mode (less downloadsy).
+     * Useful developers and the likes. When set to true overrides the default
+     * config value of productivity_device.
+     */
+    public static final String EXTRA_PRODUCTIVITY_MODE = "com.android.documentsui.PRODUCTIVITY";
+
+    /**
      * Extra boolean flag for {@link ACTION_PICK_COPY_DESTINATION}, which
      * specifies if the destination directory needs to create new directory or not.
      */
     public static final String EXTRA_DIRECTORY_COPY = "com.android.documentsui.DIRECTORY_COPY";
+
+    /**
+     * Extra flag used to store the current stack so user opens in right spot.
+     */
     public static final String EXTRA_STACK = "com.android.documentsui.STACK";
 
     /**
@@ -175,10 +183,33 @@
     }
 
     /*
-     * Indicates if the home directory should be hidden in the roots list.
+     * Returns true if app is running in "productivity mode".
      */
-    public static boolean isHomeRootHidden(Context context) {
-        return context.getResources().getBoolean(R.bool.home_root_hidden);
+    public static boolean productivityMode(Context context) {
+        return context.getResources().getBoolean(R.bool.productivity_device);
     }
 
+    /*
+     * Returns true if app is running in "productivity mode".
+     */
+    private static boolean isProductivityMode(Context context, Intent intent) {
+        return intent.getBooleanExtra(
+                Shared.EXTRA_PRODUCTIVITY_MODE,
+                context.getResources().getBoolean(R.bool.productivity_device));
+    }
+
+    /*
+     * Returns true if "Documents" root should be shown.
+     */
+    public static boolean shouldShowDocumentsRoot(Context context, Intent intent) {
+        return isProductivityMode(context, intent);
+    }
+
+    /*
+     * Returns true if device root should be shown.
+     */
+    public static boolean shouldShowDeviceRoot(Context context, Intent intent) {
+        return isProductivityMode(context, intent)
+                || intent.getBooleanExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, false);
+    }
 }
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index c7d60e3..f239eb4 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -123,9 +123,6 @@
     /** Instance state for every shown directory */
     public HashMap<String, SparseArray<Parcelable>> dirState = new HashMap<>();
 
-    /** UI selection */
-    public Selection selectedDocuments = new Selection();
-
     /** Currently copying file */
     public List<DocumentInfo> selectedDocumentsForCopy = new ArrayList<>();
 
@@ -202,7 +199,6 @@
         out.writeInt(external ? 1 : 0);
         DurableUtils.writeToParcel(out, stack);
         out.writeMap(dirState);
-        out.writeParcelable(selectedDocuments, 0);
         out.writeList(selectedDocumentsForCopy);
         out.writeList(excludedAuthorities);
         out.writeInt(openableOnly ? 1 : 0);
@@ -233,7 +229,6 @@
             state.external = in.readInt() != 0;
             DurableUtils.readFromParcel(in, state.stack);
             in.readMap(state.dirState, loader);
-            state.selectedDocuments = in.readParcelable(loader);
             in.readList(state.selectedDocumentsForCopy, loader);
             in.readList(state.excludedAuthorities, loader);
             state.openableOnly = in.readInt() != 0;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index c2bb4eb..bc2133e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -40,10 +40,12 @@
 import android.database.Cursor;
 import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
@@ -72,8 +74,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.Toolbar;
 import android.widget.TextView;
+import android.widget.Toolbar;
 
 import com.android.documentsui.BaseActivity;
 import com.android.documentsui.DirectoryLoader;
@@ -225,7 +227,8 @@
         mStateKey = buildStateKey(mRoot, mDocument);
         mQuery = args.getString(Shared.EXTRA_QUERY);
         mType = args.getInt(Shared.EXTRA_TYPE);
-        mSelection = args.getParcelable(Shared.EXTRA_SELECTION);
+        final Selection selection = args.getParcelable(Shared.EXTRA_SELECTION);
+        mSelection = selection != null ? selection : new Selection();
         mSearchMode = args.getBoolean(Shared.EXTRA_SEARCH_MODE);
 
         mIconHelper = new IconHelper(context, MODE_GRID);
@@ -289,14 +292,25 @@
         outState.putParcelable(Shared.EXTRA_ROOT, mRoot);
         outState.putParcelable(Shared.EXTRA_DOC, mDocument);
         outState.putString(Shared.EXTRA_QUERY, mQuery);
-        outState.putParcelable(Shared.EXTRA_SELECTION, mSelection);
-        outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
 
+        // Workaround. To avoid crash, write only up to 512 KB of selection.
+        // If more files are selected, then the selection will be lost.
+        final Parcel parcel = Parcel.obtain();
+        try {
+            mSelection.writeToParcel(parcel, 0);
+            if (parcel.dataSize() <= 512 * 1024) {
+                outState.putParcelable(Shared.EXTRA_SELECTION, mSelection);
+            }
+        } finally {
+            parcel.recycle();
+        }
+
+        outState.putBoolean(Shared.EXTRA_SEARCH_MODE, mSearchMode);
     }
 
     @Override
     public void onActivityResult(@RequestCode int requestCode, int resultCode, Intent data) {
-        switch(requestCode) {
+        switch (requestCode) {
             case REQUEST_COPY_DESTINATION:
                 handleCopyResult(resultCode, data);
                 break;
@@ -489,24 +503,19 @@
         @Override
         public void onSelectionChanged() {
             mSelectionManager.getSelection(mSelected);
-            TypedValue color = new TypedValue();
             if (mSelected.size() > 0) {
                 if (DEBUG) Log.d(TAG, "Maybe starting action mode.");
                 if (mActionMode == null) {
                     if (DEBUG) Log.d(TAG, "Yeah. Starting action mode.");
                     mActionMode = getActivity().startActionMode(this);
                 }
-                getActivity().getTheme().resolveAttribute(R.attr.colorActionMode, color, true);
                 updateActionMenu();
             } else {
                 if (DEBUG) Log.d(TAG, "Finishing action mode.");
                 if (mActionMode != null) {
                     mActionMode.finish();
                 }
-                getActivity().getTheme().resolveAttribute(
-                    android.R.attr.colorPrimaryDark, color, true);
             }
-            getActivity().getWindow().setStatusBarColor(color.data);
 
             if (mActionMode != null) {
                 final String title = Shared.getQuantityString(getActivity(),
@@ -596,8 +605,6 @@
 
         @Override
         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-            Metrics.logMenuAction(getContext(), item.getItemId());
-
             Selection selection = mSelectionManager.getSelection(new Selection());
 
             switch (item.getItemId()) {
@@ -665,6 +672,8 @@
     }
 
     private void openDocuments(final Selection selected) {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_OPEN);
+
         new GetDocumentsTask() {
             @Override
             void onDocumentsReady(List<DocumentInfo> docs) {
@@ -675,6 +684,8 @@
     }
 
     private void shareDocuments(final Selection selected) {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SHARE);
+
         new GetDocumentsTask() {
             @Override
             void onDocumentsReady(List<DocumentInfo> docs) {
@@ -756,6 +767,8 @@
     }
 
     private void deleteDocuments(final Selection selected) {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_DELETE);
+
         assert(!selected.isEmpty());
 
         final DocumentInfo srcParent = getDisplayState().stack.peek();
@@ -806,6 +819,12 @@
     }
 
     private void transferDocuments(final Selection selected, final @OpType int mode) {
+        if(mode == FileOperationService.OPERATION_COPY) {
+            Metrics.logUserAction(getContext(), Metrics.USER_ACTION_COPY_TO);
+        } else if (mode == FileOperationService.OPERATION_MOVE) {
+            Metrics.logUserAction(getContext(), Metrics.USER_ACTION_MOVE_TO);
+        }
+
         // Pop up a dialog to pick a destination.  This is inadequate but works for now.
         // TODO: Implement a picker that is to spec.
         final Intent intent = new Intent(
@@ -852,6 +871,8 @@
     }
 
     private void renameDocuments(Selection selected) {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_RENAME);
+
         // Batch renaming not supported
         // Rename option is only available in menu when 1 document selected
         assert(selected.size() == 1);
@@ -1011,6 +1032,8 @@
     }
 
     public void copySelectedToClipboard() {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_COPY_CLIPBOARD);
+
         Selection selection = mSelectionManager.getSelection(new Selection());
         if (!selection.isEmpty()) {
             copySelectionToClipboard(selection);
@@ -1034,6 +1057,8 @@
     }
 
     public void pasteFromClipboard() {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_PASTE_CLIPBOARD);
+
         copyFromClipboard();
         getActivity().invalidateOptionsMenu();
     }
@@ -1064,6 +1089,8 @@
     }
 
     public void selectAllFiles() {
+        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SELECT_ALL);
+
         // Exclude disabled files
         List<String> enabled = new ArrayList<String>();
         for (String id : mAdapter.getModelIds()) {
@@ -1138,7 +1165,14 @@
                     if (Objects.equals(src, dst)) {
                         return false;
                     }
-                    Metrics.logDragNDrop(getContext());
+                    // Recognize multi-window drag and drop based on the fact that localState is not
+                    // carried between processes. It will stop working when the localsState behavior
+                    // is changed. The info about window should be passed in the localState then.
+                    // The localState could also be null for copying from Recents in single window
+                    // mode, but Recents doesn't offer this functionality (no directories).
+                    Metrics.logUserAction(getContext(),
+                            src == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW
+                                    : Metrics.USER_ACTION_DRAG_N_DROP);
                     copyFromClipData(event.getClipData(), dst);
                     return true;
             }
@@ -1229,41 +1263,68 @@
                 DocumentInfo.fromDirectoryCursor(cursor));
     }
 
-    private Drawable getDragShadowIcon(List<DocumentInfo> docs) {
-        if (docs.size() == 1) {
-            final DocumentInfo doc = docs.get(0);
-            return mIconHelper.getDocumentIcon(getActivity(), doc.authority, doc.documentId,
-                    doc.mimeType, doc.icon);
+    private static class DragShadowBuilder extends View.DragShadowBuilder {
+
+        private final Context mContext;
+        private final IconHelper mIconHelper;
+        private final LayoutInflater mInflater;
+        private final View mShadowView;
+        private final TextView mTitle;
+        private final ImageView mIcon;
+        private final int mWidth;
+        private final int mHeight;
+
+        public DragShadowBuilder(Context context, IconHelper iconHelper, List<DocumentInfo> docs) {
+            mContext = context;
+            mIconHelper = iconHelper;
+            mInflater = LayoutInflater.from(context);
+
+            mWidth = mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_width);
+            mHeight= mContext.getResources().getDimensionPixelSize(R.dimen.drag_shadow_height);
+
+            mShadowView = mInflater.inflate(R.layout.drag_shadow_layout, null);
+            mTitle = (TextView) mShadowView.findViewById(android.R.id.title);
+            mIcon = (ImageView) mShadowView.findViewById(android.R.id.icon);
+
+            mTitle.setText(getTitle(docs));
+            mIcon.setImageDrawable(getIcon(docs));
         }
-        return getActivity().getDrawable(R.drawable.ic_doc_generic);
-    }
 
-    private class DrawableShadowBuilder extends View.DragShadowBuilder {
+        private Drawable getIcon(List<DocumentInfo> docs) {
+            if (docs.size() == 1) {
+                final DocumentInfo doc = docs.get(0);
+                return mIconHelper.getDocumentIcon(mContext, doc.authority, doc.documentId,
+                        doc.mimeType, doc.icon);
+            }
+            return mContext.getDrawable(R.drawable.ic_doc_generic);
+        }
 
-        private final Drawable mShadow;
-
-        private final int mShadowDimension;
-
-        public DrawableShadowBuilder(Drawable shadow) {
-            mShadow = shadow;
-            mShadowDimension = getResources().getDimensionPixelSize(
-                    R.dimen.drag_shadow_size);
-            mShadow.setBounds(0, 0, mShadowDimension, mShadowDimension);
+        private String getTitle(List<DocumentInfo> docs) {
+            if (docs.size() == 1) {
+                final DocumentInfo doc = docs.get(0);
+                return doc.displayName;
+            }
+            return Shared.getQuantityString(mContext, R.plurals.elements_dragged, docs.size());
         }
 
         @Override
         public void onProvideShadowMetrics(
                 Point shadowSize, Point shadowTouchPoint) {
-            shadowSize.set(mShadowDimension, mShadowDimension);
-            shadowTouchPoint.set(mShadowDimension / 2, mShadowDimension / 2);
+            shadowSize.set(mWidth, mHeight);
+            shadowTouchPoint.set(mWidth, mHeight);
         }
 
         @Override
         public void onDrawShadow(Canvas canvas) {
-            mShadow.draw(canvas);
+            Rect r = canvas.getClipBounds();
+            // Calling measure is necessary in order for all child views to get correctly laid out.
+            mShadowView.measure(
+                    View.MeasureSpec.makeMeasureSpec(r.right- r.left, View.MeasureSpec.EXACTLY),
+                    View.MeasureSpec.makeMeasureSpec(r.top- r.bottom, View.MeasureSpec.EXACTLY));
+            mShadowView.layout(r.left, r.top, r.right, r.bottom);
+            mShadowView.draw(canvas);
         }
     }
-
     /**
      * Abstract task providing support for loading documents *off*
      * the main thread. And if it isn't obvious, creating a list
@@ -1351,7 +1412,6 @@
                     // This has to be handled here instead of in a keyboard shortcut, because
                     // keyboard shortcuts all have to be modified with the 'Ctrl' key.
                     if (mSelectionManager.hasSelection()) {
-                        Metrics.logKeyboardAction(getContext(), Metrics.ACTION_KEYBOARD_DELETE);
                         deleteDocuments(mSelectionManager.getSelection());
                     }
                     // Always handle the key, even if there was nothing to delete. This is a
@@ -1414,7 +1474,7 @@
                 }
                 v.startDragAndDrop(
                         mClipper.getClipDataForDocuments(docs),
-                        new DrawableShadowBuilder(getDragShadowIcon(docs)),
+                        new DragShadowBuilder(getActivity(), mIconHelper, docs),
                         getDisplayState().stack.peek(),
                         View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
                                 View.DRAG_FLAG_GLOBAL_URI_WRITE
@@ -1638,8 +1698,9 @@
     @Override
     public void onLoadFinished(Loader<DirectoryResult> loader, DirectoryResult result) {
         if (!isAdded()) return;
+
         if (mSearchMode) {
-            Metrics.logSearch(getContext());
+            Metrics.logUserAction(getContext(), Metrics.USER_ACTION_SEARCH);
         }
 
         State state = getDisplayState();
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
index b80486d..1285b34 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/MultiSelectManager.java
@@ -672,9 +672,9 @@
         /**
          * Used by CREATOR.
          */
-        private Selection(String directoryKey, List<String> selection) {
+        private Selection(String directoryKey, Set<String> selection) {
             mDirectoryKey = directoryKey;
-            mSelection = new HashSet<String>(selection);
+            mSelection = selection;
             mProvisionalSelection = new HashSet<String>();
         }
 
@@ -887,7 +887,7 @@
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(mDirectoryKey);
-            dest.writeList(new ArrayList<>(mSelection));
+            dest.writeStringList(new ArrayList<>(mSelection));
             // We don't include provisional selection since it is
             // typically coupled to some other runtime state (like a band).
         }
@@ -901,9 +901,12 @@
 
             @Override
             public Selection createFromParcel(Parcel in, ClassLoader loader) {
-                return new Selection(
-                        in.readString(),
-                        in.readArrayList(loader));
+                String directoryKey = in.readString();
+
+                ArrayList<String> selected = new ArrayList<>();
+                in.readStringList(selected);
+
+                return new Selection(directoryKey, new HashSet<String>(selected));
             }
 
             @Override
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
index f294919..69f0e67 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/FilesActivityUiTest.java
@@ -22,8 +22,10 @@
 import android.app.DownloadManager;
 import android.app.DownloadManager.Request;
 import android.content.Context;
+import android.content.Intent;
 import android.net.Uri;
 import android.os.RemoteException;
+import android.provider.DocumentsContract;
 import android.support.test.uiautomator.Configurator;
 import android.support.test.uiautomator.UiObject;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -69,11 +71,10 @@
                 ROOT_1_ID);
 
         // Separate logic for "Documents" root, which presence depends on the config setting
-        boolean homeRootHidden = context.getResources().getBoolean(R.bool.home_root_hidden);
-        if (homeRootHidden) {
-            bots.roots.assertRootsAbsent("Documents");
-        } else {
+        if (Shared.shouldShowDocumentsRoot(context, new Intent(DocumentsContract.ACTION_BROWSE))) {
             bots.roots.assertRootsPresent("Documents");
+        } else {
+            bots.roots.assertRootsAbsent("Documents");
         }
     }
 
diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml
index 0763403..b77ff10 100644
--- a/packages/ExtServices/res/values/strings.xml
+++ b/packages/ExtServices/res/values/strings.xml
@@ -17,4 +17,5 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_name">Android Services Library</string>
     <string name="notification_ranker">Android Notification Ranking Service</string>
+    <string name="notification_ranker_autobundle_explanation">Auto-grouping updated by Ranking Service</string>
 </resources>
diff --git a/packages/ExtServices/src/android/ext/services/notification/Ranker.java b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
index 0b2b1a4..3ef2aea 100644
--- a/packages/ExtServices/src/android/ext/services/notification/Ranker.java
+++ b/packages/ExtServices/src/android/ext/services/notification/Ranker.java
@@ -16,16 +16,36 @@
 
 package android.ext.services.notification;
 
+import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+
+import android.os.Bundle;
+import android.service.notification.Adjustment;
 import android.service.notification.NotificationRankerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
+import android.util.Slog;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+
+import android.ext.services.R;
 
 /**
  * Class that provides an updatable ranker module for the notification manager..
  */
 public final class Ranker extends NotificationRankerService {
     private static final String TAG = "RocketRanker";
-    private static final boolean DEBUG =  Log.isLoggable(TAG, Log.DEBUG);;
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private static final int AUTOBUNDLE_AT_COUNT = 4;
+    private static final String AUTOBUNDLE_KEY = "ranker_bundle";
+
+    // Map of package : notification keys. Only contains notifications that are not bundled
+    // by the app (aka no group or sort key).
+    Map<String, LinkedHashSet<String>> mUnbundledNotifications;
 
     @Override
     public Adjustment onNotificationEnqueued(StatusBarNotification sbn, int importance,
@@ -37,10 +57,146 @@
     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
         if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
+        try {
+            List<String> notificationsToBundle = new ArrayList<>();
+            if (!sbn.isGroup()) {
+                // Not grouped by the app, add to the list of notifications for the app;
+                // send bundling update if app exceeds the autobundling limit.
+                synchronized (mUnbundledNotifications) {
+                    LinkedHashSet<String> notificationsForPackage
+                            = mUnbundledNotifications.get(sbn.getPackageName());
+                    if (notificationsForPackage == null) {
+                        notificationsForPackage = new LinkedHashSet<>();
+                    }
+                    if (notificationsForPackage.contains(sbn.getKey())) {
+                        return;
+                    }
+                    notificationsForPackage.add(sbn.getKey());
+                    mUnbundledNotifications.put(sbn.getPackageName(), notificationsForPackage);
+
+                    if (notificationsForPackage.size() >= AUTOBUNDLE_AT_COUNT) {
+                        // Autobundle all but the most recently posted (not updated) notification.
+                        int count = 0;
+                        for (String key : notificationsForPackage) {
+                            if (count < notificationsForPackage.size() - 1) {
+                                notificationsToBundle.add(key);
+                            }
+                            count++;
+                        }
+                    }
+                }
+                if (notificationsToBundle.size() > 0) {
+                    adjustAutobundlingSummary(sbn.getPackageName(), notificationsToBundle.get(0),
+                            true);
+                    adjustNotificationBundling(sbn.getPackageName(), notificationsToBundle, true);
+                }
+            } else {
+                // Grouped, but not by us. Send updates to unautobundle, if we bundled it.
+                maybeUnbundle(sbn, false);
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Failure processing new notification", e);
+        }
+    }
+
+    @Override
+    public void onNotificationRemoved(StatusBarNotification sbn) {
+        try {
+            maybeUnbundle(sbn, true);
+        } catch (Exception e) {
+            Slog.e(TAG, "Error processing canceled notification", e);
+        }
+    }
+
+    /**
+     * Un-autobundles notifications that are now grouped by the app. Additionally cancels
+     * autobundling if the status change of this notification resulted in the loose notification
+     * count being under the limit.
+     */
+    private void maybeUnbundle(StatusBarNotification sbn, boolean notificationGone) {
+        List<String> notificationsToUnAutobundle = new ArrayList<>();
+        boolean removeSummary = false;
+        synchronized (mUnbundledNotifications) {
+            LinkedHashSet<String> notificationsForPackage
+                    = mUnbundledNotifications.get(sbn.getPackageName());
+            if (notificationsForPackage == null || notificationsForPackage.size() == 0) {
+                return;
+            }
+            if (notificationsForPackage.remove(sbn.getKey())) {
+                if (!notificationGone) {
+                    // Add the current notification to the unbundling list if it still exists.
+                    notificationsToUnAutobundle.add(sbn.getKey());
+                }
+                // If the status change of this notification has brought the number of loose
+                // notifications back below the limit, remove the summary and un-autobundle.
+                if (notificationsForPackage.size() == AUTOBUNDLE_AT_COUNT - 1) {
+                    removeSummary = true;
+                    for (String key : notificationsForPackage) {
+                        notificationsToUnAutobundle.add(key);
+                    }
+                }
+            }
+        }
+        if (notificationsToUnAutobundle.size() > 0) {
+            if (removeSummary) {
+                adjustAutobundlingSummary(sbn.getPackageName(), null, false);
+            }
+            adjustNotificationBundling(sbn.getPackageName(), notificationsToUnAutobundle, false);
+        }
     }
 
     @Override
     public void onListenerConnected() {
         if (DEBUG) Log.i(TAG, "CONNECTED");
+        mUnbundledNotifications = new HashMap<>();
+        for (StatusBarNotification sbn : getActiveNotifications()) {
+            onNotificationPosted(sbn);
+        }
     }
+
+    private void adjustAutobundlingSummary(String packageName, String key, boolean summaryNeeded) {
+        Bundle signals = new Bundle();
+        if (summaryNeeded) {
+            signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, true);
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
+        } else {
+            signals.putBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false);
+        }
+        Adjustment adjustment = new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
+                getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
+        if (DEBUG) {
+            Log.i(TAG, "Summary update for: " + packageName + " "
+                    + (summaryNeeded ? "adding" : "removing"));
+        }
+        try {
+            adjustNotification(adjustment);
+        } catch (Exception e) {
+            Slog.e(TAG, "Adjustment failed", e);
+        }
+
+    }
+    private void adjustNotificationBundling(String packageName, List<String> keys, boolean bundle) {
+        List<Adjustment> adjustments = new ArrayList<>();
+        for (String key : keys) {
+            adjustments.add(createBundlingAdjustment(packageName, key, bundle));
+            if (DEBUG) Log.i(TAG, "Sending bundling adjustment for: " + key);
+        }
+        try {
+            adjustNotifications(adjustments);
+        } catch (Exception e) {
+            Slog.e(TAG, "Adjustments failed", e);
+        }
+    }
+
+    private Adjustment createBundlingAdjustment(String packageName, String key, boolean bundle) {
+        Bundle signals = new Bundle();
+        if (bundle) {
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, AUTOBUNDLE_KEY);
+        } else {
+            signals.putString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
+        }
+        return new Adjustment(packageName, key, IMPORTANCE_UNSPECIFIED, signals,
+                getContext().getString(R.string.notification_ranker_autobundle_explanation), null);
+    }
+
 }
\ No newline at end of file
diff --git a/packages/Keyguard/res/drawable/ic_backspace_24dp.xml b/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
index 47c8d14..1e4022e 100644
--- a/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
+++ b/packages/Keyguard/res/drawable/ic_backspace_24dp.xml
@@ -15,6 +15,7 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="24dp"
+        android:autoMirrored="true"
         android:height="24dp"
         android:viewportWidth="48.0"
         android:viewportHeight="48.0">
diff --git a/packages/Keyguard/res/values/attrs.xml b/packages/Keyguard/res/values/attrs.xml
index 96a5bcc..7cfe631 100644
--- a/packages/Keyguard/res/values/attrs.xml
+++ b/packages/Keyguard/res/values/attrs.xml
@@ -32,6 +32,9 @@
 
     <declare-styleable name="PasswordTextView">
         <attr name="scaledTextSize" format="integer" />
+        <attr name="android:gravity" />
+        <attr name="dotSize" format="dimension" />
+        <attr name="charPadding" format="dimension" />
     </declare-styleable>
 
     <declare-styleable name="CarrierText">
diff --git a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
index ef8bb0b..2ff7e12 100644
--- a/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
+++ b/packages/Keyguard/src/com/android/keyguard/NumPadKey.java
@@ -71,6 +71,10 @@
     }
 
     public NumPadKey(Context context, AttributeSet attrs, int defStyle) {
+        this(context, attrs, defStyle, R.layout.keyguard_num_pad_key);
+    }
+
+    protected NumPadKey(Context context, AttributeSet attrs, int defStyle, int contentResource) {
         super(context, attrs, defStyle);
         setFocusable(true);
 
@@ -92,7 +96,7 @@
         mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
-        inflater.inflate(R.layout.keyguard_num_pad_key, this, true);
+        inflater.inflate(contentResource, this, true);
 
         mDigitText = (TextView) findViewById(R.id.digit_text);
         mDigitText.setText(Integer.toString(mDigit));
@@ -113,7 +117,11 @@
             }
         }
 
-        setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
+        a = context.obtainStyledAttributes(attrs, android.R.styleable.View);
+        if (!a.hasValueOrEmpty(android.R.styleable.View_background)) {
+            setBackground(mContext.getDrawable(R.drawable.ripple_drawable));
+        }
+        a.recycle();
         setContentDescription(mDigitText.getText().toString());
     }
 
diff --git a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
index 50e7ecb..7dba545 100644
--- a/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
+++ b/packages/Keyguard/src/com/android/keyguard/PasswordTextView.java
@@ -33,6 +33,7 @@
 import android.text.InputType;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.Gravity;
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -81,6 +82,7 @@
      * The raw text size, will be multiplied by the scaled density when drawn
      */
     private final int mTextHeightRaw;
+    private final int mGravity;
     private ArrayList<CharState> mTextChars = new ArrayList<>();
     private String mText = "";
     private Stack<CharState> mCharPool = new Stack<>();
@@ -118,6 +120,12 @@
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordTextView);
         try {
             mTextHeightRaw = a.getInt(R.styleable.PasswordTextView_scaledTextSize, 0);
+            mGravity = a.getInt(R.styleable.PasswordTextView_android_gravity, Gravity.CENTER);
+            mDotSize = a.getDimensionPixelSize(R.styleable.PasswordTextView_dotSize,
+                    getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size));
+            mCharPadding = a.getDimensionPixelSize(R.styleable.PasswordTextView_charPadding,
+                    getContext().getResources().getDimensionPixelSize(
+                            R.dimen.password_char_padding));
         } finally {
             a.recycle();
         }
@@ -125,9 +133,6 @@
         mDrawPaint.setTextAlign(Paint.Align.CENTER);
         mDrawPaint.setColor(0xffffffff);
         mDrawPaint.setTypeface(Typeface.create("sans-serif-light", 0));
-        mDotSize = getContext().getResources().getDimensionPixelSize(R.dimen.password_dot_size);
-        mCharPadding = getContext().getResources().getDimensionPixelSize(R.dimen
-                .password_char_padding);
         mShowPassword = Settings.System.getInt(mContext.getContentResolver(),
                 Settings.System.TEXT_SHOW_PASSWORD, 1) == 1;
         mAppearInterpolator = AnimationUtils.loadInterpolator(mContext,
@@ -142,11 +147,24 @@
     @Override
     protected void onDraw(Canvas canvas) {
         float totalDrawingWidth = getDrawingWidth();
-        float currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2;
+        float currentDrawPosition;
+        if ((mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.LEFT) {
+            if ((mGravity & Gravity.RELATIVE_LAYOUT_DIRECTION) != 0
+                    && getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+                currentDrawPosition = getWidth() - getPaddingRight() - totalDrawingWidth;
+            } else {
+                currentDrawPosition = getPaddingLeft();
+            }
+        } else {
+            currentDrawPosition = getWidth() / 2 - totalDrawingWidth / 2;
+        }
         int length = mTextChars.size();
         Rect bounds = getCharBounds();
         int charHeight = (bounds.bottom - bounds.top);
-        float yPosition = getHeight() / 2;
+        float yPosition =
+                (getHeight() - getPaddingBottom() - getPaddingTop()) / 2 + getPaddingTop();
+        canvas.clipRect(getPaddingLeft(), getPaddingTop(),
+                getWidth() - getPaddingRight(), getHeight() - getPaddingBottom());
         float charLength = bounds.right - bounds.left;
         for (int i = 0; i < length; i++) {
             CharState charState = mTextChars.get(i);
diff --git a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
index eb96015..7c8806e 100644
--- a/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
+++ b/packages/MtpDocumentsProvider/jni/com_android_mtp_AppFuse.cpp
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_NDEBUG 0
 #define LOG_TAG "AppFuseJNI"
 #include "utils/Log.h"
 
@@ -451,7 +450,7 @@
     ScopedFd fd(static_cast<int>(jfd));
     AppFuse appfuse(env, self);
 
-    ALOGD("Start fuse loop.");
+    ALOGV("Start fuse loop.");
     while (true) {
         FuseRequest request;
 
diff --git a/packages/PrintSpooler/res/layout/print_activity_controls.xml b/packages/PrintSpooler/res/layout/print_activity_controls.xml
index a87afe0..248d0c0 100644
--- a/packages/PrintSpooler/res/layout/print_activity_controls.xml
+++ b/packages/PrintSpooler/res/layout/print_activity_controls.xml
@@ -239,7 +239,8 @@
                     android:singleLine="true"
                     android:ellipsize="end"
                     android:visibility="visible"
-                    android:inputType="textNoSuggestions">
+                    android:inputType="number"
+                    android:digits="0123456789 ,-">
                 </com.android.printspooler.widget.CustomErrorEditText>
 
             </LinearLayout>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index cde0fa3..e7aebdd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -62,7 +62,6 @@
 import android.provider.DocumentsContract;
 import android.text.Editable;
 import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
 import android.text.TextWatcher;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -117,8 +116,6 @@
 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,
@@ -165,22 +162,11 @@
     private static final int MIN_COPIES = 1;
     private static final String MIN_COPIES_STRING = String.valueOf(MIN_COPIES);
 
-    private static final Pattern PATTERN_DIGITS = Pattern.compile("[\\d]+");
-
-    private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
-            "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
-
-    private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
-            "[\\s]*[0-9]+[\\-]?[\\s]*[0-9]*[\\s]*?(([,])"
-                    + "[\\s]*[0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*|[\\s]*)+");
-
     private boolean mIsOptionsUiBound = false;
 
     private final PrinterAvailabilityDetector mPrinterAvailabilityDetector =
             new PrinterAvailabilityDetector();
 
-    private final SimpleStringSplitter mStringCommaSplitter = new SimpleStringSplitter(',');
-
     private final OnFocusChangeListener mSelectAllOnFocusListener = new SelectAllOnFocusListener();
 
     private PrintSpoolerProvider mSpoolerProvider;
@@ -1493,9 +1479,11 @@
                     cancelPrint();
                 }
             } else if (view == mMoreOptionsButton) {
-                // The selected pages is only applied once the user leaves the text field. A click
-                // on this button, does not count as leaving.
-                updateSelectedPagesFromTextField();
+                if (mPageRangeEditText.getError() == null) {
+                    // The selected pages is only applied once the user leaves the text field. A click
+                    // on this button, does not count as leaving.
+                    updateSelectedPagesFromTextField();
+                }
 
                 if (mCurrentPrinter != null) {
                     startAdvancedPrintOptionsActivity(mCurrentPrinter);
@@ -1918,42 +1906,10 @@
         }
 
         if (mRangeOptionsSpinner.getSelectedItemPosition() > 0) {
-            List<PageRange> pageRanges = new ArrayList<>();
-            mStringCommaSplitter.setString(mPageRangeEditText.getText().toString());
+            PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
+            final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
 
-            while (mStringCommaSplitter.hasNext()) {
-                String range = mStringCommaSplitter.next().trim();
-                if (TextUtils.isEmpty(range)) {
-                    continue;
-                }
-                final int dashIndex = range.indexOf('-');
-                final int fromIndex;
-                final int toIndex;
-
-                if (dashIndex > 0) {
-                    fromIndex = Integer.parseInt(range.substring(0, dashIndex).trim()) - 1;
-                    // It is possible that the dash is at the end since the input
-                    // verification can has to allow the user to keep entering if
-                    // this would lead to a valid input. So we handle this.
-                    if (dashIndex < range.length() - 1) {
-                        String fromString = range.substring(dashIndex + 1, range.length()).trim();
-                        toIndex = Integer.parseInt(fromString) - 1;
-                    } else {
-                        toIndex = fromIndex;
-                    }
-                } else {
-                    fromIndex = toIndex = Integer.parseInt(range) - 1;
-                }
-
-                PageRange pageRange = new PageRange(Math.min(fromIndex, toIndex),
-                        Math.max(fromIndex, toIndex));
-                pageRanges.add(pageRange);
-            }
-
-            PageRange[] pageRangesArray = new PageRange[pageRanges.size()];
-            pageRanges.toArray(pageRangesArray);
-
-            return PageRangeUtils.normalize(pageRangesArray);
+            return PageRangeUtils.parsePageRanges(mPageRangeEditText.getText(), pageCount);
         }
 
         return PageRange.ALL_PAGES_ARRAY;
@@ -2785,7 +2741,7 @@
                 editText.setSelection(editText.getText().length());
             }
 
-            if (view == mPageRangeEditText && !hasFocus) {
+            if (view == mPageRangeEditText && !hasFocus && mPageRangeEditText.getError() == null) {
                 updateSelectedPagesFromTextField();
             }
         }
@@ -2805,49 +2761,19 @@
         @Override
         public void afterTextChanged(Editable editable) {
             final boolean hadErrors = hasErrors();
-            String text = editable.toString();
-
-            if (TextUtils.isEmpty(text)) {
-                if (mPageRangeEditText.getError() == null) {
-                    mPageRangeEditText.setError("");
-                    updateOptionsUi();
-                }
-                return;
-            }
-
-            String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
-            if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
-                if (mPageRangeEditText.getError() == null) {
-                    mPageRangeEditText.setError("");
-                    updateOptionsUi();
-                }
-                return;
-            }
 
             PrintDocumentInfo info = mPrintedDocument.getDocumentInfo().info;
             final int pageCount = (info != null) ? getAdjustedPageCount(info) : 0;
+            PageRange[] ranges = PageRangeUtils.parsePageRanges(editable, pageCount);
 
-            // The range
-            Matcher matcher = PATTERN_DIGITS.matcher(text);
-            while (matcher.find()) {
-                String numericString = text.substring(matcher.start(), matcher.end()).trim();
-                if (TextUtils.isEmpty(numericString)) {
-                    continue;
+            if (ranges.length == 0) {
+                if (mPageRangeEditText.getError() == null) {
+                    mPageRangeEditText.setError("");
+                    updateOptionsUi();
                 }
-                final int pageIndex = Integer.parseInt(numericString);
-                if (pageIndex < 1 || pageIndex > pageCount) {
-                    if (mPageRangeEditText.getError() == null) {
-                        mPageRangeEditText.setError("");
-                        updateOptionsUi();
-                    }
-                    return;
-                }
+                return;
             }
 
-            // We intentionally do not catch the case of the from page being
-            // greater than the to page. When computing the requested pages
-            // we just swap them if necessary.
-
             if (mPageRangeEditText.getError() != null) {
                 mPageRangeEditText.setError(null);
                 updateOptionsUi();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
index cee16c8..fcc9f6a 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/SelectPrinterActivity.java
@@ -18,12 +18,11 @@
 
 import android.app.Activity;
 import android.app.LoaderManager;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender.SendIntentException;
 import android.content.Loader;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.DataSetObserver;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -34,6 +33,7 @@
 import android.printservice.PrintServiceInfo;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.ContextMenu;
@@ -80,8 +80,8 @@
 
     private static final String KEY_NOT_FIRST_CREATE = "KEY_NOT_FIRST_CREATE";
 
-    /** If there are any enabled print services */
-    private boolean mHasEnabledPrintServices;
+    /** The currently enabled print services by their ComponentName */
+    private ArrayMap<ComponentName, PrintServiceInfo> mEnabledPrintServices;
 
     private PrinterRegistry mPrinterRegistry;
 
@@ -100,6 +100,8 @@
 
         setContentView(R.layout.select_printer_activity);
 
+        mEnabledPrintServices = new ArrayMap<>();
+
         mPrinterRegistry = new PrinterRegistry(this, null, LOADER_ID_PRINT_REGISTRY,
                 LOADER_ID_PRINT_REGISTRY_INT);
 
@@ -317,7 +319,7 @@
         }
         TextView titleView = (TextView) findViewById(R.id.title);
         View progressBar = findViewById(R.id.progress_bar);
-        if (!mHasEnabledPrintServices) {
+        if (mEnabledPrintServices.size() > 0) {
             titleView.setText(R.string.print_no_print_services);
             progressBar.setVisibility(View.GONE);
         } else if (adapter.getUnfilteredCount() <= 0) {
@@ -346,11 +348,16 @@
 
     @Override
     public void onLoadFinished(Loader<List<PrintServiceInfo>> loader,
-            List<PrintServiceInfo> data) {
-        if (data == null || data.isEmpty()) {
-            mHasEnabledPrintServices = false;
-        } else {
-            mHasEnabledPrintServices = true;
+            List<PrintServiceInfo> services) {
+        mEnabledPrintServices.clear();
+
+        if (services != null && !services.isEmpty()) {
+            final int numServices = services.size();
+            for (int i = 0; i < numServices; i++) {
+                PrintServiceInfo service = services.get(i);
+
+                mEnabledPrintServices.put(service.getComponentName(), service);
+            }
         }
 
         onPrintServicesUpdate();
@@ -533,14 +540,12 @@
             CharSequence title = printer.getName();
             Drawable icon = printer.loadIcon(SelectPrinterActivity.this);
 
-            CharSequence printServiceLabel;
-            try {
-                PackageInfo packageInfo = getPackageManager().getPackageInfo(
-                        printer.getId().getServiceName().getPackageName(), 0);
+            PrintServiceInfo service = mEnabledPrintServices.get(printer.getId().getServiceName());
 
-                printServiceLabel = packageInfo.applicationInfo.loadLabel(getPackageManager());
-            } catch (NameNotFoundException e) {
-                printServiceLabel = null;
+            CharSequence printServiceLabel = null;
+            if (service != null) {
+                printServiceLabel = service.getResolveInfo().loadLabel(getPackageManager())
+                        .toString();
             }
 
             CharSequence description = printer.getDescription();
diff --git a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
index 2b317b3..7425c03 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/util/PageRangeUtils.java
@@ -18,7 +18,9 @@
 
 import android.print.PageRange;
 import android.print.PrintDocumentInfo;
+import android.util.Pair;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Comparator;
 
@@ -155,6 +157,167 @@
     }
 
     /**
+     * Return the next position after {@code pos} that is not a space character.
+     *
+     * @param s   The string to parse
+     * @param pos The starting position
+     *
+     * @return The position of the first space character
+     */
+    private static int readWhiteSpace(CharSequence s, int pos) {
+        while (pos < s.length() && s.charAt(pos) == ' ') {
+            pos++;
+        }
+
+        return pos;
+    }
+
+    /**
+     * Read a number from a string at a certain position.
+     *
+     * @param s   The string to parse
+     * @param pos The starting position
+     *
+     * @return The position after the number + the number read or null if the number was not found
+     */
+    private static Pair<Integer, Integer> readNumber(CharSequence s, int pos) {
+        Integer result = 0;
+        while (pos < s.length() && s.charAt(pos) >= '0' && s.charAt(pos) <= '9') {
+            // Number cannot start with 0
+            if (result == 0 && s.charAt(pos) == '0') {
+                break;
+            }
+            result = result * 10 + (s.charAt(pos) - '0');
+            // Abort on overflow
+            if (result < 0) {
+                break;
+            }
+            pos++;
+        }
+
+        // 0 is not a valid page number
+        if (result == 0) {
+            return new Pair<>(pos, null);
+        } else {
+            return new Pair<>(pos, result);
+        }
+    }
+
+    /**
+     * Read a single character from a string at a certain position.
+     *
+     * @param s            The string to parse
+     * @param pos          The starting position
+     * @param expectedChar The character to read
+     *
+     * @return The position after the character + the character read or null if the character was
+     *         not found
+     */
+    private static Pair<Integer, Character> readChar(CharSequence s, int pos, char expectedChar) {
+        if (pos < s.length() && s.charAt(pos) == expectedChar) {
+            return new Pair<>(pos + 1, expectedChar);
+        } else {
+            return new Pair<>(pos, null);
+        }
+    }
+
+    /**
+     * Read a page range character from a string at a certain position.
+     *
+     * @param s             The string to parse
+     * @param pos           The starting position
+     * @param maxPageNumber The highest page number to accept.
+     *
+     * @return The position after the page range + the page range read or null if the page range was
+     *         not found
+     */
+    private static Pair<Integer, PageRange> readRange(CharSequence s, int pos, int maxPageNumber) {
+        Pair<Integer, Integer> retInt;
+        Pair<Integer, Character> retChar;
+
+        Character comma;
+        if (pos == 0) {
+            // When we reading the first range, we do not want to have a comma
+            comma = ',';
+        } else {
+            retChar = readChar(s, pos, ',');
+            pos = retChar.first;
+            comma = retChar.second;
+        }
+
+        pos = readWhiteSpace(s, pos);
+
+        retInt = readNumber(s, pos);
+        pos = retInt.first;
+        Integer start = retInt.second;
+
+        pos = readWhiteSpace(s, pos);
+
+        retChar = readChar(s, pos, '-');
+        pos = retChar.first;
+        Character separator = retChar.second;
+
+        pos = readWhiteSpace(s, pos);
+
+        retInt = readNumber(s, pos);
+        pos = retInt.first;
+        Integer end = retInt.second;
+
+        pos = readWhiteSpace(s, pos);
+
+        if (comma != null &&
+                // range, maybe unbounded
+                ((separator != null && (start != null || end != null)) ||
+                        // single page
+                        (separator == null && start != null && end == null))) {
+            if (start == null) {
+                start = 1;
+            }
+
+            if (end == null) {
+                if (separator == null) {
+                    end = start;
+                } else {
+                    end = maxPageNumber;
+                }
+            }
+
+            if (start <= end && start >= 1 && end <= maxPageNumber) {
+                return new Pair<>(pos, new PageRange(start - 1, end - 1));
+            }
+        }
+
+        return new Pair<>(pos, null);
+    }
+
+    /**
+     * Parse a string into an array of page ranges.
+     *
+     * @param s             The string to parse
+     * @param maxPageNumber The highest page number to accept.
+     *
+     * @return The parsed ranges or null if the string could not be parsed.
+     */
+    public static PageRange[] parsePageRanges(CharSequence s, int maxPageNumber) {
+        ArrayList<PageRange> ranges = new ArrayList<>();
+
+        int pos = 0;
+        while (pos < s.length()) {
+            Pair<Integer, PageRange> retRange = readRange(s, pos, maxPageNumber);
+
+            if (retRange.second == null) {
+                ranges.clear();
+                break;
+            }
+
+            ranges.add(retRange.second);
+            pos = retRange.first;
+        }
+
+        return PageRangeUtils.normalize(ranges.toArray(new PageRange[ranges.size()]));
+    }
+
+    /**
      * Offsets a the start and end of page ranges with the given value.
      *
      * @param pageRanges The page ranges to offset.
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 9cf9dc9..c44f0d0 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierdie kenmerk is eksperimenteel en kan werkverrigting beïnvloed."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Geneutraliseer deur <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ongeveer <xliff:g id="TIME">%1$s</xliff:g> oor"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – sowat <xliff:g id="TIME">%2$s</xliff:g> oor"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol op WS"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol oor USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tot vol vanaf draadloos"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Laai"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laai tans op WS"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laai tans oor USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laai tans draadloos"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Laai nie"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laai nie"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Vol"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 40e5a31..cd05135 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ይህ ባህሪ የሙከራ ነውና አፈጻጸም ላይ ተጽዕኖ ሊኖረው ይችላል።"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"በ<xliff:g id="TITLE">%1$s</xliff:g> ተሽሯል"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"<xliff:g id="TIME">%1$s</xliff:g> ገደማ ቀርቷል"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ገደማ <xliff:g id="TIME">%2$s</xliff:g> ይቀራል"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> እስከሚሞላ ድረስ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በኤሲ ላይ እስከሚሞላ ድረስ"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በዩኤስቢ ላይ እስከሚሞላ ድረስ"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> በገመድ አልባ ላይ እስከሚሞላ ድረስ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ያልታወቀ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ኃይል በመሙላት ላይ"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"በኤሲ ሃይል በመሙላት ላይ"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"በዩኤስቢ ሃይል በመሙላት ላይ"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"በገመድ አልባ ሃይል በመሙላት ላይ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ባትሪ እየሞላ አይደለም"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ኃይል  እየሞላ አይደለም"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ሙሉነው"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index e21b02c..b3f55eb 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"هذه الميزة تجريبية وقد تؤثر في الأداء."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"تم الاستبدال بـ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"يتبقى <xliff:g id="TIME">%1$s</xliff:g> تقريبًا"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تبقى <xliff:g id="TIME">%2$s</xliff:g> تقريبًا"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال باستخدام التيار المتردد"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال عبر USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> حتى الاكتمال بالشحن اللاسلكي"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"غير معروف"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"شحن"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"جارٍ الشحن بتيار متردد"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏جارٍ الشحن عبر USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"جارٍ الشحن لاسلكيًا"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"لا يتم الشحن"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"لا يتم الشحن"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ممتلئة"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index ca06e664..9a099af 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya eksperimentaldır və performansa təsir edə bilər."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tərəfindən qəbul edilmir"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Təxminən <xliff:g id="TIME">%1$s</xliff:g> qalıb"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - təxminən <xliff:g id="TIME">%2$s</xliff:g> qalıb"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> dolana qədər"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC üzərindən dolana qədər"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB üzərindən dolana qədər"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> naqilsiz üzərindən dolana qədər"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Naməlum"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Enerji doldurma"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Dəyişən cərəyanda qidalanır"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB üzərindən qidalanır"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Naqilsiz qidalanır"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Doldurulmur"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Enerji doldurulmur"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Tam"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 4bd9b03..85b6ac2 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna i može da utiče na performanse."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamenjuje ga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo oko <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni punjačem"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni preko USB-a"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> dok se ne napuni bežično"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje preko punjača"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index a70fc69..c8098ec 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Гэтая функцыя з\'яўляецца эксперыментальнай і можа паўплываць на прадукцыйнасць."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Перавызначаны <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Засталося прыблізна <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – засталося прыблізна <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі ад сеткі пер. току"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўнай зарадкі па USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> да поўн. зарадкі бесправадным шляхам"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Невядома"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарадка"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зар. ад сеткі пер. току"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарадка па USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бесправадная зарадка"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не зараджаецца"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не зараджаецца"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Поўная"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index e0641b1..35b24bf 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Тази функция е експериментална и може да се отрази на ефективността."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Заменено от „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Прибл. оставащо време: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – приблизително оставащо време: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане при променлив ток"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> ˜– <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане през USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно безжично зареждане"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарежда се"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зареждане при AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зареждане през USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично зареждане"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се зарежда"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се зарежда"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Пълна"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 5ca7d03..14c2c95 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"এই বৈশিষ্ট্যটি পরীক্ষামূলক এবং এটি কার্য-সম্পাদনা প্রভাবিত করতে পারে।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> এর দ্বারা ওভাররাইড করা হয়েছে"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"প্রায় <xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> বাকী আছে"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - আনুমানিক <xliff:g id="TIME">%2$s</xliff:g> বাকি আছে"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> বাকী আছে"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACতে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB এর মাধ্যমে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> -  বেতার যোগে সম্পূর্ণ হতে <xliff:g id="TIME">%2$s</xliff:g> বাকি"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"অজানা"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC তে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB এর মাধ্যমে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"তারবিহীনভাবে চার্জ হচ্ছে"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"চার্জ হচ্ছে"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"চার্জ হচ্ছে না"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"চার্জ হচ্ছে না"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"পূর্ণ"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index ce0f409..f9c7fcad 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova funkcija je eksperimentalna te može utjecati na performanse."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Zamjenjuje <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Još otprilike <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – preostalo vreme je otprilike <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do kraja punjenja na el. napajanju"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije preko USB-a"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pune baterije bežičnim punjenjem"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Puni se"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Puni se na punjaču"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje preko USB-a"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 5106f5b..a070740 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Aquesta funció és experimental i pot afectar el rendiment."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"S\'ha substituït per <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Temps restant aproximat: <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Temps restant: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: falten aproximadament <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g>; temps restant: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per CA"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega per USB"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega sense fil"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconegut"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"S\'està carregant"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Càrrega: corr. alt."</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"S\'està carregant"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Càrrega per USB"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"S\'està carregant"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Càrrega sense fils"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"S\'està carregant"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"No s\'està carregant"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No s\'està carregant"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Plena"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 3d3dd6f..040c9f0 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkce je experimentální a může mít vliv na výkon."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Přepsáno nastavením <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Zbývající čas: <xliff:g id="TIME">%1$s</xliff:g> (přibližně)"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zbývá přibližně <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití ze zásuvky"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití přes USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabití bezdrátově"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznámé"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjí se"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjení z adaptéru"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjení přes USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrátové nabíjení"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíjí se"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíjí se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 1f354b7..ddad673 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funktion er eksperimentel og kan påvirke ydeevnen."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tilsidesat af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> tilbage"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> tilbage"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> tilbage"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med adapter"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med USB"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til fuldt opladet med trådløs"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukendt"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Oplader"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladning med AC"</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Oplader"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladning via USB"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Oplader"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Trådløs opladning"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Oplader"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Oplader ikke"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Oplader ikke"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fuld"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 404b72b..514228f 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Hierbei handelt es sich um eine experimentelle Funktion. Dies kann sich auf die Leistung auswirken."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Außer Kraft gesetzt von \"<xliff:g id="TITLE">%1$s</xliff:g>\""</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Noch ca. <xliff:g id="TIME">%1$s</xliff:g> verbleibend"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noch etwa <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei Stromanschluss voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – über USB voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – bei kabellosem Laden voll in <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Unbekannt"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Wird aufgeladen"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laden über Netzteil"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laden über USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kabelloses Laden"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Wird nicht geladen"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wird nicht geladen"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Voll"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 52988f6..8df693a 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Αυτή η λειτουργία είναι πειραματική και ενδεχομένως να επηρεάσει τις επιδόσεις."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Αντικαταστάθηκε από <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Απομένουν περίπου <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - απομένουν περίπου <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση με φορτιστή AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη φόρτιση μέσω USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για πλήρη ασύρματη φόρτιση"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Άγνωστο"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Φόρτιση"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Φόρτιση με AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Φόρτιση μέσω USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ασύρματη φόρτιση"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Δεν φορτίζει"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Δεν φορτίζει"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Πλήρης"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b2df062..466291a 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b2df062..466291a 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b2df062..466291a 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"This feature is experimental and may affect performance."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overridden by <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Approx. <xliff:g id="TIME">%1$s</xliff:g> left"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – approx. <xliff:g id="TIME">%2$s</xliff:g> left"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full on AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full over USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> until full from wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Unknown"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Charging"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Charging on AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Charging over USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Charging wirelessly"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Not charging"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Not charging"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Full"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index dd7ee4d..9bc2cf4 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar el rendimiento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Reemplazado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Falta <xliff:g id="TIME">%1$s</xliff:g> aproximadamente"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g>: alrededor de <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga por USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar la carga inalámbrica"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carga en CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carga con USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carga inalámbrica"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando."</string>
     <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>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 0cc6cb6..ec0cafe 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -34,7 +34,7 @@
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conexión sin Internet"</string>
-    <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectada"</string>
+    <string name="bluetooth_disconnected" msgid="6557104142667339895">"Desconectado"</string>
     <string name="bluetooth_disconnecting" msgid="8913264760027764974">"Desconectando…"</string>
     <string name="bluetooth_connecting" msgid="8555009514614320497">"Estableciendo conexión…"</string>
     <string name="bluetooth_connected" msgid="6038755206916626419">"Conectado"</string>
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función es experimental y puede afectar al rendimiento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Tiempo restante (aproximado): <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - Quedan <xliff:g id="TIME">%2$s</xliff:g> aproximadamente"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tiempo restante: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con CA"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería por USB"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar la batería con Wi-Fi"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconocido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando en CA"</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Cargando"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Cargando"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando de forma inalámbrica"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Cargando"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"No se está cargando"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"No se está cargando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index a275b1a..29a1aea 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"See funktsioon on katseline ja võib mõjutada toimivust."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Alistas <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Umbes <xliff:g id="TIME">%1$s</xliff:g> on jäänud"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – jäänud on umbes <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (vahelduvvool)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (USB)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, kuni aku on täis (juhtmeta laad.)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Tundmatu"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Laadimine"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laad. vahelduvv.-v."</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laadimine USB kaudu"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Juhtmevaba laadimine"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei lae"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei lae"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Täis"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 2556224..4f55e35 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Eginbidea esperimentala da eta eragina izan dezake funtzionamenduan."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> hobespena gainjarri zaio"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"<xliff:g id="TIME">%1$s</xliff:g> inguru guztiz kargatu arte"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> inguru. <xliff:g id="TIME">%2$s</xliff:g> geratzen d(ir)a"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> korrontearen bidez guztiz kargatu arte"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB bidez guztiz kargatu arte"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> haririk gabe guztiz kargatu arte"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Ezezaguna"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Kargatzea"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"KA bidez kargatzen"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB bidez kargatzen"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hari gabe kargatzen"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ez da kargatzen ari"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ez da kargatzen ari"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Beteta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 1e67182..e9d63fd 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"این قابلیت آزمایشی است و ممکن است عملکرد را تحت تأثیر قرار دهد."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"توسط <xliff:g id="TITLE">%1$s</xliff:g> لغو شد"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"تقریباً <xliff:g id="TIME">%1$s</xliff:g> باقی مانده است"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - تقریباً ‏<xliff:g id="TIME">%2$s</xliff:g> باقی مانده است"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل با جریان متناوب"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل از طریق USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> تا شارژ کامل به‌طور بی‌سیم"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ناشناس"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"در حال شارژ شدن"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"شارژ با جریان متناوب"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏شارژ از طریق USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"شارژ به صورت بی‌سیم"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"شارژ نمی‌شود"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"شارژ نمی‌شود"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"پر"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 3d8ac98..5531d27 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tämä ominaisuus on kokeellinen ja voi vaikuttaa suorituskykyyn."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Tämän ohittaa <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Noin <xliff:g id="TIME">%1$s</xliff:g> jäljellä"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – noin <xliff:g id="TIME">%2$s</xliff:g> jäljellä"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (laturilataus)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (USB-lataus)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> kunnes täynnä (WiFi-lataus)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Tuntematon"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Ladataan"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laturilataus"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-lataus"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Langaton lataus"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ei laturissa"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ei laturissa"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Täynnä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 4eb3a427..df6bdc6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut toucher les performances."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> %% – Temps restant : environ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (charge complète sur c.a. dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% par USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> %% (chargée à 100 %% avec chargeur sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge (c.a.)"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge par USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"N\'est pas en charge"</string>
     <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>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index efb10ec..ee40b2a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Cette fonctionnalité est expérimentale et peut affecter les performances."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Remplacé par <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Il reste environ <xliff:g id="TIME">%1$s</xliff:g>."</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Temps restant : <xliff:g id="TIME">%2$s</xliff:g> environ"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sur secteur dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% via USB dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> (chargée à 100 %% sans fil dans <xliff:g id="TIME">%2$s</xliff:g>)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Batterie en charge"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"En charge sur secteur"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"En charge via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"En charge sans fil"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Pas en charge"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Débranchée"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"pleine"</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 3d61e21..4a0451e 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta función é experimental e pode afectar ao rendemento."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Anulado por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Duración aproximada de <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - faltan aproximadamente <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga con USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> para completar a carga co modo sen fíos"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Descoñecido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Cargando"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Cargando con CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Cargando por USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Cargando sen fíos"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Non se está cargando"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non está cargando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Completa"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 628b4be..becda28 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"આ સુવિધા પ્રાયોગિક છે અને કામગીરી પર અસર કરી શકે છે."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> દ્વારા ઓવરરાઇડ થયું"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"અંદાજે. <xliff:g id="TIME">%1$s</xliff:g> બાકી"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - આશરે <xliff:g id="TIME">%2$s</xliff:g> બાકી"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"સંપૂર્ણ થવામાં <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g>, AC પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>, USB પર પૂર્ણ ચાર્જ થયાંને <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> વાયરલેસ દ્વારા પૂર્ણ થાય ત્યાં સુધી"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"અજાણ્યું"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ચાર્જ થઈ રહ્યું છે"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC પર ચાર્જિંગ"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB થી ચાર્જિંગ"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"વાયરલેસથી ચાર્જિંગ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ચાર્જ થઈ રહ્યું નથી"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ચાર્જ થઈ રહ્યું નથી"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"પૂર્ણ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 047c7db..6dffdd9 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यह सुविधा प्रायोगिक है और निष्पादन को प्रभावित कर सकती है."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> के द्वारा ओवरराइड किया गया"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"लगभग <xliff:g id="TIME">%1$s</xliff:g> शेष"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग <xliff:g id="TIME">%2$s</xliff:g> शेष"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूरी होने तक"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC पर पूरी होने तक"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB पर पूरी होने तक"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेस से पूरी होने तक"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हो रही है"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC से चार्ज हो रही"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB पर चार्ज हो रही"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस रूप से चार्ज हो रही"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज नहीं हो रही है"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज नहीं हो रही है"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूरी"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index f5ad6d2..1edb015 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ova je značajka eksperimentalna i može utjecati na performanse."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Premošćeno postavkom <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Još približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"Još <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – još približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – još <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti strujnim napajanjem"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti putem USB-a"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti bežičnim putem"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Punjenje"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Punjenje punjačem"</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Punjenje"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Punjenje putem USB-a"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Punjenje"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bežično punjenje"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Punjenje"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ne puni se"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ne puni se"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puna"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 7279c9c..060599c 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -296,17 +296,26 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ez egy kísérleti funkció, és hatással lehet a teljesítményre."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Felülírva erre: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Kb. <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
+    <string name="power_remaining_duration_only_short" msgid="5329694252258605547">"<xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – kb. <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
+    <string name="power_discharging_duration_short" msgid="4192244429001842403">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> van hátra"</string>
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes töltöttség eléréséig"</string>
+    <string name="power_charging_duration_short" msgid="1098603958472207920">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig hálózatról"</string>
+    <string name="power_charging_duration_ac_short" msgid="7895864687218765582">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a teljes feltöltésig USB-ről"</string>
+    <string name="power_charging_duration_usb_short" msgid="941854728040426399">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> a feltöltésig vezeték nélkül"</string>
+    <string name="power_charging_duration_wireless_short" msgid="1642664799869599476">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Ismeretlen"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Töltés"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hálózati töltés"</string>
+    <string name="battery_info_status_charging_ac_short" msgid="7431401092096415502">"Töltés"</string>
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-s töltés"</string>
+    <string name="battery_info_status_charging_usb_short" msgid="6733371990319101366">"Töltés"</string>
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Nem vezetékes töltés"</string>
+    <string name="battery_info_status_charging_wireless_short" msgid="752569941028903610">"Töltés"</string>
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nem tölt"</string>
     <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>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 23d83a2..63c4336 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Սա փորձնական գործառույթ է և կարող է ազդել աշխատանքի վրա:"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Գերազանցված է <xliff:g id="TITLE">%1$s</xliff:g>-ից"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Մնացել է մոտ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - մնաց մոտավորապես <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը հոսանքից"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը USB-ով"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> մինչև լրիվ լիցքավորումը անլար ցանցից"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Անհայտ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Լիցքավորում"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Լիցքավորում AC-ով"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Լիցքավորում USB-ով"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Անլար լիցքավորում"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Չի լիցքավորվում"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Չի լիցքավորվում"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Լիցքավորված"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 24fd0f1..c86c768 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Fitur ini bersifat eksperimental dan dapat memengaruhi kinerja."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Digantikan oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Kira-kira tersisa <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira tersisa. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh pada AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh melalui USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sampai penuh dari nirkabel"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengisi daya"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengisi daya pada AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Isi daya lewat USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Isi daya nirkabel"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengisi daya"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengisi daya"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index e22dbe0..1fc0dc0 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Þessi eiginleiki er á tilraunastigi og getur haft áhrif á frammistöðu."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Hnekkt af <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Um það bil <xliff:g id="TIME">%1$s</xliff:g> eftir"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – u.þ.b. <xliff:g id="TIME">%2$s</xliff:g> eftir"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu með hleðslutæki"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu í gegnum USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> í fulla hleðslu þráðlaust"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Óþekkt"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Í hleðslu"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Hleðslutæki tengt"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Hleður um USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Hleður þráðlaust"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ekki í hleðslu"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ekki í hleðslu"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullhlaðin"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 84ff78a..78cd29b 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Questa funzione è sperimentale e potrebbe influire sulle prestazioni."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valore sostituito da <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Circa <xliff:g id="TIME">%1$s</xliff:g> rimanenti"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – Tempo rimanente: <xliff:g id="TIME">%2$s</xliff:g> circa"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> alla carica completa tramite USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> lla carica completa con wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Sconosciuta"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"In carica"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"In carica tramite CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"In carica tramite USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"In carica, wireless"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Non in carica"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Non in carica"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Carica"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 74ea372..16e0f44 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"תכונה זו היא ניסיונית ועשויה להשפיע על הביצועים."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"נעקף על ידי <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"נשארו <xliff:g id="TIME">%1$s</xliff:g> בערך"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ‏<xliff:g id="TIME">%2$s</xliff:g> בקירוב עד לסיום"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g>‏ - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בזרם חילופין"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"‏<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי ב-USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> ‏- <xliff:g id="TIME">%2$s</xliff:g> עד למילוי בטעינה אלחוטית"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"לא ידוע"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"טוען"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"טוען בזרם חילופין"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏טוען ב-USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"טוען באופן אלחוטי"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"לא בטעינה"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"לא טוען"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"מלא"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 2556d99..ce405df 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"この機能は試験運用機能であり、パフォーマンスに影響することがあります。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g>によって上書き済み"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"あと約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 残り約<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(AC)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(USB)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - フル充電まで<xliff:g id="TIME">%2$s</xliff:g>(ワイヤレス)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACで充電しています"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBで充電しています"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"無線で充電しています"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"充電していません"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"充電していません"</string>
     <!-- String.format failed for translation -->
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index 29e9459..20a1d2a 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ეს ფუნქცია საცდელია და შეიძლება გავლენა იქონიოს შესრულებაზე."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"უკუგებულია <xliff:g id="TITLE">%1$s</xliff:g>-ის მიერ"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"დარჩენილია დაახლოებით <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"დაახლ. <xliff:g id="LEVEL">%1$s</xliff:g> დარჩენილია <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> სრულ დატენვამდე"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ელკვებით სრულ დატენვამდე"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB-თი სრულ დატენვამდე"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> უსადენოდან სრულ დატენვამდე"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"უცნობი"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"იტენება"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"დატენვა ელკვებაზე"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"დატენვა USB-ზე"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"დატენვა უსადენოდ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"არ იტენება"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"არ იტენება"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ბატარეა დატენილია"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index fdd3f95..e71c8d0 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бұл мүмкіндік эксперименттік болып табылады және өнімділікке әсер етуі мүмкін."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> үстінен басқан"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Шамамен <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - шамамен <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - айнымалы токпен толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB арқылы толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - сымсыз толғанша <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгісіз"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядталуда"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Айнымалы токпен зар."</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB арқылы зарядтау"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Сымсыз зарядтау"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Зарядталу орындалып жатқан жоқ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Зарядталып тұрған жоқ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Толық"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 2cafa29..687b71b 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"លក្ខណៈ​នេះ​គឺ​ជា​ការ​ពិសោធន៍ ហើយ​អាច​ប៉ះពាល់​ការ​អនុវត្ត។"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"បដិសេធ​ដោយ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"នៅសល់ប្រហែល <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - នៅ​សល់​ប្រហែល <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់ពេញ​រចន្ត​ឆ្លាស់"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ​តាមយូអេសប៊ី"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> រហូត​ដល់​ពេញ​ពី​ឥតខ្សែ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"មិន​ស្គាល់"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"កំពុងបញ្ចូល​ថ្ម"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"បញ្ចូលថ្មតាម AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"បញ្ចូលថ្មតាមយូអេសប៊ី"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"បញ្ចូលថ្មដោយ​​ឥតខ្សែ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"មិនកំពុង​បញ្ចូល​ថ្ម"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"មិន​បញ្ចូលថ្ម"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ពេញ"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 6d13b35..713d9b3 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ಇದು ಪ್ರಾಯೋಗಿಕ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಕಾರ್ಯಕ್ಷಮತೆ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರಬಹುದು."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ಮೂಲಕ ಅತಿಕ್ರಮಿಸುತ್ತದೆ"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"ಸುಮಾರು <xliff:g id="TIME">%1$s</xliff:g> ಉಳಿದಿದೆ"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"ಸುಮಾರು <xliff:g id="LEVEL">%1$s</xliff:g> <xliff:g id="TIME">%2$s</xliff:g> ಉಳಿದಿದೆ"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ನಲ್ಲಿ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ಮೂಲಕ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ವೈರ್‌‌ಲೆಸ್‌ನಿಂದ ಪೂರ್ಣವಾಗುವವರೆಗೆ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ಅಜ್ಞಾತ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ನಲ್ಲಿ ಚಾರ್ಜ್‌"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ಮೂಲಕ ಚಾರ್ಜ್‌"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ನಿಸ್ತಂತುವಾಗಿ ಚಾರ್ಜ್‌"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ಚಾರ್ಜ್‌ ಆಗುತ್ತಿಲ್ಲ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ಭರ್ತಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 035793e..348141e 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"실험실 기능이며 성능에 영향을 줄 수 있습니다."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> 우선 적용됨"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"약 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 대략 <xliff:g id="TIME">%2$s</xliff:g> 남음"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(AC 전원)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(USB)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 후 충전 완료(무선)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"알 수 없음"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"충전 중"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"충전 중(AC 전원)"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"충전 중(USB)"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"충전 중(무선)"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"충전 안함"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"충전 안함"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"충전 완료"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 8c43312..2016f04 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Бул сынамык мүмкүнчүлүк болгондуктан, иштин майнаптуулугуна таасир этиши мүмкүн."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> менен алмаштырылган"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Болжол менен <xliff:g id="TIME">%1$s</xliff:g> калды"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - болжол менен <xliff:g id="TIME">%2$s</xliff:g> саат калды"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> толгончо"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC аркылуу толгончо"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB аркылуу толгончо"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> зымсыз кубаттоо аркылуу толгончо"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Белгисиз"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Кубатталууда"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ӨА кубатталууда"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB\'ден кубатталууда"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Зымсыз кубатталууда"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Кубат алган жок"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Кубатталган жок"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Толук"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index f158d44..7e6db5e 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"​ຄຸນ​ສົມ​ບັດ​ນີ້​ກຳ​ລັງ​ຢູ່​ໃນ​ການ​ທົດ​ລອງ​ແລະ​ອາດ​ມີ​ຜົນ​ຕໍ່​ປະ​ສິດ​ທິ​ພາບ."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"ຖືກແທນໂດຍ <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"ຍັງເຫຼືອປະມານ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ​ເຫຼືອປະ​ມານ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງ​ຈະ​ເຕັມ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ​ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍສາກ​ດ້ວຍ​ໄຟ AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍສາກ​ດ້ວຍ USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ​ຈຶ່ງ​ຈະ​ເຕັມ​ໂດຍ​ສາກ​ແບບ​ໄຮ້​ສາຍ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ບໍ່ຮູ້ຈັກ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ກຳລັງສາກໄຟ"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ກຳ​​ລັງ​ສາກ​ຜ່ານ​ໝໍ້​ໄຟ"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"ກຳ​ລັງ​ສາກ​ຜ່ານ USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ກຳ​ລັງ​ສາກ​ໄຮ້​ສາຍ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ບໍ່ໄດ້ສາກໄຟ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ບໍ່ໄດ້ສາກໄຟ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ເຕັມ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index d5cea8e..d49876e 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ši funkcija yra eksperimentinė ir ji gali turėti įtakos našumui."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nepaisyta naudojant nuostatą „<xliff:g id="TITLE">%1$s</xliff:g>“"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Liko maždaug <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – liko maždaug <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naud. kint. sr."</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo naudojant USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> iki visiško įkrovimo belaid. ryš."</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nežinomas"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Kraunasi..."</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Įkr. naud. kint. sr."</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Įkraunama naud. USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Įkraunama be laidų"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nekraunama"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nekraunama"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Visiškai įkrautas"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index ea6f4f9..6cd9cfd5 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Šī funkcija ir eksperimentāla un var ietekmēt veiktspēju."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Jaunā preference: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Atlikušais laiks: aptuveni <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> — aptuvenais atlikušais laiks: <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai uzlādei"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai maiņstrāvas uzlādei"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai USB uzlādei"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> — <xliff:g id="TIME">%2$s</xliff:g> līdz pilnai bezvadu uzlādei"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nezināms"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Uzlāde"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Maiņstrāvas uzlāde"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB uzlāde"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezvadu uzlāde"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenotiek uzlāde"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenotiek uzlāde"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Pilns"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index e954cdf..f8f65ec 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Функцијата е експериментална и може да влијае на изведбата."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Прескокнато според <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Преостанаа прибл. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостанува приближно <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна на AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна преку USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до целосно полна, безжично"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Се полни"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Полнење на струја"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Полнење преку УСБ"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Безжично полнење"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не се полни"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не се полни"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Целосна"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index a4b3877..7350b7f 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ഈ ഫീച്ചർ പരീക്ഷണാത്മകമായതിനാൽ പ്രകടനത്തെ ബാധിച്ചേക്കാം."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ഉപയോഗിച്ച് അസാധുവാക്കി"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"ഏകദേശം <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ഏകദേശം <xliff:g id="TIME">%2$s</xliff:g> ശേഷിക്കുന്നു"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - AC-യിൽ പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - വയർലെസ് വഴി പൂർണ്ണമായും ചാർജ്ജാകുന്നതിന്, <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"അജ്ഞാതം"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ചാർജ്ജുചെയ്യുന്നു"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-യിൽ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-യിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"വയർലെസ്സ് കണക്ഷനിലൂടെ ചാർജ്ജുചെയ്യുന്നു"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ചാർജ്ജുചെയ്യുന്നില്ല"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"നിറഞ്ഞു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index d989ef8..7eeae06 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Энэ функц туршилтынх бөгөөд ажиллагаанд нөлөөлж болзошгүй."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Давхарласан <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ойролцоогоор <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ойролцоогоор <xliff:g id="TIME">%2$s</xliff:g> үлдсэн"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"АС-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"USB-р дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"утасгүй цэнэглэгчээр дүүртэл <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Тодорхойгүй"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Цэнэглэж байна"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC-р цэнэглэж байна"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB-р цэнэглэж байна"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Кабльгүйгээр цэнэглэж байна"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Цэнэглэхгүй байна"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Цэнэглэхгүй байна"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Дүүрэн"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index d363874..0521ba1 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"हे वैशिष्‍ट्य प्रायोगिक आहे आणि कदाचित कार्यप्रदर्शन प्रभावित करू शकते."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारे अधिलिखित"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"अंदाजे. <xliff:g id="TIME">%1$s</xliff:g> शिल्लक"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - अंदाजे. <xliff:g id="TIME">%2$s</xliff:g> शिल्लक"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण होण्यात"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC वरून पूर्ण होण्यात"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB वरून पूर्ण होण्यात"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसवरून पूर्ण होण्यात"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज होत आहे"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC वर चार्ज करीत आहे"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB वरून चार्ज करीत आहे"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"वायरलेस वरून चार्ज करीत आहे"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज होत नाही"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज होत नाही"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index ea79a46..a8be0e5 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ciri ini adalah percubaan dan boleh menjejaskan prestasi."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Diatasi oleh <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Kira-kira <xliff:g id="TIME">%1$s</xliff:g> lagi"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - kira-kira. <xliff:g id="TIME">%2$s</xliff:g> yang tinggal"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh di AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh melalui USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> sehingga penuh dari wayarles"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Tidak diketahui"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Mengecas"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Mengecas pada AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Mengecas melalui USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Mengecas tanpa wayar"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Tidak mengecas"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Tidak mengecas"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Penuh"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 74e0f29..220deff 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ဒီအင်္ဂါရပ်မှာ စမ်းသပ်မှု ဖြစ်၍ လုပ်ကိုင်မှုကို အကျိုးသက်ရောက်နိုင်သည်။"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> မှ ကျော်၍ လုပ်ထားသည်။"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"ခန့်မှန်းခြေ <xliff:g id="TIME">%1$s</xliff:g> ကျန်ပါသည်"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ခန့်မှန်းခြေ။ <xliff:g id="TIME">%2$s</xliff:g> ကျန်ရှိနေ"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> အပြည့်အထိ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> လျှပ်စစ်ဖြင့် အပြည့်အထိ"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ဖြင့် အပြည့်အထိ"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ကြိုးမဲ့ဖြင့် အပြည့်အထိ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"အကြောင်းအရာ မသိရှိ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"အားသွင်းနေပါသည်"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"လျှပ်စစ်ဖြင့် အားသွင်းနေ"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USBဖြင့် အားသွင်းနေ"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ကြိုးမဲ့ အားသွင်းနေ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"အားသွင်းမနေပါ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"အားသွင်းမနေပါ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"အပြည့်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 1a0b468..a1407ac 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Denne funksjonen er eksperimentell og kan påvirke ytelsen."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overstyres av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> gjenstår"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca. <xliff:g id="TIME">%2$s</xliff:g> igjen"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> –  fulladet om <xliff:g id="TIME">%2$s</xliff:g> med vekselstrøm"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – fulladet om <xliff:g id="TIME">%2$s</xliff:g> via trådløs lading"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Ukjent"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Lader"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Lader via strømuttak"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Lader via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Lader trådløst"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Lader ikke"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Lader ikke"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index a001cab..83e2e74 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"यो सुविधा प्रयोगात्मक छ र प्रदर्शनमा असर गर्न सक्छ।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> द्वारा अधिरोहित"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"लगभग <xliff:g id="TIME">%1$s</xliff:g> बाँकी छ"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - लगभग। <xliff:g id="TIME">%2$s</xliff:g> बायाँ"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> पूर्ण नभए सम्म"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC मा पूर्ण नभए सम्म"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB मा पूर्ण नभए सम्म"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> वायरलेसबाट पूर्ण नभए सम्म"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"अज्ञात"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"चार्ज हुँदै"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC मा चार्ज गर्दै"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB मा चार्ज गर्दै"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"बिना तार चार्ज गर्दै"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"चार्ज भइरहेको छैन"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"चार्ज हुँदै छैन"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"पूर्ण"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 301e327..55017d8 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Deze functie is experimenteel en kan invloed hebben op de prestaties."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Overschreven door <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca. <xliff:g id="TIME">%1$s</xliff:g> resterend"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ca. <xliff:g id="TIME">%2$s</xliff:g> resterend"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via wisselstroom"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> tot vol via draadloos"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Onbekend"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Opladen"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Opladen via netvoeding"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Opladen via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Draadloos opladen"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Wordt niet opgeladen"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Wordt niet opgeladen"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Volledig"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index c792ec9..eb1eaac 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ਇਹ ਵਿਸ਼ੇਸ਼ਤਾ ਪ੍ਰਯੋਗਾਤਮਿਕ ਹੈ ਅਤੇ ਪ੍ਰਦਰਸ਼ਨ ਤੇ ਅਸਰ ਪਾ ਸਕਦੀ ਹੈ।"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ਦੁਆਰਾ ਓਵਰਰਾਈਡ ਕੀਤਾ"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"ਲਗਭਗ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਲਗਭਗ <xliff:g id="TIME">%2$s</xliff:g> ਬਾਕੀ"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਪੂਰੀ ਹੋਣ ਤੱਕ"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB ਤੇ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਵਾਇਰਲੈਸ ਤੋਂ ਪੂਰਾ ਹੋਣ ਤੱਕ"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ਅਗਿਆਤ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ਚਾਰਜਿੰਗ"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"ਵਾਇਰਲੈਸ ਤੌਰ ਤੇ ਚਾਰਜਿੰਗ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"ਪੂਰੀ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 8aad98b1..1e56cfd 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To jest funkcja eksperymentalna i może wpływać na działanie urządzenia."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Nadpisana przez <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Pozostało około <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostało ok. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania z gniazdka"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania przez USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do pełnego naładowania bezprzewodowo"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Nieznane"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Ładowanie"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Ładowanie zasilaczem"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Ładowanie przez USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Ład. bezprzewodowe"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nie podłączony"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nie podłączony"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Naładowana"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index c05458b..93f333a 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Aproximadamente <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 402bc86..dda00fe 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Esta funcionalidade é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Resta(m) aproximadamente <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – resta(m) aprox. <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> até ficar completa através de USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até ficar compl. por rede s/ fios"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"A carregar"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"A carregar por CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"A carregar por USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"A carregar sem fios"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está a carregar"</string>
     <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>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index c05458b..93f333a 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Este recurso é experimental e pode afetar o desempenho."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Substituído por <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Aproximadamente <xliff:g id="TIME">%1$s</xliff:g> restante(s)"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - cerca de <xliff:g id="TIME">%2$s</xliff:g> restantes"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir em CA"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir via USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> até concluir sem fio"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Desconhecido"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Carregando"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Carregamento CA"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Carregamento via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Carregamento sem fio"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Não está carregando"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Não está carregando"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Cheio"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index d5e0769..3c46d41 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Această funcție este experimentală și poate afecta performanțele."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Timp rămas: aproximativ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – timp rămas: aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă la c.a."</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă prin USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> până la încărcare completă wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Necunoscut"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Încarcă"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Se încarcă la C.A."</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Se încarcă prin USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Se încarcă fără fir"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nu se încarcă"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nu încarcă"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Complet"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 7ad9f16..f4929de 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Это экспериментальная функция, она может снизить производительность устройства."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Новая настройка: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Осталось примерно <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – осталось около <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (от сети)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (через USB)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до полной зарядки (беспроводная)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Идет зарядка"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Зарядка от сети"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Зарядка через USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Беспроводная зарядка"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряжается"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряжается"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Батарея заряжена"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index 4c73219..99f018b 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"මෙම විශේෂාංගය පරීක්ෂණාත්මක සහ ඇතැම් විට ක්‍රියාකාරිත්වයට බලපෑ හැක."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> මගින් ඉක්මවන ලදී"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"දළ වශයෙන් <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතිරිය"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආසන්න <xliff:g id="TIME">%2$s</xliff:g> වම"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"AC හි <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"USB හරහ <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පුර්ණ වන තෙක්"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"රේඩියෝව වෙතින් <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> සම්පූර්ණ වන තෙක්"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"නොදනී"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ආරෝපණය වෙමින්"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC හි ආරෝපණය වෙමින්"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB හරහා ආරෝපණය වෙමින්"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"රැහැන් රහිතව ආරෝපණය වෙමින්"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ආරෝපණය නොවේ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ආරෝපණය නොවෙමින්"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"පූර්ණ"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index e5c9805..4dffdef 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Funkcia je experimentálna a môže mať vplyv na výkonnosť."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Prekonané predvoľbou <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Zostáva cca. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – zostáva približne <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia zo zásuvky"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia cez USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do úplného nabitia bezdrôtovo"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznáme"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Nabíjanie"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nabíjanie zo zásuvky"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nabíjanie cez USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Bezdrôtové nabíjanie"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nenabíja sa"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nenabíja sa"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Nabitá"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 8257e3c..676a3ab 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"To je preskusna funkcija in lahko vpliva na učinkovitost delovanja."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Preglasila nastavitev: <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Še približno <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – še približno <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek napajalnika"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek USB-ja"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napolnjenosti prek brezž. pol."</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Neznano"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Polnjenje"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Polnj. prek iz. toka"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Polnj. prek USB-ja"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Brezžično polnjenje"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Se ne polni"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Se ne polni"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Poln"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index 4a67348..c18e639 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ky funksion është eksperimental dhe mund të ndikojë në veprimtari."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Mbivendosur nga <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Afërsisht <xliff:g id="TIME">%1$s</xliff:g> të mbetura"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - afërsisht <xliff:g id="TIME">%2$s</xliff:g> të mbetura"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> derisa të jetë e plotë"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet në AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet me USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> deri sa të mbushet nga lidhja pa tel"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"I panjohur"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Po ngarkohet"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Po ngarkohet në AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Po ngarkohet me USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Po ngarkohet me valë"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Nuk po ngarkohet"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Nuk po ngarkohet"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"E mbushur"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index eaf7ac5..c0791fc 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ова функција је експериментална и може да утиче на перформансе."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замењује га <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Још отприлике <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – преостало око <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни пуњачем"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни преко USB-а"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> док се не напуни бежично"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Пуњење"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Пуњење преко пуњача"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Пуњење преко USB-а"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Бежично пуњење"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не пуни се"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не пуни се"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Пуно"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index b019672..60c42a5 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Den här funktionen är experimentell och kan påverka prestandan."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Har åsidosatts av <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Ca <xliff:g id="TIME">%1$s</xliff:g> kvar"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – ca <xliff:g id="TIME">%2$s</xliff:g> kvar"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via laddare"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> till fulladdat via trådlös laddning"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Okänd"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Laddar"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Laddas via adapter"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Laddas via USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Laddas trådlöst"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Laddar inte"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Laddar inte"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Fullt"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 3daa90f..5de7686 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Kipengele hiki ni cha majaribio na huenda kikaathiri utendaji."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Imetanguliwa na <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Zimesalia takribani <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia takriban <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g>%% - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa kutumia USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - imesalia <xliff:g id="TIME">%2$s</xliff:g> hadi ijae kwa isiyotumia waya"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Haijulikani"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Inachaji"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Inachaji kupitia AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Inachaji kupitia USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Inachaji bila kutumia waya"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Haichaji"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Haichaji"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Imejaa"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index 574d105..a5f04d0 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"இது சோதனை முறையிலான அம்சம், இது செயல்திறனைப் பாதிக்கலாம்."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> மூலம் மேலெழுதப்பட்டது"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"தோராயமாக <xliff:g id="TIME">%1$s</xliff:g> உள்ளது"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"தோராயம்: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> உள்ளது"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"முழு சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"முழு AC சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"முழு USB சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"முழு வயர்லெஸ் சார்ஜிற்கு: <xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"அறியப்படாத"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"சார்ஜ் ஏற்றப்படுகிறது"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC மூலம் சார்ஜாகிறது"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB மூலம் சார்ஜாகிறது"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"வயர்லெஸில் சார்ஜாகிறது"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"சார்ஜ் செய்யப்படவில்லை"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"சார்ஜ் ஏறவில்லை"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"முழுமை"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 2389112..fe40766 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"ఈ లక్షణం ప్రయోగాత్మకమైనది మరియు పనితీరుపై ప్రభావం చూపవచ్చు."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> ద్వారా భర్తీ చేయబడింది"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"సుమారు <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - సుమారు <xliff:g id="TIME">%2$s</xliff:g> మిగిలి ఉంది"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - ACలో పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB ద్వారా పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - వైర్‌లెస్ నుండి పూర్తిగా నిండటానికి <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"తెలియదు"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"ఛార్జ్ అవుతోంది"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"ACలో ఛార్జ్ అవుతోంది"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ద్వారా ఛార్జ్ అవుతోంది"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"వైర్‌లెస్‌ ద్వారా ఛార్జ్ అవుతోంది"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ఛార్జ్ కావడం లేదు"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ఛార్జ్ కావడం లేదు"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"నిండింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 0f6f78c..c58692f 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"คุณลักษณะนี้เป็นแบบทดลองและอาจส่งผลต่อประสิทธิภาพการทำงาน"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"แทนที่โดย <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"เหลืออีกประมาณ <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - เหลือประมาณ <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็ม"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่าน USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> จนกว่าจะเต็มเมื่อชาร์จผ่านระบบไร้สาย"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"ไม่ทราบ"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"กำลังชาร์จ"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"กำลังชาร์จไฟ AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"กำลังชาร์จผ่าน USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"กำลังชาร์จแบบไร้สาย"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"ไม่ได้ชาร์จ"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"ไม่ได้ชาร์จ"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"เต็ม"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index ef3dde7..093256c 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Ang feature na ito ay pinag-eeksperimentuhan at maaaring makaapekto sa pagganap."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Na-override ng <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Humigit-kumulang <xliff:g id="TIME">%1$s</xliff:g> na lang ang natitira"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - humigit kumulang <xliff:g id="TIME">%2$s</xliff:g> ang natitira"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno sa USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> bago mapuno mula sa wireless"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Hindi Kilala"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Nagcha-charge"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Nagcha-charge sa AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Nagcha-charge sa USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Wireless nag-charge"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Hindi nagcha-charge"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Hindi nagkakarga"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Puno"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 4b7490d..1947d39 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu özellik deneyseldir ve performansı etkileyebilir."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> tarafından geçersiz kılındı"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Yaklaşık <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - yaklaşık <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - prize takılı, tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - USB üzerinden şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - kablosuzdan tam şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> var"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Bilinmiyor"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Şarj oluyor"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"AC ile şarj oluyor"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"USB ile şarj oluyor"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Kablosuz şarj oluyor"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Şarj olmuyor"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Şarj etmiyor"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Dolu"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 5e044c3..0ed6cc0 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Це експериментальна функція. Вона може вплинути на продуктивність."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Замінено на <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Залишилося приблизно <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – залишилось близько <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження з розетки"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного зарядження через USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного з бездротового зарядження"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Невідомо"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Зарядж-ся"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Заряджання з розетки"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Заряджання через USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Заряджання без дроту"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Не заряджається"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Не заряджається"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Акумулятор заряджено"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index 6d3c6c4..99a0f2b 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"یہ خصوصیت تجرباتی ہے اور اس کی وجہ سے کاکردگی متاثر ہو سکتی ہے۔"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> کے ذریعہ منسوخ کردیا گیا"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"تقریبا <xliff:g id="TIME">%1$s</xliff:g> باقی ہیں"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g>‎ - تقریبا <xliff:g id="TIME">%2$s</xliff:g> باقی"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>‎"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>‎ پورا ہونے تک"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> AC‎ پر پورا ہونے تک"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> USB‎ پر پورا ہونے تک"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"‏‎<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>‎ وائرلیس سے پورا ہونے تک"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"نامعلوم"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"چارج ہو رہا ہے"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"‏AC پر چارج ہو رہی ہے"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"‏‫USB پر چارج ہورہی ہے"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"وائرلیس چارجنگ"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"چارج نہیں ہو رہا ہے"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"چارج نہیں ہو رہا ہے"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"مکمل"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index ec04120..b9c0243 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Bu funksiya tajribaviy bo‘lib, u qurilma unumdorligiga ta’sir qilishi mumkin."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"<xliff:g id="TITLE">%1$s</xliff:g> bilan almashtirildi"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Taxminan <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> – taxminan <xliff:g id="TIME">%2$s</xliff:g> qoldi"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, to‘lguncha"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, o‘zgaruvchan tok orqali to‘lguncha"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, USB orqali to‘lguncha"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g>, simsiz quvvatlash orqali to‘lguncha"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Noma’lum"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Quvvat olmoqda"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Quvvat olmoqda (AC)"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Quvvat olmoqda (USB)"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Simsiz quvvat olmoqda"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Quvvat olmayapti"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Quvvatlanmayapti"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"To‘la"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index dd1d482..0c3dc89 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Tính năng này là tính năng thử nghiệm và có thể ảnh hưởng đến hoạt động."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Bị ghi đè bởi <xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Còn khoảng <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - còn khoảng <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy khi cắm vào nguồn AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy qua USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> cho đến khi đầy từ không dây"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Không xác định"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Đang sạc"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Sạc trên AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Sạc qua USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Sạc không dây"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Hiện không sạc"</string>
     <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>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 4117ae1..2b8ce65 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"此功能为实验性功能,可能会影响性能。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已被“<xliff:g id="TITLE">%1$s</xliff:g>”覆盖"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"还剩大约 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还可用大约<xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(交流电充电)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(USB充电)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - 还需<xliff:g id="TIME">%2$s</xliff:g>充满(无线充电)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"正在充电"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在通过交流电源充电"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在通过USB充电"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在无线充电"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"未在充电"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未在充电"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"电量充足"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 1fb4c5e..8dd0417 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會影響效能。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已由「<xliff:g id="TITLE">%1$s</xliff:g>」覆寫"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"尚餘大約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 尚餘大約 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過插頭充電)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (透過 USB 充電)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> 後完成充電 (無線充電)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"未知"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 充電"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"未開始充電"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"電量已滿"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 9c85f0e..dd59954 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"這是一項實驗性功能,可能會對效能造成影響。"</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"已改為<xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"還剩大約 <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - 大約還剩 <xliff:g id="TIME">%2$s</xliff:g>"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (AC)"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (USB)"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽 (無線充電)"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"不明"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"充電中"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"正在透過 AC 變壓器充電"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"正在透過 USB 充電"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"正在透過無線方式充電"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"非充電中"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"非充電中"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"電力充足"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6017d68..610cac4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -296,17 +296,35 @@
     <string name="accessibility_display_daltonizer_preference_subtitle" msgid="3484969015295282911">"Lesi sici esesilingo futhi singathinta ukusebenza."</string>
     <string name="daltonizer_type_overridden" msgid="3116947244410245916">"Igitshezwe ngaphezulu yi-<xliff:g id="TITLE">%1$s</xliff:g>"</string>
     <string name="power_remaining_duration_only" msgid="4400068916452346544">"Cishe ngu-<xliff:g id="TIME">%1$s</xliff:g> osele"</string>
+    <!-- no translation found for power_remaining_duration_only_short (5329694252258605547) -->
+    <skip />
     <string name="power_discharging_duration" msgid="1605929174734600590">"<xliff:g id="LEVEL">%1$s</xliff:g> - isilinganiso esingu-<xliff:g id="TIME">%2$s</xliff:g> esisele"</string>
+    <!-- no translation found for power_discharging_duration_short (4192244429001842403) -->
+    <skip />
     <string name="power_charging" msgid="1779532561355864267">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="2853265177761520490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale"</string>
+    <!-- no translation found for power_charging_duration_short (1098603958472207920) -->
+    <skip />
     <string name="power_charging_duration_ac" msgid="3969186192576594254">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ku-AC"</string>
+    <!-- no translation found for power_charging_duration_ac_short (7895864687218765582) -->
+    <skip />
     <string name="power_charging_duration_usb" msgid="182405645340976546">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale ngaphezulu kwe-USB"</string>
+    <!-- no translation found for power_charging_duration_usb_short (941854728040426399) -->
+    <skip />
     <string name="power_charging_duration_wireless" msgid="1829295708243159464">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> kuze igcwale kusukela kokungenantambo"</string>
+    <!-- no translation found for power_charging_duration_wireless_short (1642664799869599476) -->
+    <skip />
     <string name="battery_info_status_unknown" msgid="196130600938058547">"Akwaziwa"</string>
     <string name="battery_info_status_charging" msgid="1705179948350365604">"Iyashaja"</string>
     <string name="battery_info_status_charging_ac" msgid="2909861890674399949">"Iyashaja ku-AC"</string>
+    <!-- no translation found for battery_info_status_charging_ac_short (7431401092096415502) -->
+    <skip />
     <string name="battery_info_status_charging_usb" msgid="2207489369680923929">"Iyashaja ngaphezulu kwe-USB"</string>
+    <!-- no translation found for battery_info_status_charging_usb_short (6733371990319101366) -->
+    <skip />
     <string name="battery_info_status_charging_wireless" msgid="3574032603735446573">"Iyashaja ngaphandle kwentambo"</string>
+    <!-- no translation found for battery_info_status_charging_wireless_short (752569941028903610) -->
+    <skip />
     <string name="battery_info_status_discharging" msgid="310932812698268588">"Ayishaji"</string>
     <string name="battery_info_status_not_charging" msgid="2820070506621483576">"Ayishaji"</string>
     <string name="battery_info_status_full" msgid="2824614753861462808">"Kugcwele"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a560e3c..084acac 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -850,5 +850,7 @@
     <!-- Description for a custom screen zoom level. This shows the requested display
          density in raw pixels per inch rather than using a relative description. [CHAR LIMIT=24] -->
     <string name="screen_zoom_summary_custom">Custom (<xliff:g id="densityDpi" example="160">%d</xliff:g>)</string>
+    <!-- Label for Help and feedback menu item -->
+    <string name="help_feedback_label">Help &amp; feedback</string>
 
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
new file mode 100644
index 0000000..320cd58
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/HelpUtils.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources.Theme;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuItem.OnMenuItemClickListener;
+
+import java.net.URISyntaxException;
+import java.util.Locale;
+
+/**
+ * Functions to easily prepare contextual help menu option items with an intent that opens up the
+ * browser to a particular URL, while taking into account the preferred language and app version.
+ */
+public class HelpUtils {
+    private final static String TAG = HelpUtils.class.getSimpleName();
+
+    private static final int MENU_HELP = Menu.FIRST + 100;
+
+    /**
+     * Help URL query parameter key for the preferred language.
+     */
+    private final static String PARAM_LANGUAGE_CODE = "hl";
+
+    /**
+     * Help URL query parameter key for the app version.
+     */
+    private final static String PARAM_VERSION = "version";
+
+    // Constants for help intents.
+    private static final String EXTRA_CONTEXT = "EXTRA_CONTEXT";
+    private static final String EXTRA_THEME = "EXTRA_THEME";
+    private static final String EXTRA_PRIMARY_COLOR = "EXTRA_PRIMARY_COLOR";
+    private static final String EXTRA_BACKUP_URI = "EXTRA_BACKUP_URI";
+
+    /**
+     * Cached version code to prevent repeated calls to the package manager.
+     */
+    private static String sCachedVersionCode = null;
+
+    /** Static helper that is not instantiable*/
+    private HelpUtils() { }
+
+    public static boolean prepareHelpMenuItem(Activity activity, Menu menu, String helpUri,
+            String backupContext) {
+        MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
+        return prepareHelpMenuItem(activity, helpItem, helpUri, backupContext);
+    }
+
+    public static boolean prepareHelpMenuItem(Activity activity, Menu menu, int helpUriResource,
+            String backupContext) {
+        MenuItem helpItem = menu.add(0, MENU_HELP, 0, R.string.help_feedback_label);
+        return prepareHelpMenuItem(activity, helpItem, activity.getString(helpUriResource),
+                backupContext);
+    }
+
+    /**
+     * Prepares the help menu item by doing the following.
+     * - If the helpUrlString is empty or null, the help menu item is made invisible.
+     * - Otherwise, this makes the help menu item visible and sets the intent for the help menu
+     *   item to view the URL.
+     *
+     * @return returns whether the help menu item has been made visible.
+     */
+    public static boolean prepareHelpMenuItem(final Activity activity, MenuItem helpMenuItem,
+            String helpUriString, String backupContext) {
+        if (TextUtils.isEmpty(helpUriString)) {
+            // The help url string is empty or null, so set the help menu item to be invisible.
+            helpMenuItem.setVisible(false);
+
+            // return that the help menu item is not visible (i.e. false)
+            return false;
+        } else {
+            final Intent intent = getHelpIntent(activity, helpUriString, backupContext);
+
+            // Set the intent to the help menu item, show the help menu item in the overflow
+            // menu, and make it visible.
+            if (intent != null) {
+                helpMenuItem.setOnMenuItemClickListener(new OnMenuItemClickListener() {
+                    @Override
+                    public boolean onMenuItemClick(MenuItem item) {
+                        try {
+                            activity.startActivityForResult(intent, 0);
+                        } catch (ActivityNotFoundException exc) {
+                            Log.e(TAG, "No activity found for intent: " + intent);
+                        }
+                        return true;
+                    }
+                });
+                helpMenuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
+                helpMenuItem.setVisible(true);
+            } else {
+                helpMenuItem.setVisible(false);
+                return false;
+            }
+
+            // return that the help menu item is visible (i.e., true)
+            return true;
+        }
+    }
+
+    public static Intent getHelpIntent(Context context, String helpUriString,
+            String backupContext) {
+        // Try to handle as Intent Uri, otherwise just treat as Uri.
+        try {
+            Intent intent = Intent.parseUri(helpUriString,
+                    Intent.URI_ANDROID_APP_SCHEME | Intent.URI_INTENT_SCHEME);
+            addIntentParameters(context, intent, backupContext);
+            ComponentName component = intent.resolveActivity(context.getPackageManager());
+            if (component != null) {
+                return intent;
+            } else if (intent.hasExtra(EXTRA_BACKUP_URI)) {
+                // This extra contains a backup URI for when the intent isn't available.
+                return getHelpIntent(context, intent.getStringExtra(EXTRA_BACKUP_URI),
+                        backupContext);
+            } else {
+                return null;
+            }
+        } catch (URISyntaxException e) {
+        }
+        // The help url string exists, so first add in some extra query parameters.
+        final Uri fullUri = uriWithAddedParameters(context, Uri.parse(helpUriString));
+
+        // Then, create an intent that will be fired when the user
+        // selects this help menu item.
+        Intent intent = new Intent(Intent.ACTION_VIEW, fullUri);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+        return intent;
+    }
+
+    private static void addIntentParameters(Context context, Intent intent, String backupContext) {
+        if (!intent.hasExtra(EXTRA_CONTEXT)) {
+            // Insert some context if none exists.
+            intent.putExtra(EXTRA_CONTEXT, backupContext);
+        }
+        intent.putExtra(EXTRA_THEME, 1 /* Light, dark action bar */);
+        Theme theme = context.getTheme();
+        TypedValue typedValue = new TypedValue();
+        theme.resolveAttribute(android.R.attr.colorPrimary, typedValue, true);
+        intent.putExtra(EXTRA_PRIMARY_COLOR, context.getColor(typedValue.resourceId));
+    }
+
+    /**
+     * Adds two query parameters into the Uri, namely the language code and the version code
+     * of the app's package as gotten via the context.
+     * @return the uri with added query parameters
+     */
+    public static Uri uriWithAddedParameters(Context context, Uri baseUri) {
+        Uri.Builder builder = baseUri.buildUpon();
+
+        // Add in the preferred language
+        builder.appendQueryParameter(PARAM_LANGUAGE_CODE, Locale.getDefault().toString());
+
+        // Add in the package version code
+        if (sCachedVersionCode == null) {
+            // There is no cached version code, so try to get it from the package manager.
+            try {
+                // cache the version code
+                PackageInfo info = context.getPackageManager().getPackageInfo(
+                        context.getPackageName(), 0);
+                sCachedVersionCode = Integer.toString(info.versionCode);
+
+                // append the version code to the uri
+                builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
+            } catch (NameNotFoundException e) {
+                // Cannot find the package name, so don't add in the version parameter
+                // This shouldn't happen.
+                Log.wtf(TAG, "Invalid package name for context", e);
+            }
+        } else {
+            builder.appendQueryParameter(PARAM_VERSION, sCachedVersionCode);
+        }
+
+        // Build the full uri and return it
+        return builder.build();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index e86ca82..59637be 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -459,58 +459,40 @@
         LockPatternUtils lockPatternUtils = new LockPatternUtils(context);
         EnforcedAdmin enforcedAdmin = null;
         final int userId = UserHandle.myUserId();
-        if (lockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
-            // userId is managed profile and has a separate challenge, only consider
-            // the admins in that user.
-            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userId);
+        final UserManager um = UserManager.get(context);
+        final List<UserInfo> profiles = um.getProfiles(userId);
+        final int profilesSize = profiles.size();
+        // As we do not have a separate screen lock timeout settings for work challenge,
+        // we need to combine all profiles maximum time to lock even work challenge is
+        // enabled.
+        for (int i = 0; i < profilesSize; i++) {
+            final UserInfo userInfo = profiles.get(i);
+            final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
             if (admins == null) {
-                return null;
+                continue;
             }
             for (ComponentName admin : admins) {
-                if (dpm.getMaximumTimeToLock(admin, userId) > 0) {
+                if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
                     if (enforcedAdmin == null) {
-                        enforcedAdmin = new EnforcedAdmin(admin, userId);
+                        enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
                     } else {
                         return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
                     }
-                }
-            }
-        } else {
-            // Return all admins for this user and the profiles that are visible from this
-            // user that do not use a separate work challenge.
-            final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
-            for (UserInfo userInfo : um.getProfiles(userId)) {
-                final List<ComponentName> admins = dpm.getActiveAdminsAsUser(userInfo.id);
-                if (admins == null) {
+                    // This same admins could have set policies both on the managed profile
+                    // and on the parent. So, if the admin has set the policy on the
+                    // managed profile here, we don't need to further check if that admin
+                    // has set policy on the parent admin.
                     continue;
                 }
-                final boolean isSeparateProfileChallengeEnabled =
-                        lockPatternUtils.isSeparateProfileChallengeEnabled(userInfo.id);
-                for (ComponentName admin : admins) {
-                    if (!isSeparateProfileChallengeEnabled) {
-                        if (dpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
-                            if (enforcedAdmin == null) {
-                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
-                            } else {
-                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
-                            }
-                            // This same admins could have set policies both on the managed profile
-                            // and on the parent. So, if the admin has set the policy on the
-                            // managed profile here, we don't need to further check if that admin
-                            // has set policy on the parent admin.
-                            continue;
-                        }
-                    }
-                    if (userInfo.isManagedProfile()) {
-                        // If userInfo.id is a managed profile, we also need to look at
-                        // the policies set on the parent.
-                        DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
-                        if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
-                            if (enforcedAdmin == null) {
-                                enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
-                            } else {
-                                return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
-                            }
+                if (userInfo.isManagedProfile()) {
+                    // If userInfo.id is a managed profile, we also need to look at
+                    // the policies set on the parent.
+                    final DevicePolicyManager parentDpm = dpm.getParentProfileInstance(userInfo);
+                    if (parentDpm.getMaximumTimeToLock(admin, userInfo.id) > 0) {
+                        if (enforcedAdmin == null) {
+                            enforcedAdmin = new EnforcedAdmin(admin, userInfo.id);
+                        } else {
+                            return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
                         }
                     }
                 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 586f269..ca0b86a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -15,7 +15,7 @@
 import android.os.BatteryManager;
 import android.os.UserManager;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import java.text.NumberFormat;
 
@@ -73,21 +73,22 @@
     /**
      * Returns a circular icon for a user.
      */
-    public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+    public static UserIconDrawable getUserIcon(Context context, UserManager um, UserInfo user) {
+        final int iconSize = UserIconDrawable.getSizeForList(context);
         if (user.isManagedProfile()) {
             // We use predefined values for managed profiles
             Bitmap b = BitmapFactory.decodeResource(context.getResources(),
                     com.android.internal.R.drawable.ic_corp_icon);
-            return CircleFramedDrawable.getInstance(context, b);
+            return new UserIconDrawable(iconSize).setIcon(b).bake();
         }
         if (user.iconPath != null) {
             Bitmap icon = um.getUserIcon(user.id);
             if (icon != null) {
-                return CircleFramedDrawable.getInstance(context, icon);
+                return new UserIconDrawable(iconSize).setIcon(icon).bake();
             }
         }
-        return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
-                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+        return new UserIconDrawable(iconSize).setIconDrawable(
+                UserIcons.getDefaultUserIcon(user.id, /* light= */ false)).bake();
     }
 
     /** Formats the ratio of amount/total as a percentage. */
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 6052ccd..7f0e27a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -399,6 +399,9 @@
         // Copy previous profile list into removedProfiles
         removedProfiles.clear();
         removedProfiles.addAll(profiles);
+        if (DEBUG) {
+            Log.d(TAG,"Current Profiles" + profiles.toString());
+        }
         profiles.clear();
 
         if (uuids == null) {
@@ -415,6 +418,13 @@
             }
         }
 
+        if ((mHfpClientProfile != null) &&
+                BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
+                BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) {
+            profiles.add(mHfpClientProfile);
+            removedProfiles.remove(mHfpClientProfile);
+        }
+
         if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
             mA2dpProfile != null) {
             profiles.add(mA2dpProfile);
@@ -425,7 +435,7 @@
                 mA2dpSinkProfile != null) {
                 profiles.add(mA2dpSinkProfile);
                 removedProfiles.remove(mA2dpSinkProfile);
-            }
+        }
 
         if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
             mOppProfile != null) {
@@ -461,5 +471,9 @@
             profiles.remove(mPbapProfile);
             removedProfiles.add(mPbapProfile);
         }
+
+        if (DEBUG) {
+            Log.d(TAG,"New Profiles" + profiles.toString());
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
new file mode 100644
index 0000000..32478a7
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -0,0 +1,428 @@
+/*
+ * 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.settingslib.drawable;
+
+import android.annotation.NonNull;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one with an optional badge and frame
+ */
+public class UserIconDrawable extends Drawable implements Drawable.Callback {
+
+    private Drawable mUserDrawable;
+    private Bitmap mUserIcon;
+    private Bitmap mBitmap; // baked representation. Required for transparent border around badge
+    private final Paint mIconPaint = new Paint();
+    private final Paint mPaint = new Paint();
+    private final Matrix mIconMatrix = new Matrix();
+    private float mIntrinsicRadius;
+    private float mDisplayRadius;
+    private float mPadding = 0;
+    private int mSize = 0; // custom "intrinsic" size for this drawable if non-zero
+    private boolean mInvalidated = true;
+    private ColorStateList mTintColor = null;
+    private PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_ATOP;
+
+    private float mFrameWidth;
+    private float mFramePadding;
+    private ColorStateList mFrameColor = null;
+    private Paint mFramePaint;
+
+    private Drawable mBadge;
+    private Paint mClearPaint;
+    private float mBadgeRadius;
+    private float mBadgeMargin;
+
+    /**
+     * Gets the system default managed-user badge as a drawable
+     * @param context
+     * @return drawable containing just the badge
+     */
+    public static Drawable getManagedUserBadgeDrawable(Context context) {
+        int displayDensity = context.getResources().getDisplayMetrics().densityDpi;
+        return context.getResources().getDrawableForDensity(
+                com.android.internal.R.drawable.ic_corp_user_badge,
+                displayDensity, context.getTheme());
+    }
+
+    /**
+     * Gets the preferred list-item size of this drawable.
+     * @param context
+     * @return size in pixels
+     */
+    public static int getSizeForList(Context context) {
+        return (int) context.getResources().getDimension(R.dimen.circle_avatar_size);
+    }
+
+    public UserIconDrawable() {
+        this(0);
+    }
+
+    /**
+     * Use this constructor if the drawable is intended to be placed in listviews
+     * @param intrinsicSize if 0, the intrinsic size will come from the icon itself
+     */
+    public UserIconDrawable(int intrinsicSize) {
+        super();
+        mIconPaint.setAntiAlias(true);
+        mIconPaint.setFilterBitmap(true);
+        mPaint.setFilterBitmap(true);
+        mPaint.setAntiAlias(true);
+        if (intrinsicSize > 0) {
+            setBounds(0, 0, intrinsicSize, intrinsicSize);
+            setIntrinsicSize(intrinsicSize);
+        }
+        setIcon(null);
+    }
+
+    public UserIconDrawable setIcon(Bitmap icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        }
+        mUserIcon = icon;
+        if (mUserIcon == null) {
+            mIconPaint.setShader(null);
+            mBitmap = null;
+        } else {
+            mIconPaint.setShader(new BitmapShader(icon, Shader.TileMode.CLAMP,
+                    Shader.TileMode.CLAMP));
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setIconDrawable(Drawable icon) {
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+        }
+        mUserIcon = null;
+        mUserDrawable = icon;
+        if (mUserDrawable == null) {
+            mBitmap = null;
+        } else {
+            mUserDrawable.setCallback(this);
+        }
+        onBoundsChange(getBounds());
+        return this;
+    }
+
+    public UserIconDrawable setBadge(Drawable badge) {
+        mBadge = badge;
+        if (mBadge != null) {
+            if (mClearPaint == null) {
+                mClearPaint = new Paint();
+                mClearPaint.setAntiAlias(true);
+                mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+                mClearPaint.setStyle(Paint.Style.FILL);
+            }
+            // update metrics
+            onBoundsChange(getBounds());
+        } else {
+            invalidateSelf();
+        }
+        return this;
+    }
+
+    public UserIconDrawable setBadgeIfManagedUser(Context context, int userId) {
+        Drawable badge = null;
+        boolean isManaged = context.getSystemService(DevicePolicyManager.class)
+                .getProfileOwnerAsUser(userId) != null;
+        if (isManaged) {
+            badge = getManagedUserBadgeDrawable(context);
+        }
+        return setBadge(badge);
+    }
+
+    public void setBadgeRadius(float radius) {
+        mBadgeRadius = radius;
+        onBoundsChange(getBounds());
+    }
+
+    public void setBadgeMargin(float margin) {
+        mBadgeMargin = margin;
+        onBoundsChange(getBounds());
+    }
+
+    /**
+     * Sets global padding of icon/frame. Doesn't effect the badge.
+     * @param padding
+     */
+    public void setPadding(float padding) {
+        mPadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    private void initFramePaint() {
+        if (mFramePaint == null) {
+            mFramePaint = new Paint();
+            mFramePaint.setStyle(Paint.Style.STROKE);
+            mFramePaint.setAntiAlias(true);
+        }
+    }
+
+    public void setFrameWidth(float width) {
+        initFramePaint();
+        mFrameWidth = width;
+        mFramePaint.setStrokeWidth(width);
+        onBoundsChange(getBounds());
+    }
+
+    public void setFramePadding(float padding) {
+        initFramePaint();
+        mFramePadding = padding;
+        onBoundsChange(getBounds());
+    }
+
+    public void setFrameColor(int color) {
+        initFramePaint();
+        mFramePaint.setColor(color);
+        invalidateSelf();
+    }
+
+    public void setFrameColor(ColorStateList colorList) {
+        initFramePaint();
+        mFrameColor = colorList;
+        invalidateSelf();
+    }
+
+    /**
+     * This sets the "intrinsic" size of this drawable. Useful for views which use the drawable's
+     * intrinsic size for layout. It is independent of the bounds.
+     * @param size if 0, the intrinsic size will be set to the displayed icon's size
+     */
+    public void setIntrinsicSize(int size) {
+        mSize = size;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mInvalidated) {
+            rebake();
+        }
+        if (mBitmap != null) {
+            if (mTintColor == null) {
+                mPaint.setColorFilter(null);
+            } else {
+                int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
+                if (mPaint.getColorFilter() == null) {
+                    mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
+                } else {
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
+                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
+                }
+            }
+
+            canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mPaint.setAlpha(alpha);
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) {
+    }
+
+    @Override
+    public void setTintList(ColorStateList tintList) {
+        mTintColor = tintList;
+        super.invalidateSelf();
+    }
+
+    @Override
+    public void setTintMode(@NonNull PorterDuff.Mode mode) {
+        mTintMode = mode;
+        super.invalidateSelf();
+    }
+
+    /**
+     * This 'bakes' the current state of this icon into a bitmap and removes/recycles the source
+     * bitmap/drawable. Use this when no more changes will be made and an intrinsic size is set.
+     * This effectively turns this into a static drawable.
+     */
+    public UserIconDrawable bake() {
+        if (mSize <= 0) {
+            throw new IllegalStateException("Baking requires an explicit intrinsic size");
+        }
+        onBoundsChange(new Rect(0, 0, mSize, mSize));
+        rebake();
+        mFrameColor = null;
+        mFramePaint = null;
+        mClearPaint = null;
+        if (mUserDrawable != null) {
+            mUserDrawable.setCallback(null);
+            mUserDrawable = null;
+        } else if (mUserIcon != null) {
+            mUserIcon.recycle();
+            mUserIcon = null;
+        }
+        return this;
+    }
+
+    private void rebake() {
+        mInvalidated = false;
+
+        if (mBitmap == null || (mUserDrawable == null && mUserIcon == null)) {
+            return;
+        }
+
+        final Canvas canvas = new Canvas(mBitmap);
+        canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+        if(mUserDrawable != null) {
+            mUserDrawable.draw(canvas);
+        } else if (mUserIcon != null) {
+            int saveId = canvas.save();
+            canvas.concat(mIconMatrix);
+            canvas.drawCircle(mUserIcon.getWidth() * 0.5f, mUserIcon.getHeight() * 0.5f,
+                    mIntrinsicRadius, mIconPaint);
+            canvas.restoreToCount(saveId);
+        }
+
+        if (mFrameColor != null) {
+            mFramePaint.setColor(mFrameColor.getColorForState(getState(), Color.TRANSPARENT));
+        }
+        if ((mFrameWidth + mFramePadding) > 0.001f) {
+            float radius = mDisplayRadius - mPadding - mFrameWidth * 0.5f;
+            canvas.drawCircle(getBounds().exactCenterX(), getBounds().exactCenterY(),
+                    radius, mFramePaint);
+        }
+
+        if ((mBadge != null) && (mBadgeRadius > 0.001f)) {
+            final float badgeDiameter = mBadgeRadius * 2f;
+            final float badgeTop = mBitmap.getHeight() - badgeDiameter;
+            float badgeLeft = mBitmap.getWidth() - badgeDiameter;
+
+            mBadge.setBounds((int) badgeLeft, (int) badgeTop,
+                    (int) (badgeLeft + badgeDiameter), (int) (badgeTop + badgeDiameter));
+
+            final float borderRadius = mBadge.getBounds().width() * 0.5f + mBadgeMargin;
+            canvas.drawCircle(badgeLeft + mBadgeRadius, badgeTop + mBadgeRadius,
+                    borderRadius, mClearPaint);
+
+            mBadge.draw(canvas);
+        }
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        if (bounds.isEmpty() || (mUserIcon == null && mUserDrawable == null)) {
+            return;
+        }
+
+        // re-create bitmap if applicable
+        float newDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        int size = (int) (newDisplayRadius * 2);
+        if (mBitmap == null || size != ((int) (mDisplayRadius * 2))) {
+            mDisplayRadius = newDisplayRadius;
+            if (mBitmap != null) {
+                mBitmap.recycle();
+            }
+            mBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+        }
+
+        // update metrics
+        mDisplayRadius = Math.min(bounds.width(), bounds.height()) * 0.5f;
+        final float iconRadius = mDisplayRadius - mFrameWidth - mFramePadding - mPadding;
+        RectF dstRect = new RectF(bounds.exactCenterX() - iconRadius,
+                                  bounds.exactCenterY() - iconRadius,
+                                  bounds.exactCenterX() + iconRadius,
+                                  bounds.exactCenterY() + iconRadius);
+        if (mUserDrawable != null) {
+            Rect rounded = new Rect();
+            dstRect.round(rounded);
+            mIntrinsicRadius = Math.min(mUserDrawable.getIntrinsicWidth(),
+                                        mUserDrawable.getIntrinsicHeight()) * 0.5f;
+            mUserDrawable.setBounds(rounded);
+        } else if (mUserIcon != null) {
+            // Build square-to-square transformation matrix
+            final float iconCX = mUserIcon.getWidth() * 0.5f;
+            final float iconCY = mUserIcon.getHeight() * 0.5f;
+            mIntrinsicRadius = Math.min(iconCX, iconCY);
+            RectF srcRect = new RectF(iconCX - mIntrinsicRadius, iconCY - mIntrinsicRadius,
+                                      iconCX + mIntrinsicRadius, iconCY + mIntrinsicRadius);
+            mIconMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.FILL);
+        }
+
+        invalidateSelf();
+    }
+
+    @Override
+    public void invalidateSelf() {
+        super.invalidateSelf();
+        mInvalidated = true;
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mFrameColor != null && mFrameColor.isStateful();
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return (mSize <= 0 ? (int) mIntrinsicRadius * 2 : mSize);
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return getIntrinsicWidth();
+    }
+
+    @Override
+    public void invalidateDrawable(@NonNull Drawable who) {
+        invalidateSelf();
+    }
+
+    @Override
+    public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+        scheduleSelf(what, when);
+    }
+
+    @Override
+    public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+        unscheduleSelf(what);
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
index f9fa805f..750601d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/UserAdapter.java
@@ -33,7 +33,7 @@
 import android.widget.SpinnerAdapter;
 import android.widget.TextView;
 import com.android.internal.util.UserIcons;
-import com.android.settingslib.drawable.CircleFramedDrawable;
+import com.android.settingslib.drawable.UserIconDrawable;
 
 import com.android.settingslib.R;
 
@@ -71,7 +71,8 @@
         }
 
         private static Drawable encircle(Context context, Drawable icon) {
-            return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(icon));
+            return new UserIconDrawable(UserIconDrawable.getSizeForList(context))
+                    .setIconDrawable(icon).bake();
         }
     }
     private ArrayList<UserDetails> data;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 61c9f2b..5621642 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1942,7 +1942,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 126;
+            private static final int SETTINGS_VERSION = 127;
 
             private final int mUserId;
 
@@ -2167,6 +2167,36 @@
                     currentVersion = 126;
                 }
 
+                if (currentVersion == 126) {
+                    // Version 126: copy the primary values of LOCK_SCREEN_SHOW_NOTIFICATIONS and
+                    // LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS into managed profile.
+                    if (mUserManager.isManagedProfile(userId)) {
+                        final SettingsState systemSecureSettings =
+                                getSecureSettingsLocked(UserHandle.USER_SYSTEM);
+
+                        final Setting showNotifications = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS);
+                        if (showNotifications != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                                    showNotifications.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+
+                        final Setting allowPrivate = systemSecureSettings.getSettingLocked(
+                                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+                        if (allowPrivate != null) {
+                            final SettingsState secureSettings = getSecureSettingsLocked(userId);
+                            secureSettings.insertSettingLocked(
+                                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+                                    allowPrivate.getValue(),
+                                    SettingsState.SYSTEM_PACKAGE_NAME);
+                        }
+                    }
+                    currentVersion = 127;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 // Return the current version.
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c131fa5..7ca7614 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -463,7 +463,6 @@
                 .setContentTitle(title)
                 .setTicker(title)
                 .setContentText(name)
-                .setContentInfo(percentageText)
                 .setProgress(info.max, info.progress, false)
                 .setOngoing(true)
                 .setContentIntent(infoPendingIntent)
@@ -958,7 +957,7 @@
                 .setDeleteIntent(newCancelIntent(context, info));
 
         if (!TextUtils.isEmpty(info.name)) {
-            builder.setContentInfo(info.name);
+            builder.setSubText(info.name);
         }
 
         Log.v(TAG, "Sending 'Share' notification for ID " + info.id + ": " + title);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 7222cff..9e2442c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -184,6 +184,14 @@
             android:exported="true"
             />
 
+        <!-- Recents depends on every user having their own SystemUI process, so on user switch,
+             ensure that the process is created by starting this service.
+             -->
+        <service android:name="SystemUISecondaryUserService"
+            android:exported="true"
+            android:permission="com.android.systemui.permission.SELF" />
+
+
         <!-- started from PhoneWindowManager
              TODO: Should have an android:permission attribute -->
         <service android:name=".screenshot.TakeScreenshotService"
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
similarity index 68%
rename from packages/SystemUI/res/values-h560dp/config.xml
rename to packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
index 8b576b9..20251c2 100644
--- a/packages/SystemUI/res/values-h560dp/config.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_frame.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  ~ Copyright (C) 2014 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.
@@ -16,8 +16,7 @@
   ~ limitations under the License
   -->
 
-<resources>
-    <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">6</integer>
-</resources>
-
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_activated="true" android:color="@color/current_user_border_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-h560dp/config.xml b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
similarity index 68%
copy from packages/SystemUI/res/values-h560dp/config.xml
copy to packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
index 8b576b9..2b75c36 100644
--- a/packages/SystemUI/res/values-h560dp/config.xml
+++ b/packages/SystemUI/res/color/qs_user_detail_avatar_tint.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  ~ Copyright (C) 2014 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.
@@ -16,8 +16,7 @@
   ~ limitations under the License
   -->
 
-<resources>
-    <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">6</integer>
-</resources>
-
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false" android:color="@color/qs_tile_disabled_color" />
+    <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
new file mode 100644
index 0000000..6519673
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:pathData="M0 0h24v24H0z" />
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M22 3H7c-.69 0-1.23 .35 -1.59 .88 L0 12l5.41 8.11c.36 .53 .9 .89
+1.59 .89 h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59
+12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_down.xml b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
new file mode 100644
index 0000000..25a2560
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
+    <path android:pathData="M0-.75h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
new file mode 100644
index 0000000..599f350
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:pathData="M0 0h24v24H0z" />
+   <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_left.xml b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
new file mode 100644
index 0000000..038187e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
+    <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
new file mode 100644
index 0000000..1e2195e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
+3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
+19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
+14z" />
+    <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_right.xml b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
new file mode 100644
index 0000000..f2d7315
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
+    <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_up.xml b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
new file mode 100644
index 0000000..36a83b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
@@ -0,0 +1,25 @@
+<?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
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path android:fillColor="@color/ksh_key_item_color"
+            android:pathData="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" />
+    <path android:pathData="M0 0h24v24H0z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_close_button.xml b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
index 86fda0d..186a4ba 100644
--- a/packages/SystemUI/res/drawable/tv_pip_close_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_close_button.xml
@@ -19,8 +19,20 @@
     <item android:state_focused="true">
         <layer-list>
             <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_close_white" />
+            <item android:drawable="@drawable/ic_close_white"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
         </layer-list>
     </item>
-    <item android:drawable="@drawable/ic_close_white" />
+    <item>
+        <layer-list>
+            <item android:drawable="@drawable/ic_close_white"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
+        </layer-list>
+    </item>
 </selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_full_button.xml b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
index 332c669..c48dc828 100644
--- a/packages/SystemUI/res/drawable/tv_pip_full_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_full_button.xml
@@ -19,8 +19,20 @@
     <item android:state_focused="true">
         <layer-list>
             <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+            <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
         </layer-list>
     </item>
-    <item android:drawable="@drawable/ic_fullscreen_white_24dp" />
+    <item>
+        <layer-list>
+            <item android:drawable="@drawable/ic_fullscreen_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
+        </layer-list>
+    </item>
 </selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
index d277b07..bcc8973 100644
--- a/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_pause_button.xml
@@ -19,8 +19,20 @@
     <item android:state_focused="true">
         <layer-list>
             <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_pause_white_24dp" />
+            <item android:drawable="@drawable/ic_pause_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
         </layer-list>
     </item>
-    <item android:drawable="@drawable/ic_pause_white_24dp" />
+    <item>
+        <layer-list>
+            <item android:drawable="@drawable/ic_pause_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
+        </layer-list>
+    </item>
 </selector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_play_button.xml b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
index fecdc09..f77ea1d 100644
--- a/packages/SystemUI/res/drawable/tv_pip_play_button.xml
+++ b/packages/SystemUI/res/drawable/tv_pip_play_button.xml
@@ -19,8 +19,20 @@
     <item android:state_focused="true">
         <layer-list>
             <item android:drawable="@drawable/tv_pip_button_focused" />
-            <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+            <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
         </layer-list>
     </item>
-    <item android:drawable="@drawable/ic_play_arrow_white_24dp" />
+    <item>
+        <layer-list>
+            <item android:drawable="@drawable/ic_play_arrow_white_24dp"
+                android:top="@dimen/tv_pip_button_icon_padding"
+                android:bottom="@dimen/tv_pip_button_icon_padding"
+                android:left="@dimen/tv_pip_button_icon_padding"
+                android:right="@dimen/tv_pip_button_icon_padding" />
+        </layer-list>
+    </item>
 </selector>
diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
index af3e379..1f24ab0 100644
--- a/packages/SystemUI/res/layout/battery_detail.xml
+++ b/packages/SystemUI/res/layout/battery_detail.xml
@@ -26,10 +26,13 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingStart="72dp"
-        android:paddingBottom="@dimen/battery_detail_graph_space_top"
-        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textAppearance="?android:attr/textAppearanceSmall"
         android:textColor="?android:attr/colorAccent" />
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/battery_detail_graph_space_top" />
+
     <com.android.settingslib.graph.UsageView
         android:id="@+id/battery_usage"
         android:layout_width="match_parent"
@@ -40,11 +43,14 @@
         android:colorAccent="?android:attr/colorAccent"
         systemui:textColor="#66FFFFFF" />
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/battery_detail_graph_space_bottom" />
+
     <View
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?android:attr/listDivider"
-        android:layout_marginTop="@dimen/battery_detail_graph_space_bottom"
         android:layout_marginBottom="8dp" />
 
     <RelativeLayout
diff --git a/packages/SystemUI/res/layout/forced_resizable_activity.xml b/packages/SystemUI/res/layout/forced_resizable_activity.xml
index df245bc..3c778c4 100644
--- a/packages/SystemUI/res/layout/forced_resizable_activity.xml
+++ b/packages/SystemUI/res/layout/forced_resizable_activity.xml
@@ -18,10 +18,9 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <TextView
+    <include
+        layout="@*android:layout/transient_notification"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:text="@string/forced_resizable_info_text"
-        android:textColor="#ffffff"/>
+        android:layout_gravity="center"/>
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
index 9c2c0ab..3865020 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcut_app_item.xml
@@ -22,8 +22,17 @@
         android:paddingStart="24dp"
         android:paddingEnd="24dp"
         android:paddingBottom="8dp">
+    <ImageView
+            android:id="@+id/keyboard_shortcuts_icon"
+            android:layout_width="24dp"
+            android:layout_height="24dp"
+            android:layout_marginEnd="32dp"
+            android:layout_gravity="center_vertical"
+            android:visibility="gone"
+            android:layout_alignParentStart="true"/>
     <TextView
             android:id="@+id/keyboard_shortcuts_keyword"
+            android:layout_toEndOf="@+id/keyboard_shortcuts_icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:paddingEnd="12dp"
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
new file mode 100644
index 0000000..0cecb96
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="@dimen/ksh_item_padding"
+        android:layout_marginStart="@dimen/ksh_item_margin_start"
+        android:scaleType="fitXY"
+        android:background="@color/ksh_key_item_background"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 5002c12..1215029 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -17,9 +17,9 @@
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
-          android:layout_marginStart="4dp"
-          android:padding="4dp"
-          android:background="#EEEEEE"
-          android:textColor="#8C000000"
+          android:padding="@dimen/ksh_item_padding"
+          android:layout_marginStart="@dimen/ksh_item_margin_start"
+          android:background="@color/ksh_key_item_background"
+          android:textColor="@color/ksh_key_item_color"
           android:singleLine="true"
-          android:textSize="14sp"/>
+          android:textSize="@dimen/ksh_item_text_size"/>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
index c6e453a..d685528 100644
--- a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -33,14 +33,18 @@
     <TextView android:id="@+id/user_name"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginEnd="16dp"
+            android:layout_marginEnd="13dp"
             android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
             />
     <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
             android:contentDescription="@null"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
-            sysui:framePadding="6dp"
-            sysui:activeFrameColor="@color/current_user_border_color" />
+            sysui:framePadding="2.5dp"
+            sysui:badgeDiameter="18dp"
+            sysui:badgeMargin="1dp"
+            sysui:frameColor="@color/qs_user_detail_avatar_frame" />
 </com.android.systemui.qs.tiles.UserDetailItemView>
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 3358a18..af2a285 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -25,13 +25,15 @@
     android:visibility="invisible"
     android:orientation="vertical">
 
+    <com.android.systemui.ResizingSpace
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/qs_detail_margin_top" />
+
     <include
         android:id="@+id/qs_detail_header"
         layout="@layout/qs_detail_header"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="28dp"
-        />
+        android:layout_height="wrap_content" />
 
     <com.android.systemui.statusbar.AlphaOptimizedImageView
         android:id="@+id/qs_detail_header_progress"
diff --git a/packages/SystemUI/res/layout/qs_detail_items.xml b/packages/SystemUI/res/layout/qs_detail_items.xml
index c22e42c..f1a8d63 100644
--- a/packages/SystemUI/res/layout/qs_detail_items.xml
+++ b/packages/SystemUI/res/layout/qs_detail_items.xml
@@ -16,17 +16,19 @@
 -->
 <!-- extends FrameLayout -->
 <com.android.systemui.qs.QSDetailItems xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:sysui="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:paddingTop="16dp"
+    android:paddingTop="@dimen/qs_detail_items_padding_top"
     android:paddingStart="16dp"
     android:paddingEnd="16dp">
 
-    <LinearLayout
+    <com.android.systemui.qs.AutoSizingList
         android:id="@android:id/list"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:orientation="vertical" />
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        sysui:itemHeight="@dimen/qs_detail_item_height" />
 
     <LinearLayout
         android:id="@android:id/empty"
@@ -48,9 +50,4 @@
             android:layout_marginTop="20dp"
             android:textAppearance="@style/TextAppearance.QS.DetailEmpty" />
     </LinearLayout>
-
-    <View
-        android:id="@+id/min_height_spacer"
-        android:layout_width="match_parent"
-        android:layout_height="0dp"/>
-</com.android.systemui.qs.QSDetailItems>
\ No newline at end of file
+</com.android.systemui.qs.QSDetailItems>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index b0dca9a..cce9c0f 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -26,9 +26,7 @@
             android:gravity="center_horizontal"
             android:minLines="2"
             android:padding="0dp"
-            android:fontFamily="sans-serif-condensed"
-            android:textStyle="normal"
-            android:textSize="@dimen/qs_tile_text_size"
+            android:textAppearance="@style/TextAppearance.QS.TileLabel"
             android:clickable="false" />
      <ImageView android:id="@+id/restricted_padlock"
             android:layout_width="@dimen/qs_tile_text_size"
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 661d74a..58fc069 100644
--- a/packages/SystemUI/res/layout/qs_user_detail_item.xml
+++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml
@@ -33,12 +33,16 @@
 
     <com.android.systemui.statusbar.phone.UserAvatarView
             android:id="@+id/user_picture"
-            android:layout_width="@dimen/max_avatar_size"
-            android:layout_height="@dimen/max_avatar_size"
-            android:layout_marginBottom="10dp"
+            android:layout_width="@dimen/framed_avatar_size"
+            android:layout_height="@dimen/framed_avatar_size"
+            android:layout_marginBottom="7dp"
+            android:backgroundTint="@color/qs_user_detail_avatar_tint"
+            android:backgroundTintMode="src_atop"
             systemui:frameWidth="2dp"
-            systemui:framePadding="6dp"
-            systemui:activeFrameColor="@color/current_user_border_color"/>
+            systemui:framePadding="2.5dp"
+            systemui:badgeDiameter="18dp"
+            systemui:badgeMargin="1dp"
+            systemui:frameColor="@color/qs_user_detail_avatar_frame"/>
 
     <LinearLayout
             android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
index 224a0a0..60112be 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons.xml
@@ -112,21 +112,6 @@
         <ImageView
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:scaleType="matrix"
-            android:src="@drawable/screen_pinning_light_bg_circ" />
-
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:paddingEnd="@dimen/screen_pinning_request_inner_padding"
-            android:paddingStart="@dimen/screen_pinning_request_inner_padding"
-            android:paddingTop="@dimen/screen_pinning_request_inner_padding"
-            android:scaleType="matrix"
-            android:src="@drawable/screen_pinning_bg_circ" />
-
-        <ImageView
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
             android:paddingEnd="@dimen/screen_pinning_request_nav_side_padding"
             android:paddingStart="@dimen/screen_pinning_request_nav_side_padding"
             android:paddingTop="@dimen/screen_pinning_request_nav_icon_padding"
diff --git a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
index 1e5193f..ebad7a4 100644
--- a/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
+++ b/packages/SystemUI/res/layout/screen_pinning_request_buttons_land.xml
@@ -43,21 +43,6 @@
         <ImageView
             android:layout_height="match_parent"
             android:layout_width="match_parent"
-            android:scaleType="matrix"
-            android:src="@drawable/screen_pinning_light_bg_circ" />
-
-        <ImageView
-            android:layout_height="match_parent"
-            android:layout_width="match_parent"
-            android:scaleType="matrix"
-            android:paddingLeft="@dimen/screen_pinning_request_inner_padding"
-            android:paddingTop="@dimen/screen_pinning_request_inner_padding"
-            android:paddingBottom="@dimen/screen_pinning_request_inner_padding"
-            android:src="@drawable/screen_pinning_bg_circ" />
-
-        <ImageView
-            android:layout_height="match_parent"
-            android:layout_width="match_parent"
             android:scaleType="center"
             android:paddingLeft="@dimen/screen_pinning_request_nav_icon_padding"
             android:paddingTop="@dimen/screen_pinning_request_nav_side_padding"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 3dca77d..7c4ce15 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -55,6 +55,7 @@
         android:layout_width="match_parent"
         android:layout_height="@dimen/heads_up_scrim_height"
         android:background="@drawable/heads_up_scrim"
+        sysui:ignoreRightInset="true"
         android:importantForAccessibility="no"/>
 
     <include layout="@layout/status_bar"
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index f1be672..c5398a2 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -531,30 +531,18 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Početak"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Nedavni ekrani"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Nazad"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Obavještenja"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Skracenice tastature"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Promijeni način unosa"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplikacije"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Pomoć"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Preglednik"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Kontakti"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-pošta"</string>
+    <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzika"</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Kalendar"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"Prikazati sa kontrolama jačine zvuka"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Ne ometaj"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"Prečica za dugmad za Jačinu zvuka"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 7c2da10..322197e 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -534,7 +534,7 @@
     <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"میان‌برهای صفحه‌کلید"</string>
     <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"تغییر روش ورودی"</string>
     <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"برنامه‌ها"</string>
-    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"همیار"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"دستیار"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"مرورگر"</string>
     <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"مخاطبین"</string>
     <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"رایانامه"</string>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index c8f6649..45c3626 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -530,30 +530,18 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Inicio"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recentes"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Volver"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificacións"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Atallos de teclado"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Cambiar de método de entrada"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicacións"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistente"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Navegador"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Contactos"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Correo electrónico"</string>
+    <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"MI"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Música"</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendario"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"Mostrar cos controis de volume"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Non molestar"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"Atallo dos botóns de volume"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 26a81c8..c40797c 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,4 +24,14 @@
 
     <dimen name="docked_divider_handle_width">2dp</dimen>
     <dimen name="docked_divider_handle_height">16dp</dimen>
+
+    <dimen name="qs_tile_margin_top">2dp</dimen>
+    <dimen name="qs_brightness_padding_top">0dp</dimen>
+
+    <dimen name="battery_detail_graph_space_top">9dp</dimen>
+    <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
+
+    <integer name="quick_settings_num_columns">4</integer>
+    <bool name="quick_settings_wide">true</bool>
+    <dimen name="qs_detail_margin_top">0dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 220250b..eed5f36 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -402,7 +402,7 @@
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"Enheten forblir låst til du låser den opp manuelt"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"Motta varsler raskere"</string>
     <string name="hidden_notifications_text" msgid="2326409389088668981">"Se dem før du låser opp"</string>
-    <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei, takk"</string>
+    <string name="hidden_notifications_cancel" msgid="3690709735122344913">"Nei takk"</string>
     <string name="hidden_notifications_setup" msgid="41079514801976810">"Konfigurer"</string>
     <string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="volume_zen_end_now" msgid="3179845345429841822">"Avslutt nå"</string>
@@ -411,7 +411,7 @@
     <string name="screen_pinning_title" msgid="3273740381976175811">"Skjermen er låst"</string>
     <string name="screen_pinning_description" msgid="3577937698406151604">"På denne måten blir skjermen synlig frem til du låser den opp. Trykk og hold inne Tilbake for å låse opp."</string>
     <string name="screen_pinning_positive" msgid="3783985798366751226">"Skjønner"</string>
-    <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei, takk"</string>
+    <string name="screen_pinning_negative" msgid="3741602308343880268">"Nei takk"</string>
     <string name="quick_settings_reset_confirmation_title" msgid="748792586749897883">"Vil du skjule <xliff:g id="TILE_LABEL">%1$s</xliff:g>?"</string>
     <string name="quick_settings_reset_confirmation_message" msgid="2235970126803317374">"Den vises igjen neste gang du slår den på i innstillingene."</string>
     <string name="quick_settings_reset_confirmation_button" msgid="2660339101868367515">"Skjul"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 0e818c4..81d0456 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -531,30 +531,18 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Ecran de pornire"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Recente"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Înapoi"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Notificări"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Comenzi rapide de la tastatură"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Comutați metoda de introducere a textului"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Aplicații"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Asistent"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Browser"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Agendă"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"E-mail"</string>
+    <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"IM"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Muzică"</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Calendar"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"Afișează cu comenzile de volum"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Nu deranja"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"Comandă rapidă din butoanele de volum"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 1baa67d..86f0ec4 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -532,30 +532,18 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"Главный экран"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"Недавние"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"Назад"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"Уведомления"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"Быстрые клавиши"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"Сменить способ ввода"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"Приложения"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"Помощник"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"Браузер"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"Контакты"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"Эл. почта"</string>
+    <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"Чат"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"Музыка."</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"Календарь"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"Показывать при нажатии кнопок регулировки громкости"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"Не беспокоить"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"Кнопки регулировки громкости"</string>
diff --git a/packages/SystemUI/res/values-sw410dp/config.xml b/packages/SystemUI/res/values-sw410dp/config.xml
new file mode 100644
index 0000000..08b2f88
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="quick_settings_num_rows">2</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
new file mode 100644
index 0000000..5ce6524
--- /dev/null
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <dimen name="qs_detail_items_padding_top">16dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-sw540dp/config.xml b/packages/SystemUI/res/values-sw540dp/config.xml
new file mode 100644
index 0000000..e554fc6d
--- /dev/null
+++ b/packages/SystemUI/res/values-sw540dp/config.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+     for different hardware and product builds. -->
+<resources>
+    <integer name="quick_settings_num_rows">3</integer>
+</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 4ed15d5..49a7a29 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -40,4 +40,8 @@
 
     <dimen name="battery_detail_graph_space_top">27dp</dimen>
     <dimen name="battery_detail_graph_space_bottom">27dp</dimen>
+
+    <dimen name="qs_tile_margin_top">16dp</dimen>
+    <dimen name="qs_brightness_padding_top">6dp</dimen>
+    <dimen name="qs_detail_margin_top">28dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-w550dp-land/dimens.xml b/packages/SystemUI/res/values-w550dp-land/dimens.xml
index cd17bed..4160c83 100644
--- a/packages/SystemUI/res/values-w550dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-w550dp-land/dimens.xml
@@ -20,7 +20,4 @@
     <dimen name="notification_panel_width">544dp</dimen>
 
     <dimen name="qs_expand_margin">32dp</dimen>
-
-    <dimen name="battery_detail_graph_space_top">9dp</dimen>
-    <dimen name="battery_detail_graph_space_bottom">9dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index d559344..a25b9f1 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -34,14 +34,14 @@
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。节电助手已开启。"</string>
+    <string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"剩余<xliff:g id="PERCENTAGE">%s</xliff:g>。省电模式已开启。"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"不支持USB充电功能。\n只能使用随附的充电器充电。"</string>
     <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string>
     <string name="invalid_charger_text" msgid="5474997287953892710">"仅限使用设备随附的充电器。"</string>
     <string name="battery_low_why" msgid="4553600287639198111">"设置"</string>
-    <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启节电助手吗?"</string>
+    <string name="battery_saver_confirmation_title" msgid="5299585433050361634">"要开启省电模式吗?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"开启"</string>
-    <string name="battery_saver_start_action" msgid="5576697451677486320">"开启节电助手"</string>
+    <string name="battery_saver_start_action" msgid="5576697451677486320">"开启省电模式"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"设置"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"WLAN"</string>
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"自动旋转屏幕"</string>
@@ -373,9 +373,9 @@
     <string name="user_remove_user_title" msgid="4681256956076895559">"是否移除用户?"</string>
     <string name="user_remove_user_message" msgid="1453218013959498039">"此用户的所有应用和数据均将被删除。"</string>
     <string name="user_remove_user_remove" msgid="7479275741742178297">"移除"</string>
-    <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已开启"</string>
+    <string name="battery_saver_notification_title" msgid="237918726750955859">"省电模式已开启"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"降低性能并限制后台流量"</string>
-    <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭节电助手"</string>
+    <string name="battery_saver_notification_action_text" msgid="109158658238110382">"关闭省电模式"</string>
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>将开始截取您的屏幕上显示的所有内容。"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"不再显示"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"全部清除"</string>
@@ -497,8 +497,8 @@
     <string name="color_revert_title" msgid="4746666545480534663">"确认设置"</string>
     <string name="color_revert_message" msgid="9116001069397996691">"部分颜色设置可能会导致此设备无法使用。请点击“确定”确认这些颜色设置,否则,系统将在 10 秒后重置这些设置。"</string>
     <string name="battery_panel_title" msgid="7944156115535366613">"电池使用情况"</string>
-    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用节电助手"</string>
-    <string name="battery_detail_switch_title" msgid="6285872470260795421">"节电助手"</string>
+    <string name="battery_detail_charging_summary" msgid="1279095653533044008">"充电过程中无法使用省电模式"</string>
+    <string name="battery_detail_switch_title" msgid="6285872470260795421">"省电模式"</string>
     <string name="battery_detail_switch_summary" msgid="9049111149407626804">"降低性能并限制后台流量"</string>
     <string name="keyboard_key_button_template" msgid="6230056639734377300">"<xliff:g id="NAME">%1$s</xliff:g>按钮"</string>
     <string name="keyboard_key_home" msgid="2243500072071305073">"Home"</string>
@@ -530,30 +530,18 @@
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"主屏幕"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"最近"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"返回"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_im (1892749399083161405) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"通知"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"键盘快捷键"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"切换输入法"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"应用"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"辅助应用"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"浏览器"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"通讯录"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"电子邮件"</string>
+    <string name="keyboard_shortcut_group_applications_im" msgid="1892749399083161405">"即时通讯"</string>
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"音乐"</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"日历"</string>
     <string name="tuner_full_zen_title" msgid="4540823317772234308">"与音量控件一起显示"</string>
     <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"请勿打扰"</string>
     <string name="volume_dnd_silent" msgid="4363882330723050727">"音量按钮快捷键"</string>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 1e979fd..1543360 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -53,10 +53,14 @@
         <enum name="vertical" value="1" />
     </attr>
     <declare-styleable name="UserAvatarView">
+        <attr name="avatarPadding" format="dimension" />
         <attr name="frameWidth" format="dimension" />
         <attr name="framePadding" format="dimension" />
+        <!-- {@deprecated Use a statelist in frameColor instead.} -->
         <attr name="activeFrameColor" format="color" />
-        <attr name="frameColor" />
+        <attr name="frameColor" format="color" />
+        <attr name="badgeDiameter" format="dimension" />
+        <attr name="badgeMargin" format="dimension" />
     </declare-styleable>
     <declare-styleable name="UserDetailItemView">
         <attr name="regularFontFamily" format="string" />
@@ -97,5 +101,9 @@
     <declare-styleable name="DensityContainer">
         <attr name="android:layout" />
     </declare-styleable>
+
+    <declare-styleable name="AutoSizingList">
+        <attr name="itemHeight" format="dimension" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b9aa26b..d9fcf42 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -151,7 +151,7 @@
 
     <color name="docked_divider_background">#ff000000</color>
     <color name="docked_divider_handle">#ffffff</color>
-    <drawable name="forced_resizable_background">#80000000</drawable>
+    <drawable name="forced_resizable_background">#40000000</drawable>
 
     <color name="default_remote_input_background">@*android:color/notification_default_color</color>
     <color name="remote_input_hint">#99ffffff</color>
@@ -169,6 +169,8 @@
     <color name="ksh_system_group_color">#ff00bcd4</color>
     <color name="ksh_application_group_color">#fff44336</color>
     <color name="ksh_keyword_color">#d9000000</color>
+    <color name="ksh_key_item_color">@color/material_grey_600</color>
+    <color name="ksh_key_item_background">#eeeeee</color>
 
     <!-- Background color of edit overflow -->
     <color name="qs_edit_overflow_bg">#455A64</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index fd051b1..42798a4 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -92,11 +92,8 @@
     <!-- The number of columns in the QuickSettings -->
     <integer name="quick_settings_num_columns">3</integer>
 
-    <!-- The maximum number of rows in the QuickSettings -->
-    <integer name="quick_settings_max_rows">4</integer>
-
-    <!-- The maximum number of rows in the QuickSettings when on the keyguard -->
-    <integer name="quick_settings_max_rows_keyguard">3</integer>
+    <!-- The number of rows in the QuickSettings -->
+    <integer name="quick_settings_num_rows">1</integer>
 
     <!-- The number of columns that the top level tiles span in the QuickSettings -->
     <integer name="quick_settings_user_time_settings_tile_span">1</integer>
@@ -116,9 +113,6 @@
     <integer name="quick_settings_brightness_dialog_short_timeout">2000</integer>
     <integer name="quick_settings_brightness_dialog_long_timeout">4000</integer>
 
-    <!-- The maximum number of items to be displayed in quick settings -->
-    <integer name="quick_settings_detail_max_item_count">5</integer>
-
     <!-- Should "4G" be shown instead of "LTE" when the network is NETWORK_TYPE_LTE? -->
     <bool name="config_show4GForLTE">true</bool>
 
@@ -178,7 +172,7 @@
     <!-- Recents: The relative range of visible tasks from the current scroll position
          while the stack is focused. -->
     <item name="recents_layout_focused_range_min" format="float" type="integer">-3</item>
-    <item name="recents_layout_focused_range_max" format="float" type="integer">3</item>
+    <item name="recents_layout_focused_range_max" format="float" type="integer">2</item>
 
     <!-- Recents: The relative range of visible tasks from the current scroll position
          while the stack is not focused. -->
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index 22b7578..40e3b12 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -16,6 +16,10 @@
 
 <resources>
     <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
+         when the PIP menu is shown with settings. -->
+    <string translatable="false" name="pip_settings_bounds">"662 54 1142 324"</string>
+
+    <!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
          when the PIP menu is shown in center. -->
     <string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c094da9..cf2e338 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -172,6 +172,7 @@
 
     <dimen name="qs_tile_height">88dp</dimen>
     <dimen name="qs_tile_margin">16dp</dimen>
+    <dimen name="qs_tile_margin_top">16dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_date_anim_translation">32dp</dimen>
@@ -201,10 +202,12 @@
     <dimen name="qs_detail_item_primary_text_size">16sp</dimen>
     <dimen name="qs_detail_item_secondary_text_size">14sp</dimen>
     <dimen name="qs_detail_empty_text_size">14sp</dimen>
+    <dimen name="qs_detail_margin_top">28dp</dimen>
     <dimen name="qs_data_usage_text_size">14sp</dimen>
     <dimen name="qs_data_usage_usage_text_size">36sp</dimen>
     <dimen name="qs_expand_margin">0dp</dimen>
     <dimen name="qs_battery_padding">2dp</dimen>
+    <dimen name="qs_detail_items_padding_top">4dp</dimen>
 
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
@@ -385,6 +388,9 @@
          quick settings header -->
     <dimen name="max_avatar_size">48dp</dimen>
 
+    <!-- Size of user icon + frame in the qs/keyguard user picker (incl. frame) -->
+    <dimen name="framed_avatar_size">54dp</dimen>
+
     <!-- Margin on the left side of the carrier text on Keyguard -->
     <dimen name="keyguard_carrier_text_margin">16dp</dimen>
 
@@ -561,6 +567,9 @@
 
     <!-- Keyboard shortcuts helper -->
     <dimen name="ksh_layout_width">@dimen/match_parent</dimen>
+    <dimen name="ksh_item_text_size">14sp</dimen>
+    <dimen name="ksh_item_padding">4dp</dimen>
+    <dimen name="ksh_item_margin_start">4dp</dimen>
 
 <!-- Recents Layout -->
 
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/packages/SystemUI/res/values/dimens_tv.xml
index 337513d..f536f86 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/packages/SystemUI/res/values/dimens_tv.xml
@@ -54,5 +54,9 @@
 
     <!-- Extra space around the PIP and its outline in PIP onboarding activity  -->
     <dimen name="tv_pip_bounds_space">3dp</dimen>
+    <!-- Extra space around the PIP control button icon to match with the focused circle -->
+    <dimen name="tv_pip_button_icon_padding">5dp</dimen>
 
+    <!-- Values for entering Recents and exiting Recents -->
+    <dimen name="recents_tv_home_recents_shift">125dip</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
index 20cd330..6984d5a 100644
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ b/packages/SystemUI/res/values/integers_tv.xml
@@ -17,5 +17,11 @@
     <integer name="item_scale_anim_duration">150</integer>
     <integer name="dismiss_short_duration">200</integer>
     <integer name="dismiss_long_duration">400</integer>
+
     <integer name="recents_tv_pip_focus_anim_duration">200</integer>
+
+    <!-- Duration for how long it takes cards to slide in or out when going to and from recents. -->
+    <integer name="recents_home_duration">400</integer>
+    <!-- Delay between the start of slide in animation for each card. -->
+    <integer name="recents_home_delay">40</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 060e050..a33b7a3 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1082,6 +1082,10 @@
     <string name="volume_stream_limited_dnd" translatable="false">%s — Priority only</string>
     <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string>
 
+    <string name="volume_stream_content_description_unmute">%1$s. Tap to unmute.</string>
+    <string name="volume_stream_content_description_vibrate">%1$s. Tap to set to vibrate. Accessibility services may be muted.</string>
+    <string name="volume_stream_content_description_mute">%1$s. Tap to mute. Accessibility services may be muted.</string>
+
     <!-- Name of special SystemUI debug settings -->
     <string name="system_ui_tuner">System UI Tuner</string>
 
@@ -1557,9 +1561,6 @@
     <!-- Accessibility action for moving down the docked stack divider [CHAR LIMIT=NONE] -->
     <string name="accessibility_action_divider_move_right">Move right</string>
 
-    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
-    <string name="forced_resizable_info_text">App may not work with multi-window</string>
-
     <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
     <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
 
@@ -1587,4 +1588,10 @@
     <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
     <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
 
+    <!-- Multi-Window strings -->
+    <!-- Text that gets shown on top of current activity to inform the user that the system force-resized the current activity and that things might crash/not work properly [CHAR LIMIT=NONE] -->
+    <string name="dock_forced_resizable">App may not work with split-screen.</string>
+    <!-- Warning message when we try to dock a non-resizeble tasks and launch it in fullscreen instead. -->
+    <string name="dock_non_resizeble_failed_to_dock_text">App does not support split-screen.</string>
+
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 2b134af..f560a13 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -49,6 +49,9 @@
 
     <style name="Animation.ForcedResizable" parent="@android:style/Animation">
         <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>
+
+        <!-- If the target stack doesn't have focus, we do a task to front animation. -->
+        <item name="android:taskToFrontEnterAnimation">@anim/forced_resizable_enter</item>
         <item name="android:activityCloseExitAnimation">@anim/forced_resizable_exit</item>
     </style>
 
@@ -180,6 +183,11 @@
         <item name="android:textColor">@color/data_usage_secondary</item>
     </style>
 
+    <style name="TextAppearance.QS.TileLabel">
+        <item name="android:textSize">@dimen/qs_tile_text_size</item>
+        <item name="android:fontFamily">sans-serif-condensed</item>
+    </style>
+
     <style name="BaseBrightnessDialogContainer">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
diff --git a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java b/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
deleted file mode 100644
index 1933bbc..0000000
--- a/packages/SystemUI/src/com/android/systemui/BitmapHelper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui;
-
-import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Shader;
-
-public class BitmapHelper {
-    /**
-     * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
-     * to fit and clipped to an inscribed circle.
-     * @param input Bitmap to resize and clip
-     * @param width Width of output bitmap (and diameter of circle)
-     * @param height Height of output bitmap
-     * @return A shiny new bitmap for you to use
-     */
-    public static Bitmap createCircularClip(Bitmap input, int width, int height) {
-        if (input == null) return null;
-
-        final int inWidth = input.getWidth();
-        final int inHeight = input.getHeight();
-        final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        final Canvas canvas = new Canvas(output);
-        final Paint paint = new Paint();
-        paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        paint.setAntiAlias(true);
-        final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
-        final RectF dstRect = new RectF(0, 0, width, height);
-        final Matrix m = new Matrix();
-        m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
-        canvas.setMatrix(m);
-        canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
-        return output;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/ResizingSpace.java b/packages/SystemUI/src/com/android/systemui/ResizingSpace.java
new file mode 100644
index 0000000..c2bc53e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/ResizingSpace.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+
+public class ResizingSpace extends View {
+
+    private final int mWidth;
+    private final int mHeight;
+
+    public ResizingSpace(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        if (getVisibility() == VISIBLE) {
+            setVisibility(INVISIBLE);
+        }
+        TypedArray a = context.obtainStyledAttributes(attrs, android.R.styleable.ViewGroup_Layout);
+        mWidth = a.getResourceId(android.R.styleable.ViewGroup_Layout_layout_width, 0);
+        mHeight = a.getResourceId(android.R.styleable.ViewGroup_Layout_layout_height, 0);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        LayoutParams params = getLayoutParams();
+        boolean changed = false;
+        if (mWidth > 0) {
+            int width = getContext().getResources().getDimensionPixelOffset(mWidth);
+            if (width != params.width) {
+                params.width = width;
+                changed = true;
+            }
+        }
+        if (mHeight > 0) {
+            int height = getContext().getResources().getDimensionPixelOffset(mHeight);
+            if (height != params.height) {
+                params.height = height;
+                changed = true;
+            }
+        }
+        if (changed) {
+            setLayoutParams(params);
+        }
+    }
+
+    /**
+     * Draw nothing.
+     *
+     * @param canvas an unused parameter.
+     */
+    @Override
+    public void draw(Canvas canvas) {
+    }
+
+    /**
+     * Compare to: {@link View#getDefaultSize(int, int)}
+     * If mode is AT_MOST, return the child size instead of the parent size
+     * (unless it is too big).
+     */
+    private static int getDefaultSize2(int size, int measureSpec) {
+        int result = size;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize = MeasureSpec.getSize(measureSpec);
+
+        switch (specMode) {
+            case MeasureSpec.UNSPECIFIED:
+                result = size;
+                break;
+            case MeasureSpec.AT_MOST:
+                result = Math.min(size, specSize);
+                break;
+            case MeasureSpec.EXACTLY:
+                result = specSize;
+                break;
+        }
+        return result;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(
+                getDefaultSize2(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getDefaultSize2(getSuggestedMinimumHeight(), heightMeasureSpec));
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index c0a565db..e838191 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -329,7 +329,7 @@
      */
     public void dismissChild(final View view, float velocity, boolean useAccelerateInterpolator) {
         dismissChild(view, velocity, null /* endAction */, 0 /* delay */,
-                useAccelerateInterpolator, 0 /* fixedDuration */);
+                useAccelerateInterpolator, 0 /* fixedDuration */, false /* isDismissAll */);
     }
 
     /**
@@ -341,17 +341,22 @@
      * @param fixedDuration If not 0, this exact duration will be taken
      */
     public void dismissChild(final View animView, float velocity, final Runnable endAction,
-            long delay, boolean useAccelerateInterpolator, long fixedDuration) {
+            long delay, boolean useAccelerateInterpolator, long fixedDuration,
+            boolean isDismissAll) {
         final boolean canBeDismissed = mCallback.canChildBeDismissed(animView);
         float newPos;
         boolean isLayoutRtl = animView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
 
-        if (velocity < 0
-                || (velocity == 0 && getTranslation(animView) < 0)
-                // if we use the Menu to dismiss an item in landscape, animate up
-                || (velocity == 0 && getTranslation(animView) == 0 && mSwipeDirection == Y)
-                // if the language is rtl we prefer swiping to the left
-                || (velocity == 0 && getTranslation(animView) == 0 && isLayoutRtl)) {
+        // if we use the Menu to dismiss an item in landscape, animate up
+        boolean animateUpForMenu = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
+                && mSwipeDirection == Y;
+        // if the language is rtl we prefer swiping to the left
+        boolean animateLeftForRtl = velocity == 0 && (getTranslation(animView) == 0 || isDismissAll)
+                && isLayoutRtl;
+        boolean animateLeft = velocity < 0
+                || (velocity == 0 && getTranslation(animView) < 0 && !isDismissAll);
+
+        if (animateLeft || animateLeftForRtl || animateUpForMenu) {
             newPos = -getSize(animView);
         } else {
             newPos = getSize(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index b2b6127..455a69f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -123,6 +123,17 @@
         startServicesIfNeeded(SERVICES);
     }
 
+    /**
+     * Ensures that all the Secondary user SystemUI services are running. If they are already
+     * running, this is a no-op. This is needed to conditinally start all the services, as we only
+     * need to have it in the main process.
+     *
+     * <p>This method must only be called from the main thread.</p>
+     */
+    void startSecondaryUserServicesIfNeeded() {
+        startServicesIfNeeded(SERVICES_PER_USER);
+    }
+
     private void startServicesIfNeeded(Class<?>[] services) {
         if (mServicesStarted) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
new file mode 100644
index 0000000..f619bfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUISecondaryUserService.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui;
+
+import android.app.ActivityManager;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Process;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+public class SystemUISecondaryUserService extends Service {
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        ((SystemUIApplication) getApplication()).startSecondaryUserServicesIfNeeded();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
+        if (args == null || args.length == 0) {
+            for (SystemUI ui: services) {
+                pw.println("dumping service: " + ui.getClass().getName());
+                ui.dump(fd, pw, args);
+            }
+        } else {
+            String svc = args[0];
+            for (SystemUI ui: services) {
+                String name = ui.getClass().getName();
+                if (name.endsWith(svc)) {
+                    ui.dump(fd, pw, args);
+                }
+            }
+        }
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index 0d822cb..0798590 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -250,7 +250,6 @@
             FalsingLog.i("onSucccessfulUnlock", "");
         }
         mDataCollector.onSucccessfulUnlock();
-        sessionExitpoint(true /* force */);
     }
 
     public void onBouncerShown() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d2c60ef..84d3599 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -115,9 +115,9 @@
         }
 
         @Override // Binder interface
-        public void onFinishedGoingToSleep(int reason) {
+        public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
             checkPermission();
-            mKeyguardViewMediator.onFinishedGoingToSleep(reason);
+            mKeyguardViewMediator.onFinishedGoingToSleep(reason, cameraGestureTriggered);
         }
 
         @Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a5dfc4b..66754a7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -747,7 +747,7 @@
         notifyStartedGoingToSleep();
     }
 
-    public void onFinishedGoingToSleep(int why) {
+    public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
         if (DEBUG) Log.d(TAG, "onFinishedGoingToSleep(" + why + ")");
         synchronized (this) {
             mDeviceInteractive = false;
@@ -758,6 +758,16 @@
 
             notifyFinishedGoingToSleep();
 
+            if (cameraGestureTriggered) {
+                Log.i(TAG, "Camera gesture was triggered, preventing Keyguard locking.");
+
+                // Just to make sure, make sure the device is awake.
+                mContext.getSystemService(PowerManager.class).wakeUp(SystemClock.uptimeMillis(),
+                        "com.android.systemui:CAMERA_GESTURE_PREVENT_LOCK");
+                mPendingLock = false;
+                mPendingReset = false;
+            }
+
             if (mPendingReset) {
                 resetStateLocked();
                 mPendingReset = false;
@@ -771,7 +781,7 @@
             // We do not have timeout and power button instant lock setting for profile lock.
             // So we use the personal setting if there is any. But if there is no device
             // we need to make sure we lock it immediately when the screen is off.
-            if (!mLockLater) {
+            if (!mLockLater && !cameraGestureTriggered) {
                 doKeyguardForChildProfilesLocked();
             }
 
@@ -794,7 +804,7 @@
 
         // From DevicePolicyAdmin
         final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
-                .getMaximumTimeToLock(null, userId);
+                .getMaximumTimeToLockForUserAndProfiles(userId);
 
         long timeout;
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
new file mode 100644
index 0000000..00e6221
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoSizingList.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.android.systemui.qs;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.database.DataSetObserver;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import com.android.systemui.R;
+
+/**
+ * Similar to a ListView, but it will show only as many items as fit on screen and
+ * bind those instead of scrolling.
+ */
+public class AutoSizingList extends LinearLayout {
+
+    private static final String TAG = "AutoSizingList";
+    private final int mItemSize;
+    private final Handler mHandler;
+
+    private ListAdapter mAdapter;
+    private int mCount;
+
+    public AutoSizingList(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+
+        mHandler = new Handler();
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AutoSizingList);
+        mItemSize = a.getDimensionPixelSize(R.styleable.AutoSizingList_itemHeight, 0);
+    }
+
+    public void setAdapter(ListAdapter adapter) {
+        if (mAdapter != null) {
+            mAdapter.unregisterDataSetObserver(mDataObserver);
+        }
+        mAdapter = adapter;
+        if (adapter != null) {
+            adapter.registerDataSetObserver(mDataObserver);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int requestedHeight = MeasureSpec.getSize(heightMeasureSpec);
+        if (requestedHeight != 0) {
+            int count = Math.min(requestedHeight / mItemSize, getDesiredCount());
+            if (mCount != count) {
+                postRebindChildren();
+                mCount = count;
+            }
+        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    private int getDesiredCount() {
+        return mAdapter != null ? mAdapter.getCount() : 0;
+    }
+
+    private void postRebindChildren() {
+        mHandler.post(mBindChildren);
+    }
+
+    private void rebindChildren() {
+        if (mAdapter == null) {
+            return;
+        }
+        for (int i = 0; i < mCount; i++) {
+            View v = i < getChildCount() ? getChildAt(i) : null;
+            View newView = mAdapter.getView(i, v, this);
+            if (newView != v) {
+                if (v != null) {
+                    removeView(v);
+                }
+                addView(newView, i);
+            }
+        }
+        // Ditch extra views.
+        while (getChildCount() > mCount) {
+            removeViewAt(getChildCount() - 1);
+        }
+    }
+
+    private final Runnable mBindChildren = new Runnable() {
+        @Override
+        public void run() {
+            rebindChildren();
+        }
+    };
+
+    private final DataSetObserver mDataObserver = new DataSetObserver() {
+        @Override
+        public void onChanged() {
+            if (mCount > getDesiredCount()) {
+                mCount = getDesiredCount();
+            }
+            postRebindChildren();
+        }
+
+        @Override
+        public void onInvalidated() {
+            postRebindChildren();
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 115c9d0..5a23610 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -1,6 +1,8 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
 import android.util.AttributeSet;
@@ -199,11 +201,22 @@
 
         @Override
         public boolean updateResources() {
-            if (super.updateResources()) {
-                mMaxRows = mColumns != 3 ? 2 : 3;
-                return true;
+            final int rows = getRows();
+            boolean changed = rows != mMaxRows;
+            if (changed) {
+                mMaxRows = rows;
+                requestLayout();
             }
-            return false;
+            return super.updateResources() || changed;
+        }
+
+        private int getRows() {
+            final Resources res = getContext().getResources();
+            if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+                // Always have 3 rows in portrait.
+                return 3;
+            }
+            return Math.max(1, res.getInteger(R.integer.quick_settings_num_rows));
         }
 
         public void setMaxRows(int maxRows) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index de8eec4..4d959d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -157,7 +157,7 @@
         for (QSTile<?> tile : tiles) {
             QSTileBaseView tileView = mQsPanel.getTileView(tile);
             final TextView label = ((QSTileView) tileView).getLabel();
-            final View tileIcon = tileView.getIcon();
+            final View tileIcon = tileView.getIcon().getIconView();
             if (count < mNumQuickTiles && mAllowFancy) {
                 // Quick tiles.
                 QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
index 5d06aeb..e3a4909 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainer.java
@@ -47,10 +47,10 @@
     private final Rect mQsBounds = new Rect();
 
     private int mHeightOverride = -1;
-    private QSPanel mQSPanel;
+    protected QSPanel mQSPanel;
     private QSDetail mQSDetail;
     protected BaseStatusBarHeader mHeader;
-    private float mQsExpansion;
+    protected float mQsExpansion;
     private boolean mQsExpanded;
     private boolean mHeaderAnimating;
     private boolean mKeyguardShowing;
@@ -100,7 +100,13 @@
         // Since we control our own bottom, be whatever size we want.
         // Otherwise the QSPanel ends up with 0 height when the window is only the
         // size of the status bar.
-        super.onMeasure(widthMeasureSpec, MeasureSpec.UNSPECIFIED);
+        mQSPanel.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(
+                MeasureSpec.getSize(heightMeasureSpec), MeasureSpec.UNSPECIFIED));
+        int width = mQSPanel.getMeasuredWidth();
+        int height = ((LayoutParams) mQSPanel.getLayoutParams()).topMargin
+                + mQSPanel.getMeasuredHeight();
+        super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
 
         // QSCustomizer is always be the height of the screen, but do this after
         // other measuring to avoid changing the height of the QSContainer.
@@ -156,14 +162,18 @@
     }
 
     private void updateBottom() {
-        int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
-        int height = mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
-                : (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
-                + mHeader.getCollapsedHeight();
+        int height = calculateContainerHeight();
         setBottom(getTop() + height);
         mQSDetail.setBottom(getTop() + height);
     }
 
+    protected int calculateContainerHeight() {
+        int heightOverride = mHeightOverride != -1 ? mHeightOverride : getMeasuredHeight();
+        return mQSCustomizer.isCustomizing() ? mQSCustomizer.getHeight()
+                : (int) (mQsExpansion * (heightOverride - mHeader.getCollapsedHeight()))
+                + mHeader.getCollapsedHeight();
+    }
+
     private void updateQsState() {
         boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating;
         mQSPanel.setExpanded(mQsExpanded);
@@ -296,4 +306,8 @@
             updateQsState();
         }
     };
+
+    public int getQsMinExpansionHeight() {
+        return mHeader.getHeight();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
index 25b9105..2dd4a0a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetailItems.java
@@ -28,11 +28,10 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
-
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 
@@ -45,16 +44,17 @@
 
     private final Context mContext;
     private final H mHandler = new H();
+    private final Adapter mAdapter = new Adapter();
 
     private String mTag;
     private Callback mCallback;
     private boolean mItemsVisible = true;
-    private LinearLayout mItems;
+    private AutoSizingList mItemList;
     private View mEmpty;
-    private View mMinHeightSpacer;
     private TextView mEmptyText;
     private ImageView mEmptyIcon;
-    private int mMaxItems;
+
+    private Item[] mItems;
 
     public QSDetailItems(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -73,27 +73,22 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mItems = (LinearLayout) findViewById(android.R.id.list);
-        mItems.setVisibility(GONE);
+        mItemList = (AutoSizingList) findViewById(android.R.id.list);
+        mItemList.setVisibility(GONE);
+        mItemList.setAdapter(mAdapter);
         mEmpty = findViewById(android.R.id.empty);
         mEmpty.setVisibility(GONE);
         mEmptyText = (TextView) mEmpty.findViewById(android.R.id.title);
         mEmptyIcon = (ImageView) mEmpty.findViewById(android.R.id.icon);
-        mMinHeightSpacer = findViewById(R.id.min_height_spacer);
-
-        // By default, a detail item view has fixed size.
-        mMaxItems = getResources().getInteger(
-                R.integer.quick_settings_detail_max_item_count);
-        setMinHeightInItems(mMaxItems);
     }
 
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         FontSizeUtils.updateFontSize(mEmptyText, R.dimen.qs_detail_empty_text_size);
-        int count = mItems.getChildCount();
+        int count = mItemList.getChildCount();
         for (int i = 0; i < count; i++) {
-            View item = mItems.getChildAt(i);
+            View item = mItemList.getChildAt(i);
             FontSizeUtils.updateFontSize(item, android.R.id.title,
                     R.dimen.qs_detail_item_primary_text_size);
             FontSizeUtils.updateFontSize(item, android.R.id.summary,
@@ -110,16 +105,6 @@
         mEmptyText.setText(text);
     }
 
-    /**
-     * Set the minimum height of this detail view, in item count.
-     */
-    public void setMinHeightInItems(int minHeightInItems) {
-        ViewGroup.LayoutParams lp = mMinHeightSpacer.getLayoutParams();
-        lp.height = minHeightInItems * getResources().getDimensionPixelSize(
-                R.dimen.qs_detail_item_height);
-        mMinHeightSpacer.setLayoutParams(lp);
-    }
-
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -153,65 +138,82 @@
     }
 
     private void handleSetItems(Item[] items) {
-        final int itemCount = items != null ? Math.min(items.length, mMaxItems) : 0;
+        final int itemCount = items != null ? items.length : 0;
         mEmpty.setVisibility(itemCount == 0 ? VISIBLE : GONE);
-        mItems.setVisibility(itemCount == 0 ? GONE : VISIBLE);
-        for (int i = mItems.getChildCount() - 1; i >= itemCount; i--) {
-            mItems.removeViewAt(i);
-        }
-        for (int i = 0; i < itemCount; i++) {
-            bind(items[i], mItems.getChildAt(i));
-        }
+        mItemList.setVisibility(itemCount == 0 ? GONE : VISIBLE);
+        mItems = items;
+        mAdapter.notifyDataSetChanged();
     }
 
     private void handleSetItemsVisible(boolean visible) {
         if (mItemsVisible == visible) return;
         mItemsVisible = visible;
-        for (int i = 0; i < mItems.getChildCount(); i++) {
-            mItems.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+        for (int i = 0; i < mItemList.getChildCount(); i++) {
+            mItemList.getChildAt(i).setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
         }
     }
 
-    private void bind(final Item item, View view) {
-        if (view == null) {
-            view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, this, false);
-            mItems.addView(view);
+    private class Adapter extends BaseAdapter {
+
+        @Override
+        public int getCount() {
+            return mItems != null ? mItems.length : 0;
         }
-        view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
-        final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
-        iv.setImageResource(item.icon);
-        iv.getOverlay().clear();
-        if (item.overlay != null) {
-            item.overlay.setBounds(0, 0, item.overlay.getIntrinsicWidth(),
-                    item.overlay.getIntrinsicHeight());
-            iv.getOverlay().add(item.overlay);
+
+        @Override
+        public Object getItem(int position) {
+            return mItems[position];
         }
-        final TextView title = (TextView) view.findViewById(android.R.id.title);
-        title.setText(item.line1);
-        final TextView summary = (TextView) view.findViewById(android.R.id.summary);
-        final boolean twoLines = !TextUtils.isEmpty(item.line2);
-        title.setMaxLines(twoLines ? 1 : 2);
-        summary.setVisibility(twoLines ? VISIBLE : GONE);
-        summary.setText(twoLines ? item.line2 : null);
-        view.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mCallback != null) {
-                    mCallback.onDetailItemClick(item);
-                }
+
+        @Override
+        public long getItemId(int position) {
+            return 0;
+        }
+
+        @Override
+        public View getView(int position, View view, ViewGroup parent) {
+            final Item item = mItems[position];
+            if (view == null) {
+                view = LayoutInflater.from(mContext).inflate(R.layout.qs_detail_item, parent,
+                        false);
             }
-        });
-        final ImageView disconnect = (ImageView) view.findViewById(android.R.id.icon2);
-        disconnect.setVisibility(item.canDisconnect ? VISIBLE : GONE);
-        disconnect.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                if (mCallback != null) {
-                    mCallback.onDetailItemDisconnect(item);
-                }
+            view.setVisibility(mItemsVisible ? VISIBLE : INVISIBLE);
+            final ImageView iv = (ImageView) view.findViewById(android.R.id.icon);
+            iv.setImageResource(item.icon);
+            iv.getOverlay().clear();
+            if (item.overlay != null) {
+                item.overlay.setBounds(0, 0, item.overlay.getIntrinsicWidth(),
+                        item.overlay.getIntrinsicHeight());
+                iv.getOverlay().add(item.overlay);
             }
-        });
-    }
+            final TextView title = (TextView) view.findViewById(android.R.id.title);
+            title.setText(item.line1);
+            final TextView summary = (TextView) view.findViewById(android.R.id.summary);
+            final boolean twoLines = !TextUtils.isEmpty(item.line2);
+            title.setMaxLines(twoLines ? 1 : 2);
+            summary.setVisibility(twoLines ? VISIBLE : GONE);
+            summary.setText(twoLines ? item.line2 : null);
+            view.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mCallback != null) {
+                        mCallback.onDetailItemClick(item);
+                    }
+                }
+            });
+            final ImageView disconnect = (ImageView) view.findViewById(android.R.id.icon2);
+            disconnect.setVisibility(item.canDisconnect ? VISIBLE : GONE);
+            disconnect.setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    if (mCallback != null) {
+                        mCallback.onDetailItemDisconnect(item);
+                    }
+                }
+            });
+            return view;
+        }
+    };
 
     private class H extends Handler {
         private static final int SET_ITEMS = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index 546f8c3..6c224f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -30,9 +30,9 @@
 
 public class QSIconView extends ViewGroup {
 
-    private final View mIcon;
-    private final int mIconSizePx;
-    private final int mTilePaddingBelowIconPx;
+    protected final View mIcon;
+    protected final int mIconSizePx;
+    protected final int mTilePaddingBelowIconPx;
     private boolean mAnimationEnabled = true;
 
     public QSIconView(Context context) {
@@ -50,6 +50,10 @@
         mAnimationEnabled = false;
     }
 
+    public View getIconView() {
+        return mIcon;
+    }
+
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         final int w = MeasureSpec.getSize(widthMeasureSpec);
@@ -109,11 +113,11 @@
         return icon;
     }
 
-    protected static int exactly(int size) {
+    protected final int exactly(int size) {
         return MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY);
     }
 
-    protected static void layout(View child, int left, int top) {
+    protected final void layout(View child, int left, int top) {
         child.layout(left, top, left + child.getMeasuredWidth(), top + child.getMeasuredHeight());
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index a05818a..1149c59 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -85,20 +85,7 @@
                 R.layout.quick_settings_brightness_dialog, this, false);
         addView(mBrightnessView);
 
-        mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
-                R.layout.qs_paged_tile_layout, this, false);
-        addView((View) mTileLayout);
-        findViewById(android.R.id.edit).setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(final View v) {
-                mHost.startRunnableDismissingKeyguard(new Runnable() {
-                    @Override
-                    public void run() {
-                        showEdit(v);
-                    }
-                });
-            }
-        });
+        setupTileLayout();
 
         mFooter = new QSFooter(this, context);
         addView(mFooter.getView());
@@ -111,6 +98,14 @@
 
     }
 
+    protected void setupTileLayout() {
+        mTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate(
+                R.layout.qs_paged_tile_layout, this, false);
+        addView((View) mTileLayout);
+        findViewById(android.R.id.edit).setOnClickListener(view ->
+                mHost.startRunnableDismissingKeyguard(() -> showEdit(view)));
+    }
+
     public boolean isShowingCustomize() {
         return mCustomizePanel != null && mCustomizePanel.isCustomizing();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
index 57a1a4a..c3766e8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java
@@ -33,7 +33,6 @@
 /** View that represents a standard quick settings tile. **/
 public class QSTileView extends QSTileBaseView {
     protected final Context mContext;
-    private QSIconView mIconView;
     private final int mTileSpacingPx;
     private int mTilePaddingTopPx;
 
@@ -44,7 +43,6 @@
         super(context, icon);
 
         mContext = context;
-        mIconView = icon;
         final Resources res = context.getResources();
         mTileSpacingPx = res.getDimensionPixelSize(R.dimen.qs_tile_spacing);
 
@@ -82,13 +80,13 @@
     }
 
     protected void createLabel() {
-        final Resources res = mContext.getResources();
         View view = LayoutInflater.from(mContext).inflate(R.layout.qs_tile_label, null);
         mLabel = (TextView) view.findViewById(R.id.tile_label);
         mPadLock = (ImageView) view.findViewById(R.id.restricted_padlock);
         addView(view);
     }
 
+    @Override
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         if (!Objects.equal(mLabel.getText(), state.label)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 55eda98..1e3c458 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -19,11 +19,12 @@
     private static final String TAG = "TileLayout";
 
     protected int mColumns;
-    private int mCellWidth;
-    private int mCellHeight;
-    private int mCellMargin;
+    protected int mCellWidth;
+    protected int mCellHeight;
+    protected int mCellMargin;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
+    private int mCellMarginTop;
 
     public TileLayout(Context context) {
         this(context, null);
@@ -60,9 +61,10 @@
         final int columns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns));
         mCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height);
         mCellMargin = res.getDimensionPixelSize(R.dimen.qs_tile_margin);
+        mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
         if (mColumns != columns) {
             mColumns = columns;
-            postInvalidate();
+            requestLayout();
             return true;
         }
         return false;
@@ -81,7 +83,8 @@
             record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight));
             previousView = record.tileView.updateAccessibilityOrder(previousView);
         }
-        setMeasuredDimension(width, (mCellHeight + mCellMargin) * rows);
+        setMeasuredDimension(width,
+                (mCellHeight + mCellMargin) * rows + (mCellMarginTop - mCellMargin));
     }
 
     private static int exactly(int size) {
@@ -114,7 +117,7 @@
     }
 
     private int getRowTop(int row) {
-        return row * (mCellHeight + mCellMargin) + mCellMargin;
+        return row * (mCellHeight + mCellMargin) + mCellMarginTop;
     }
 
     private int getColumnStart(int column) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 2ba4044..e8e17b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -105,7 +105,7 @@
 
     public void saveSpecs(QSTileHost host) {
         List<String> newSpecs = new ArrayList<>();
-        for (int i = 0; mTiles.get(i) != null; i++) {
+        for (int i = 0; i < mTiles.size() && mTiles.get(i) != null; i++) {
             newSpecs.add(mTiles.get(i).spec);
         }
         host.changeTiles(mCurrentSpecs, newSpecs);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 6114573..6b20681 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -138,7 +138,7 @@
         mListening = listening;
         try {
             if (listening) {
-                if (mServiceManager.getType() == TileService.TILE_MODE_PASSIVE) {
+                if (!mServiceManager.isActiveTile()) {
                     mServiceManager.setBindRequested(true);
                     mService.onStartListening();
                 }
@@ -209,7 +209,7 @@
         } catch (RemoteException e) {
         }
         try {
-            if (mServiceManager.getType() == TileService.TILE_MODE_ACTIVE) {
+            if (mServiceManager.isActiveTile()) {
                 mServiceManager.setBindRequested(true);
                 mService.onStartListening();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 8910d44..5a26a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -15,8 +15,6 @@
  */
 package com.android.systemui.qs.external;
 
-import libcore.util.Objects;
-
 import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -25,6 +23,7 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.net.Uri;
 import android.os.Handler;
@@ -34,9 +33,11 @@
 import android.service.quicksettings.IQSService;
 import android.service.quicksettings.IQSTileService;
 import android.service.quicksettings.Tile;
+import android.service.quicksettings.TileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.ArraySet;
 import android.util.Log;
+import libcore.util.Objects;
 
 import java.util.Set;
 
@@ -98,6 +99,17 @@
         }
     }
 
+    public boolean isActiveTile() {
+        try {
+            ServiceInfo info = mContext.getPackageManager().getServiceInfo(mIntent.getComponent(),
+                    PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_META_DATA);
+            return info.metaData != null
+                    && info.metaData.getBoolean(TileService.META_DATA_ACTIVE_TILE, false);
+        } catch (NameNotFoundException e) {
+            return false;
+        }
+    }
+
     /**
      * Binds just long enough to send any queued messages, then unbinds.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 664ddd6..ab21532 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -21,7 +21,6 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.TileService;
 import android.support.annotation.VisibleForTesting;
 import android.util.Log;
 
@@ -51,7 +50,6 @@
     private int mPriority;
     private boolean mJustBound;
     private long mLastUpdate;
-    private int mType;
     private boolean mShowingDialog;
     // Whether we have a pending bind going out to the service without a response yet.
     // This defaults to true to ensure tiles start out unavailable.
@@ -69,25 +67,11 @@
         mServices = tileServices;
         mHandler = handler;
         mStateManager = tileLifecycleManager;
-        mType = tileServices.getContext().getSharedPreferences(PREFS_FILE, 0)
-                .getInt(tileLifecycleManager.getComponent().flattenToString(),
-                        TileService.TILE_MODE_UNSET);
         mStateManager.setQSService(tileServices);
-        if (mType == TileService.TILE_MODE_UNSET) {
-            bindService();
-            mStateManager.onTileAdded();
-        }
     }
 
-    public int getType() {
-        return mType;
-    }
-
-    public void setType(int type) {
-        mServices.getContext().getSharedPreferences(PREFS_FILE, 0).edit()
-                .putInt(mStateManager.getComponent().flattenToString(), type).commit();
-        mType = type;
-        mServices.recalculateBindAllowance();
+    public boolean isActiveTile() {
+        return mStateManager.isActiveTile();
     }
 
     public void setShowingDialog(boolean dialog) {
@@ -114,7 +98,7 @@
 
     public void setLastUpdate(long lastUpdate) {
         mLastUpdate = lastUpdate;
-        if (mBound && mType == TileService.TILE_MODE_ACTIVE) {
+        if (mBound && isActiveTile()) {
             mStateManager.onStopListening();
             setBindRequested(false);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 5bb2a35..f36d013 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -153,7 +153,7 @@
                 return;
             }
             TileServiceManager service = mServices.get(customTile);
-            if (service.getType() != TileService.TILE_MODE_ACTIVE) {
+            if (!service.isActiveTile()) {
                 return;
             }
             service.setBindRequested(true);
@@ -165,17 +165,6 @@
     }
 
     @Override
-    public void setTileMode(ComponentName component, int mode) {
-        verifyCaller(component.getPackageName());
-        CustomTile customTile = getTileForComponent(component);
-        if (customTile != null) {
-            synchronized (mServices) {
-                mServices.get(customTile).setType(mode);
-            }
-        }
-    }
-
-    @Override
     public void updateQsTile(Tile tile) {
         ComponentName componentName = tile.getComponentName();
         verifyCaller(componentName.getPackageName());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index 2032783..e494fd8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -25,6 +25,7 @@
 import android.text.SpannableStringBuilder;
 import android.text.Spanned;
 import android.text.style.RelativeSizeSpan;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnAttachStateChangeListener;
@@ -207,18 +208,21 @@
                     }
                 }
             });
+            final TextView batterySaverTitle =
+                    (TextView) mCurrentView.findViewById(android.R.id.title);
+            final TextView batterySaverSummary =
+                    (TextView) mCurrentView.findViewById(android.R.id.summary);
             if (mCharging) {
-                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
-                        R.string.battery_detail_charging_summary);
-                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.INVISIBLE);
+                mCurrentView.findViewById(R.id.switch_container).setAlpha(.7f);
+                batterySaverTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+                batterySaverTitle.setText(R.string.battery_detail_charging_summary);
                 mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.GONE);
                 mCurrentView.findViewById(R.id.switch_container).setClickable(false);
             } else {
-                ((TextView) mCurrentView.findViewById(android.R.id.title)).setText(
-                        R.string.battery_detail_switch_title);
-                ((TextView) mCurrentView.findViewById(android.R.id.summary)).setText(
-                        R.string.battery_detail_switch_summary);
-                mCurrentView.findViewById(android.R.id.icon).setVisibility(View.VISIBLE);
+                mCurrentView.findViewById(R.id.switch_container).setAlpha(1);
+                batterySaverTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
+                batterySaverTitle.setText(R.string.battery_detail_switch_title);
+                batterySaverSummary.setText(R.string.battery_detail_switch_summary);
                 mCurrentView.findViewById(android.R.id.toggle).setVisibility(View.VISIBLE);
                 mCurrentView.findViewById(R.id.switch_container).setClickable(true);
                 mCurrentView.findViewById(R.id.switch_container).setOnClickListener(this);
@@ -227,7 +231,7 @@
 
         private void bindBatteryInfo(BatteryInfo info) {
             SpannableStringBuilder builder = new SpannableStringBuilder();
-            builder.append(info.batteryPercentString, new RelativeSizeSpan(2),
+            builder.append(info.batteryPercentString, new RelativeSizeSpan(2.6f),
                     Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
             if (info.remainingLabel != null) {
                 if (mContext.getResources().getBoolean(R.bool.quick_settings_wide)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 1fef8f1..63c85db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -216,7 +216,6 @@
             mItems.setEmptyState(R.drawable.ic_qs_bluetooth_detail_empty,
                     R.string.quick_settings_bluetooth_detail_empty_text);
             mItems.setCallback(this);
-            mItems.setMinHeightInItems(0);
             updateItems();
             setItemsVisible(mState.value);
             return mItems;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
index fcf758b..99eae02 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailItemView.java
@@ -30,6 +30,7 @@
 import android.widget.TextView;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.UserAvatarView;
@@ -87,25 +88,29 @@
         return (UserDetailItemView) convertView;
     }
 
-    public void bind(String name, Bitmap picture) {
+    public void bind(String name, Bitmap picture, int userId) {
         mName.setText(name);
-        mAvatar.setBitmap(picture);
+        mAvatar.setAvatarWithBadge(picture, userId);
     }
 
-    public void bind(String name, Drawable picture) {
+    public void bind(String name, Drawable picture, int userId) {
         mName.setText(name);
-        mAvatar.setDrawable(picture);
+        mAvatar.setDrawableWithBadge(picture, userId);
+    }
+
+    public void setAvatarEnabled(boolean enabled) {
+        mAvatar.setEnabled(enabled);
     }
 
     public void setDisabledByAdmin(boolean disabled) {
         mRestrictedPadlock.setVisibility(disabled ? View.VISIBLE : View.GONE);
         mName.setEnabled(!disabled);
-        mAvatar.setDisabled(disabled);
+        mAvatar.setEnabled(!disabled);
     }
 
     public void setEnabled(boolean enabled) {
         mName.setEnabled(enabled);
-        mAvatar.setDisabled(!enabled);
+        mAvatar.setEnabled(enabled);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index da98762..d4fa765 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -76,9 +76,9 @@
             }
             String name = getName(mContext, item);
             if (item.picture == null) {
-                v.bind(name, getDrawable(mContext, item));
+                v.bind(name, getDrawable(mContext, item), item.resolveId());
             } else {
-                v.bind(name, item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             v.setActivated(item.isCurrent);
             v.setDisabledByAdmin(item.isDisabledByAdmin);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 8060e07..b1d9555 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -78,6 +78,9 @@
 import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.statusbar.BaseStatusBar;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
 /**
  * The main Recents activity that is started from AlternateRecentsComponent.
  */
@@ -733,4 +736,20 @@
         });
         return true;
     }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" visible="); writer.print(mIsVisible ? "Y" : "N");
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (mRecentsView != null) {
+            mRecentsView.dump(prefix, writer);
+        }
+        EventBus.getDefault().dump(prefix, writer);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 4dae746..fda340d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents;
 
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
+import static android.view.View.MeasureSpec;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -36,7 +37,6 @@
 import android.util.MutableBoolean;
 import android.view.AppTransitionAnimationSpec;
 import android.view.LayoutInflater;
-import android.view.View;
 import android.view.ViewConfiguration;
 
 import com.android.internal.logging.MetricsLogger;
@@ -45,7 +45,6 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
-import com.android.systemui.recents.events.activity.ForcedResizableEvent;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
 import com.android.systemui.recents.events.activity.IterateRecentsEvent;
 import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
@@ -125,13 +124,6 @@
                 loader.loadTasks(mContext, plan, launchOpts);
             }
         }
-
-        @Override
-        public void onActivityForcedResizable(String packageName, int taskId) {
-            EventBus.getDefault().sendOntoMainThread(
-                    new ForcedResizableEvent(packageName, taskId));
-
-        }
     }
 
     protected static RecentsTaskLoadPlan sInstanceLoadPlan;
@@ -144,7 +136,6 @@
 
     // Task launching
     Rect mTaskStackBounds = new Rect();
-    Rect mLastTaskViewBounds = new Rect();
     TaskViewTransform mTmpTransform = new TaskViewTransform();
     int mStatusBarHeight;
     int mNavBarHeight;
@@ -169,8 +160,7 @@
         }
     });
 
-    protected Bitmap mThumbnailTransitionBitmapCache;
-    Task mThumbnailTransitionBitmapCacheKey;
+    protected Bitmap mThumbTransitionBitmapCache;
 
     public RecentsImpl(Context context) {
         mContext = context;
@@ -186,7 +176,6 @@
 
         // Initialize the static configuration resources
         reloadHeaderBarLayout();
-        updateHeaderBarLayout(null /* stack */);
 
         // When we start, preload the data associated with the previous recent tasks.
         // We can use a new plan since the caches will be the same.
@@ -201,12 +190,11 @@
     }
 
     public void onBootCompleted() {
-        updateHeaderBarLayout(null /* stack */);
+        // Do nothing
     }
 
     public void onConfigurationChanged() {
         reloadHeaderBarLayout();
-        updateHeaderBarLayout(null /* stack */);
     }
 
     /**
@@ -368,9 +356,14 @@
             loader.preloadTasks(sInstanceLoadPlan, topTask.id, topTaskHome.value);
             TaskStack stack = sInstanceLoadPlan.getTaskStack();
             if (stack.getTaskCount() > 0) {
-                // We try and draw the thumbnail transition bitmap in parallel before
-                // toggle/show recents is called
-                preCacheThumbnailTransitionBitmapAsync(topTask, stack, mDummyStackView);
+                // Only preload the icon (but not the thumbnail since it may not have been taken for
+                // the pausing activity)
+                preloadIcon(topTask);
+
+                // At this point, we don't know anything about the stack state.  So only calculate
+                // the dimensions of the thumbnail that we need for the transition into Recents, but
+                // do not draw it until we construct the activity options when we start Recents
+                updateHeaderBarLayout(stack);
             }
         }
     }
@@ -601,20 +594,31 @@
         if (stack != null) {
             stackLayout.getTaskStackBounds(windowRect, systemInsets.top, systemInsets.right,
                     mTaskStackBounds);
+            stackLayout.reset();
             stackLayout.initialize(windowRect, mTaskStackBounds,
                     TaskStackLayoutAlgorithm.StackState.getStackStateForStack(stack));
             mDummyStackView.setTasks(stack, false /* allowNotifyStackChanges */);
-        }
-        Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
-        if (!taskViewBounds.equals(mLastTaskViewBounds)) {
-            mLastTaskViewBounds.set(taskViewBounds);
 
-            int taskViewWidth = taskViewBounds.width();
-            synchronized (mHeaderBarLock) {
-                mHeaderBar.measure(
-                    View.MeasureSpec.makeMeasureSpec(taskViewWidth, View.MeasureSpec.EXACTLY),
-                    View.MeasureSpec.makeMeasureSpec(mTaskBarHeight, View.MeasureSpec.EXACTLY));
-                mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+            Rect taskViewBounds = stackLayout.getUntransformedTaskViewBounds();
+            if (!taskViewBounds.isEmpty()) {
+                int taskViewWidth = taskViewBounds.width();
+                synchronized (mHeaderBarLock) {
+                    if (mHeaderBar.getMeasuredWidth() != taskViewWidth ||
+                            mHeaderBar.getMeasuredHeight() != mTaskBarHeight) {
+                        mHeaderBar.measure(
+                                MeasureSpec.makeMeasureSpec(taskViewWidth, MeasureSpec.EXACTLY),
+                                MeasureSpec.makeMeasureSpec(mTaskBarHeight, MeasureSpec.EXACTLY));
+                    }
+                    mHeaderBar.layout(0, 0, taskViewWidth, mTaskBarHeight);
+                }
+
+                // Update the transition bitmap to match the new header bar height
+                if (mThumbTransitionBitmapCache == null ||
+                        (mThumbTransitionBitmapCache.getWidth() != taskViewWidth) ||
+                        (mThumbTransitionBitmapCache.getHeight() != mTaskBarHeight)) {
+                    mThumbTransitionBitmapCache = Bitmap.createBitmap(taskViewWidth,
+                            mTaskBarHeight, Bitmap.Config.ARGB_8888);
+                }
             }
         }
     }
@@ -652,40 +656,6 @@
     }
 
     /**
-     * Caches the header thumbnail used for a window animation asynchronously into
-     * {@link #mThumbnailTransitionBitmapCache}.
-     */
-    private void preCacheThumbnailTransitionBitmapAsync(ActivityManager.RunningTaskInfo topTask,
-            TaskStack stack, TaskStackView stackView) {
-        preloadIcon(topTask);
-
-        // Update the header bar if necessary
-        updateHeaderBarLayout(stack);
-
-        // Update the destination rect
-        final Task toTask = new Task();
-        final TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
-        ForegroundThread.getHandler().postAtFrontOfQueue(new Runnable() {
-            @Override
-            public void run() {
-                final Bitmap transitionBitmap = drawThumbnailTransitionBitmap(toTask, toTransform);
-                if (transitionBitmap != null) {
-                    mHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            mThumbnailTransitionBitmapCache = transitionBitmap;
-                            mThumbnailTransitionBitmapCacheKey = toTask;
-                        }
-                    });
-                } else {
-                    Log.e(TAG, "Could not load thumbnail for task: " + toTask + " at transform: " +
-                            toTransform);
-                }
-            }
-        });
-    }
-
-    /**
      * Creates the activity options for a unknown state->recents transition.
      */
     protected ActivityOptions getUnknownTransitionActivityOptions() {
@@ -724,9 +694,10 @@
                 if (task.isFreeformTask()) {
                     mTmpTransform = stackLayout.getStackTransformScreenCoordinates(task,
                                     stackScroller.getStackScroll(), mTmpTransform, null);
+                    Bitmap thumbnail = drawThumbnailTransitionBitmap(task, mTmpTransform,
+                            mThumbTransitionBitmapCache);
                     Rect toTaskRect = new Rect();
                     mTmpTransform.rect.round(toTaskRect);
-                    Bitmap thumbnail = getThumbnailBitmap(topTask, task, mTmpTransform);
                     specs.add(new AppTransitionAnimationSpec(task.key.id, thumbnail, toTaskRect));
                 }
             }
@@ -738,9 +709,10 @@
             // Update the destination rect
             Task toTask = new Task();
             TaskViewTransform toTransform = getThumbnailTransitionTransform(stackView, toTask);
-            RectF toTaskRect = toTransform.rect;
-            Bitmap thumbnail = getThumbnailBitmap(topTask, toTask, toTransform);
+            Bitmap thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform,
+                    mThumbTransitionBitmapCache);
             if (thumbnail != null) {
+                RectF toTaskRect = toTransform.rect;
                 return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
                         thumbnail, (int) toTaskRect.left, (int) toTaskRect.top,
                         (int) toTaskRect.width(), (int) toTaskRect.height(), mHandler, null);
@@ -750,22 +722,6 @@
         }
     }
 
-    private Bitmap getThumbnailBitmap(ActivityManager.RunningTaskInfo topTask, Task toTask,
-            TaskViewTransform toTransform) {
-        Bitmap thumbnail;
-        if (mThumbnailTransitionBitmapCacheKey != null
-                && mThumbnailTransitionBitmapCacheKey.key != null
-                && mThumbnailTransitionBitmapCacheKey.key.equals(toTask.key)) {
-            thumbnail = mThumbnailTransitionBitmapCache;
-            mThumbnailTransitionBitmapCacheKey = null;
-            mThumbnailTransitionBitmapCache = null;
-        } else {
-            preloadIcon(topTask);
-            thumbnail = drawThumbnailTransitionBitmap(toTask, toTransform);
-        }
-        return thumbnail;
-    }
-
     /**
      * Returns the transition rect for the given task id.
      */
@@ -793,26 +749,19 @@
     /**
      * Draws the header of a task used for the window animation into a bitmap.
      */
-    private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform) {
+    private Bitmap drawThumbnailTransitionBitmap(Task toTask, TaskViewTransform toTransform,
+            Bitmap thumbnail) {
         SystemServicesProxy ssp = Recents.getSystemServices();
         if (toTransform != null && toTask.key != null) {
-            Bitmap thumbnail;
             synchronized (mHeaderBarLock) {
-                int toHeaderWidth = (int) toTransform.rect.width();
-                int toHeaderHeight = (int) (mHeaderBar.getMeasuredHeight() * toTransform.scale);
-                if (toHeaderWidth <= 0 || toHeaderHeight <= 0) {
-                    return null;
-                }
                 boolean disabledInSafeMode = !toTask.isSystemApp && ssp.isInSafeMode();
                 mHeaderBar.onTaskViewSizeChanged((int) toTransform.rect.width(),
                         (int) toTransform.rect.height());
-                thumbnail = Bitmap.createBitmap(toHeaderWidth, toHeaderHeight,
-                        Bitmap.Config.ARGB_8888);
                 if (RecentsDebugFlags.Static.EnableTransitionThumbnailDebugMode) {
                     thumbnail.eraseColor(0xFFff0000);
                 } else {
+                    thumbnail.eraseColor(0);
                     Canvas c = new Canvas(thumbnail);
-                    c.scale(toTransform.scale, toTransform.scale);
                     // Workaround for b/27815919, reset the callback so that we do not trigger an
                     // invalidate on the header bar as a result of updating the icon
                     Drawable icon = mHeaderBar.getIconView().getDrawable();
@@ -854,6 +803,18 @@
         boolean hasRecentTasks = stack.getTaskCount() > 0;
         boolean useThumbnailTransition = (topTask != null) && !isTopTaskHome && hasRecentTasks;
 
+        // Update the launch state that we need in updateHeaderBarLayout()
+        launchState.launchedFromHome = !useThumbnailTransition;
+        launchState.launchedFromApp = useThumbnailTransition || mLaunchedWhileDocking;
+        launchState.launchedViaDockGesture = mLaunchedWhileDocking;
+        launchState.launchedViaDragGesture = mDraggingInRecents;
+        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
+        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+
+        // Preload the icon (this will be a null-op if we have preloaded the icon already in
+        // preloadRecents())
+        preloadIcon(topTask);
+
         // Update the header bar if necessary
         updateHeaderBarLayout(stack);
 
@@ -861,44 +822,27 @@
         TaskStackLayoutAlgorithm.VisibilityReport stackVr =
                 mDummyStackView.computeStackVisibilityReport();
 
-        // Update the launch state
-        launchState.launchedFromHome = false;
-        launchState.launchedFromApp = mLaunchedWhileDocking;
-        launchState.launchedViaDockGesture = mLaunchedWhileDocking;
-        launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
-        launchState.launchedWithAltTab = mTriggeredFromAltTab;
+        // Update the remaining launch state
         launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
         launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
-        launchState.launchedViaDragGesture = mDraggingInRecents;
 
         if (!animate) {
             startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1));
             return;
         }
 
+        ActivityOptions opts;
         if (useThumbnailTransition) {
-            launchState.launchedFromApp = true;
-
             // Try starting with a thumbnail transition
-            ActivityOptions opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
-            if (opts != null) {
-                startRecentsActivity(opts);
-            } else {
-                // Fall through below to the non-thumbnail transition
-                useThumbnailTransition = false;
-            }
-        }
-
-        if (!useThumbnailTransition) {
-            launchState.launchedFromHome = true;
-
+            opts = getThumbnailTransitionActivityOptions(topTask, mDummyStackView);
+        } else {
             // If there is no thumbnail transition, but is launching from home into recents, then
             // use a quick home transition
-            ActivityOptions opts = hasRecentTasks
-                    ? getHomeTransitionActivityOptions()
-                    : getUnknownTransitionActivityOptions();
-            startRecentsActivity(opts);
+            opts = hasRecentTasks
+                ? getHomeTransitionActivityOptions()
+                : getUnknownTransitionActivityOptions();
         }
+        startRecentsActivity(opts);
         mLastToggleTime = SystemClock.elapsedRealtime();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
index 0d56ae9..38ad1c7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
@@ -30,6 +30,7 @@
 
 import com.android.systemui.recents.misc.ReferenceCountedTrigger;
 
+import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
@@ -652,19 +653,43 @@
     /**
      * @return a dump of the current state of the EventBus
      */
-    public String dump() {
+    public void dump(String prefix, PrintWriter writer) {
+        writer.println(dumpInternal(prefix));
+    }
+
+    public String dumpInternal(String prefix) {
+        String innerPrefix = prefix + "  ";
+        String innerInnerPrefix = innerPrefix + "  ";
         StringBuilder output = new StringBuilder();
+        output.append(prefix);
         output.append("Registered class types:");
         output.append("\n");
-        for (Class<?> clz : mSubscriberTypeMap.keySet()) {
-            output.append("\t");
+        ArrayList<Class<?>> subsciberTypes = new ArrayList<>(mSubscriberTypeMap.keySet());
+        Collections.sort(subsciberTypes, new Comparator<Class<?>>() {
+            @Override
+            public int compare(Class<?> o1, Class<?> o2) {
+                return o1.getSimpleName().compareTo(o2.getSimpleName());
+            }
+        });
+        for (int i = 0; i < subsciberTypes.size(); i++) {
+            Class<?> clz = subsciberTypes.get(i);
+            output.append(innerPrefix);
             output.append(clz.getSimpleName());
             output.append("\n");
         }
+        output.append(prefix);
         output.append("Event map:");
         output.append("\n");
-        for (Class<?> clz : mEventTypeMap.keySet()) {
-            output.append("\t");
+        ArrayList<Class<?>> classes = new ArrayList<>(mEventTypeMap.keySet());
+        Collections.sort(classes, new Comparator<Class<?>>() {
+            @Override
+            public int compare(Class<?> o1, Class<?> o2) {
+                return o1.getSimpleName().compareTo(o2.getSimpleName());
+            }
+        });
+        for (int i = 0; i < classes.size(); i++) {
+            Class<?> clz = classes.get(i);
+            output.append(innerPrefix);
             output.append(clz.getSimpleName());
             output.append(" -> ");
             output.append("\n");
@@ -673,7 +698,7 @@
                 Object subscriber = handler.subscriber.getReference();
                 if (subscriber != null) {
                     String id = Integer.toHexString(System.identityHashCode(subscriber));
-                    output.append("\t\t");
+                    output.append(innerInnerPrefix);
                     output.append(subscriber.getClass().getSimpleName());
                     output.append(" [0x" + id + ", #" + handler.priority + "]");
                     output.append("\n");
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
deleted file mode 100644
index cdcabf0..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/events/activity/ForcedResizableEvent.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.recents.events.activity;
-
-import com.android.systemui.recents.events.EventBus;
-
-/**
- * Sent when recents received the information that an activity got forced resizable, and we need
- * to inform the user about that.
- */
-public class ForcedResizableEvent extends EventBus.Event {
-
-    public final String packageName;
-    public final int taskId;
-
-    public ForcedResizableEvent(String packageName, int taskId) {
-        this.packageName = packageName;
-        this.taskId = taskId;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 1a4b40f..2d5addb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -144,6 +144,7 @@
         public void onPinnedActivityRestartAttempt() { }
         public void onPinnedStackAnimationEnded() { }
         public void onActivityForcedResizable(String packageName, int taskId) { }
+        public void onActivityDismissingDockedStack() { }
     }
 
     /**
@@ -182,6 +183,11 @@
             mHandler.obtainMessage(H.ON_ACTIVITY_FORCED_RESIZABLE, taskId, 0, packageName)
                     .sendToTarget();
         }
+
+        @Override
+        public void onActivityDismissingDockedStack() throws RemoteException {
+            mHandler.sendEmptyMessage(H.ON_ACTIVITY_DISMISSING_DOCKED_STACK);
+        }
     };
 
     /**
@@ -1091,6 +1097,7 @@
         private static final int ON_PINNED_ACTIVITY_RESTART_ATTEMPT = 3;
         private static final int ON_PINNED_STACK_ANIMATION_ENDED = 4;
         private static final int ON_ACTIVITY_FORCED_RESIZABLE = 5;
+        private static final int ON_ACTIVITY_DISMISSING_DOCKED_STACK = 6;
 
         @Override
         public void handleMessage(Message msg) {
@@ -1126,6 +1133,12 @@
                     }
                     break;
                 }
+                case ON_ACTIVITY_DISMISSING_DOCKED_STACK: {
+                    for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) {
+                        mTaskStackListeners.get(i).onActivityDismissingDockedStack();
+                    }
+                    break;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
index e28612a..69d98af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/Utilities.java
@@ -243,4 +243,14 @@
     public static float dpToPx(Resources res, float dp) {
         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
     }
+
+    /**
+     * Returns a lightweight dump of a rect.
+     */
+    public static String dumpRect(Rect r) {
+        if (r == null) {
+            return "N:0,0-0,0";
+        }
+        return r.left + "," + r.top + "-" + r.right + "," + r.bottom;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 6668079..24eeaf2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -29,6 +29,7 @@
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.misc.Utilities;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Objects;
 
@@ -37,6 +38,9 @@
  * A task represents the top most task in the system's task stack.
  */
 public class Task {
+
+    public static final String TAG = "Task";
+
     /* Task callbacks */
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
@@ -100,7 +104,8 @@
 
         @Override
         public String toString() {
-            return "t" + id + ", s" + stackId + ", u" + userId;
+            return "id=" + id + " stackId=" + stackId + " user=" + userId + " lastActiveTime=" +
+                    lastActiveTime;
         }
 
         private void updateHashCode() {
@@ -306,4 +311,13 @@
     public String toString() {
         return "[" + key.toString() + "] " + title;
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.print(prefix); writer.print(key);
+        if (affiliationTaskId != key.id) {
+            writer.print(" "); writer.print("affTaskId=" + affiliationTaskId);
+        }
+        writer.print(" "); writer.print(title);
+        writer.println();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index df3f56c..fbb5987 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -55,6 +55,7 @@
 import com.android.systemui.recents.views.DropTarget;
 import com.android.systemui.recents.views.TaskStackLayoutAlgorithm;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -208,6 +209,8 @@
  */
 public class TaskStack {
 
+    private static final String TAG = "TaskStack";
+
     /** Task stack callbacks */
     public interface TaskStackCallbacks {
         /**
@@ -725,7 +728,9 @@
     /** Finds the task with the specified task id. */
     public Task findTaskWithId(int taskId) {
         ArrayList<Task> tasks = computeAllTasksList();
-        for (Task task : tasks) {
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            Task task = tasks.get(i);
             if (task.key.id == taskId) {
                 return task;
             }
@@ -880,7 +885,10 @@
         ArraySet<ComponentName> existingComponents = new ArraySet<>();
         ArraySet<ComponentName> removedComponents = new ArraySet<>();
         ArrayList<Task.TaskKey> taskKeys = getTaskKeys();
-        for (Task.TaskKey t : taskKeys) {
+        int taskKeyCount = taskKeys.size();
+        for (int i = 0; i < taskKeyCount; i++) {
+            Task.TaskKey t = taskKeys.get(i);
+
             // Skip if this doesn't apply to the current user
             if (t.userId != userId) continue;
 
@@ -903,8 +911,10 @@
     @Override
     public String toString() {
         String str = "Stack Tasks (" + mStackTaskList.size() + "):\n";
-        for (Task t : mStackTaskList.getTasks()) {
-            str += "    " + t.toString() + "\n";
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            str += "    " + tasks.get(i).toString() + "\n";
         }
         return str;
     }
@@ -921,4 +931,17 @@
         }
         return map;
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" numStackTasks="); writer.print(mStackTaskList.size());
+        writer.println();
+        ArrayList<Task> tasks = mStackTaskList.getTasks();
+        int taskCount = tasks.size();
+        for (int i = 0; i < taskCount; i++) {
+            tasks.get(i).dump(innerPrefix, writer);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
index 483f9e5..13e1a14 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvActivity.java
@@ -37,14 +37,12 @@
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
 import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.HideRecentsEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
 import com.android.systemui.recents.events.activity.ToggleRecentsEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.ui.AllTaskViewsDismissedEvent;
 import com.android.systemui.recents.events.ui.DeleteTaskDataEvent;
-import com.android.systemui.recents.events.ui.UpdateFreeformTaskViewVisibilityEvent;
 import com.android.systemui.recents.events.ui.UserInteractionEvent;
 import com.android.systemui.recents.events.ui.focus.DismissFocusedTaskViewEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -53,8 +51,9 @@
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-import com.android.systemui.recents.tv.animations.FocusAnimationHolder;
+import com.android.systemui.recents.tv.animations.HomeRecentsEnterExitAnimationHolder;
 import com.android.systemui.recents.tv.views.RecentsTvView;
+import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
 import com.android.systemui.recents.tv.views.TaskStackHorizontalViewAdapter;
 import com.android.systemui.statusbar.BaseStatusBar;
 import com.android.systemui.tv.pip.PipManager;
@@ -77,12 +76,14 @@
     private RecentsPackageMonitor mPackageMonitor;
     private long mLastTabKeyEventTime;
     private boolean mIgnoreAltTabRelease;
+    private boolean mLaunchedFromHome;
 
     private RecentsTvView mRecentsView;
-    private FocusAnimationHolder mRecentsFocusAnimationHolder;
     private View mPipView;
     private TaskStackHorizontalViewAdapter mTaskStackViewAdapter;
+    private TaskStackHorizontalGridView mTaskStackHorizontalGridView;
     private FinishRecentsRunnable mFinishLaunchHomeRunnable;
+    private HomeRecentsEnterExitAnimationHolder mHomeRecentsEnterExitAnimationHolder;
 
     private final PipManager mPipManager = PipManager.getInstance();
     private final PipManager.Listener mPipListener = new PipManager.Listener() {
@@ -133,13 +134,7 @@
             new View.OnFocusChangeListener() {
                 @Override
                 public void onFocusChange(View v, boolean hasFocus) {
-                    if (hasFocus) {
-                        mRecentsFocusAnimationHolder.startFocusLoseAnimation();
-                        mPipRecentsOverlayManager.requestFocus(
-                                mTaskStackViewAdapter.getItemCount() > 0);
-                    } else {
-                        mRecentsFocusAnimationHolder.startFocusGainAnimation();
-                    }
+                    handlePipViewFocusChange(hasFocus);
                 }
             };
 
@@ -182,6 +177,7 @@
         if (!plan.hasTasks()) {
             loader.preloadTasks(plan, -1, launchState.launchedFromHome);
         }
+        mLaunchedFromHome = launchState.launchedFromHome;
         TaskStack stack = plan.getTaskStack();
         RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options();
         loadOpts.runningTaskId = launchState.launchedToTaskId;
@@ -195,7 +191,8 @@
         Collections.reverse(stackTasks);
         if (mTaskStackViewAdapter == null) {
             mTaskStackViewAdapter = new TaskStackHorizontalViewAdapter(stackTasks);
-            mRecentsView.setTaskStackViewAdapter(mTaskStackViewAdapter);
+            mTaskStackHorizontalGridView = mRecentsView
+                    .setTaskStackViewAdapter(mTaskStackViewAdapter);
         } else {
             mTaskStackViewAdapter.setNewStackTasks(stackTasks);
         }
@@ -237,17 +234,24 @@
     }
 
     void dismissRecentsToHome(boolean animateTaskViews) {
-        DismissRecentsToHomeAnimationStarted dismissEvent =
-                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
-        dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
-        dismissEvent.addPostAnimationCallback(new Runnable() {
+        Runnable closeSystemWindows = new Runnable() {
             @Override
             public void run() {
                 Recents.getSystemServices().sendCloseSystemWindows(
                         BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY);
             }
-        });
-        EventBus.getDefault().send(dismissEvent);
+        };
+        DismissRecentsToHomeAnimationStarted dismissEvent =
+                new DismissRecentsToHomeAnimationStarted(animateTaskViews);
+        dismissEvent.addPostAnimationCallback(mFinishLaunchHomeRunnable);
+        dismissEvent.addPostAnimationCallback(closeSystemWindows);
+
+        if(mTaskStackHorizontalGridView.getChildCount() > 0) {
+            mHomeRecentsEnterExitAnimationHolder.startExitAnimation(dismissEvent);
+        } else {
+            closeSystemWindows.run();
+            mFinishLaunchHomeRunnable.run();
+        }
     }
 
     boolean dismissRecentsToHomeIfVisible(boolean animated) {
@@ -288,11 +292,10 @@
         mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
                 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
                 View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
-        mRecentsFocusAnimationHolder = new FocusAnimationHolder(mRecentsView);
 
         mPipView = findViewById(R.id.pip);
         // Place mPipView at the PIP bounds for fine tuned focus handling.
-        Rect pipBounds = mPipManager.getPipBounds();
+        Rect pipBounds = mPipManager.getRecentsFocusedPipBounds();
         LayoutParams lp = (LayoutParams) mPipView.getLayoutParams();
         lp.width = pipBounds.width();
         lp.height = pipBounds.height();
@@ -328,6 +331,19 @@
         // Update the recent tasks
         updateRecentsTasks();
 
+        mHomeRecentsEnterExitAnimationHolder = new HomeRecentsEnterExitAnimationHolder(
+                getApplicationContext(), mTaskStackHorizontalGridView);
+        if(mTaskStackHorizontalGridView != null &&
+                mTaskStackHorizontalGridView.getChildCount() > 0) {
+            if(mLaunchedFromHome) {
+                mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues();
+            } else {
+                mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues();
+            }
+        } else {
+            mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
+        }
+
         // If this is a new instance from a configuration change, then we have to manually trigger
         // the enter animation state, or if recents was relaunched by AM, without going through
         // the normal mechanisms
@@ -349,6 +365,9 @@
     @Override
     public void onEnterAnimationComplete() {
         super.onEnterAnimationComplete();
+        if(mLaunchedFromHome) {
+            mHomeRecentsEnterExitAnimationHolder.startEnterAnimation();
+        }
         EventBus.getDefault().send(new EnterRecentsWindowAnimationCompletedEvent());
     }
 
@@ -453,12 +472,6 @@
         }
     }
 
-    public final void onBusEvent(EnterRecentsWindowLastAnimationFrameEvent event) {
-        EventBus.getDefault().send(new UpdateFreeformTaskViewVisibilityEvent(true));
-        mRecentsView.getViewTreeObserver().addOnPreDrawListener(this);
-        mRecentsView.invalidate();
-    }
-
     public final void onBusEvent(CancelEnterRecentsWindowAnimationEvent event) {
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         int launchToTaskId = launchState.launchedToTaskId;
@@ -498,6 +511,11 @@
     @Override
     public boolean onPreDraw() {
         mRecentsView.getViewTreeObserver().removeOnPreDrawListener(this);
+        if(mLaunchedFromHome) {
+            mHomeRecentsEnterExitAnimationHolder.setEnterFromHomeStartingAnimationValues();
+        } else {
+            mHomeRecentsEnterExitAnimationHolder.setEnterFromAppStartingAnimationValues();
+        }
         // We post to make sure that this information is delivered after this traversals is
         // finished.
         mRecentsView.post(new Runnable() {
@@ -513,11 +531,30 @@
         if (mPipManager.isPipShown()) {
             mPipView.setVisibility(View.VISIBLE);
             mPipView.setOnFocusChangeListener(mPipViewFocusChangeListener);
-            mPipView.requestFocus();
+            if (mPipView.hasFocus()) {
+                // This can happen only if the activity is resumed. Ask for reset.
+                handlePipViewFocusChange(true);
+            } else {
+                mPipView.requestFocus();
+            }
         } else {
             mPipView.setVisibility(View.GONE);
             mPipRecentsOverlayManager.removePipRecentsOverlayView();
-            mRecentsFocusAnimationHolder.reset();
+        }
+    }
+
+    /**
+     * Handles the PIP view's focus change.
+     * This starts the relevant recents row animation
+     * and give focus to the recents overlay if needed.
+     */
+    private void handlePipViewFocusChange(boolean hasFocus) {
+        mRecentsView.startRecentsRowFocusAnimation(!hasFocus);
+        if (hasFocus) {
+            // When PIP view has focus, recents overlay view will takes the focus
+            // as if it's the part of the Recents UI.
+            mPipRecentsOverlayManager.requestFocus(
+                    mTaskStackViewAdapter.getItemCount() > 0);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
index c1b47dc..fd31872 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/RecentsTvImpl.java
@@ -25,15 +25,19 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 
+import com.android.systemui.SystemUIApplication;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsImpl;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.RecentsTaskLoader;
 import com.android.systemui.recents.model.TaskStack;
+import com.android.systemui.recents.model.ThumbnailData;
 import com.android.systemui.recents.tv.views.TaskCardView;
+import com.android.systemui.statusbar.tv.TvStatusBar;
 
 public class RecentsTvImpl extends RecentsImpl{
     public final static String RECENTS_TV_ACTIVITY =
@@ -81,16 +85,7 @@
         }
 
         if (!useThumbnailTransition) {
-            // If there is no thumbnail transition, but is launching from home into recents, then
-            // use a quick home transition and do the animation from home
-            if (hasRecentTasks) {
-                ActivityOptions opts = getHomeTransitionActivityOptions();
-                startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
-            } else {
-                // Otherwise we do the normal fade from an unknown source
-                ActivityOptions opts = getUnknownTransitionActivityOptions();
-                startRecentsActivity(topTask, opts, true /* fromHome */, false /* fromThumbnail */);
-            }
+            startRecentsActivity(topTask, null, true /* fromHome */, false /* fromThumbnail */);
         }
         mLastToggleTime = SystemClock.elapsedRealtime();
     }
@@ -124,14 +119,26 @@
      */
     private ActivityOptions getThumbnailTransitionActivityOptionsForTV(
             ActivityManager.RunningTaskInfo topTask) {
-        Bitmap thumbnail = mThumbnailTransitionBitmapCache;
         Rect rect = TaskCardView.getStartingCardThumbnailRect(mContext);
-        if (thumbnail != null) {
+        SystemServicesProxy ssp = Recents.getSystemServices();
+        ThumbnailData thumbnailData = ssp.getTaskThumbnail(topTask.id);
+        if (thumbnailData.thumbnail != null) {
+            Bitmap thumbnail = Bitmap.createScaledBitmap(thumbnailData.thumbnail, rect.width(),
+                    rect.height(), false);
             return ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    null, (int) rect.left, (int) rect.top,
-                    (int) rect.width(), (int) rect.height(), mHandler, null);
+                    thumbnail, (int) rect.left, (int) rect.top, (int) rect.width(),
+                    (int) rect.height(), mHandler, null);
         }
         // If both the screenshot and thumbnail fails, then just fall back to the default transition
         return getUnknownTransitionActivityOptions();
     }
+
+    @Override
+    public void onVisibilityChanged(Context context, boolean visible) {
+        SystemUIApplication app = (SystemUIApplication) context;
+        TvStatusBar statusBar = app.getComponent(TvStatusBar.class);
+        if (statusBar != null) {
+            statusBar.updateRecentsVisibility(visible);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
index 8996d0b..fbcfa97 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/DismissAnimationsHolder.java
@@ -18,9 +18,8 @@
 
 import android.animation.Animator;
 import android.content.res.Resources;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
-import android.view.View;
 import android.widget.LinearLayout;
+import com.android.systemui.Interpolators;
 import com.android.systemui.recents.tv.views.TaskCardView;
 
 import com.android.systemui.R;
@@ -28,7 +27,6 @@
 public class DismissAnimationsHolder {
     private LinearLayout mDismissArea;
     private LinearLayout mTaskCardView;
-    private FastOutSlowInInterpolator mFastOutSlowIn;
     private int mCardYDelta;
     private long mShortDuration;
     private long mLongDuration;
@@ -36,7 +34,6 @@
     public DismissAnimationsHolder(TaskCardView taskCardView) {
         mTaskCardView = (LinearLayout) taskCardView.findViewById(R.id.recents_tv_card);
         mDismissArea = (LinearLayout) taskCardView.findViewById(R.id.card_dismiss);
-        mFastOutSlowIn = new FastOutSlowInInterpolator();
 
         Resources res = taskCardView.getResources();
         mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
@@ -45,36 +42,42 @@
     }
 
     public void startEnterAnimation() {
-        mDismissArea.animate().setDuration(mShortDuration);
-        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
-        mDismissArea.animate().alpha(1.0f);
+        mDismissArea.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(1.0f);
 
-        mTaskCardView.animate().setDuration(mShortDuration);
-        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
-        mTaskCardView.animate().translationYBy(mCardYDelta);
-        mTaskCardView.animate().alpha(0.5f);
+        mTaskCardView.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationYBy(mCardYDelta)
+                .alpha(0.5f);
     }
 
     public void startExitAnimation() {
-        mDismissArea.animate().setDuration(mShortDuration);
-        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
-        mDismissArea.animate().alpha(0.0f);
+        mDismissArea.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(0.0f);
 
-        mTaskCardView.animate().setDuration(mShortDuration);
-        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
-        mTaskCardView.animate().translationYBy(-mCardYDelta);
-        mTaskCardView.animate().alpha(1.0f);
+        mTaskCardView.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationYBy(-mCardYDelta)
+                .alpha(1.0f);
     }
 
     public void startDismissAnimation(Animator.AnimatorListener listener) {
-        mDismissArea.animate().setDuration(mShortDuration);
-        mDismissArea.animate().setInterpolator(mFastOutSlowIn);
-        mDismissArea.animate().alpha(0.0f);
+        mDismissArea.animate()
+                .setDuration(mShortDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .alpha(0.0f);
 
-        mTaskCardView.animate().setDuration(mLongDuration);
-        mTaskCardView.animate().setInterpolator(mFastOutSlowIn);
-        mTaskCardView.animate().translationYBy(mCardYDelta);
-        mTaskCardView.animate().alpha(0.0f);
-        mTaskCardView.animate().setListener(listener);
+        mTaskCardView.animate()
+                .setDuration(mLongDuration)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .translationYBy(mCardYDelta)
+                .alpha(0.0f)
+                .setListener(listener);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
deleted file mode 100644
index 864540c..0000000
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/FocusAnimationHolder.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.systemui.recents.tv.animations;
-
-import android.content.res.Resources;
-import android.view.View;
-
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.recents.tv.views.TaskCardView;
-
-/**
- * Collections of Recents row's animation depending on the PIP's focus.
- */
-public class FocusAnimationHolder {
-    private final float DIM_ALPHA = 0.5f;
-
-    private View mRecentsRowView;
-    private int mCardYDelta;
-    private long mDuration;
-
-    public FocusAnimationHolder(View recentsRowView) {
-        mRecentsRowView = recentsRowView;
-
-        Resources res = recentsRowView.getResources();
-        mCardYDelta = res.getDimensionPixelOffset(R.dimen.recents_tv_dismiss_shift_down);
-        mDuration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
-    }
-
-    public void startFocusGainAnimation() {
-        mRecentsRowView.animate()
-                .setDuration(mDuration)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .alpha(1f)
-                .translationY(0);
-    }
-
-    public void startFocusLoseAnimation() {
-        mRecentsRowView.animate()
-                .setDuration(mDuration)
-                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                .alpha(DIM_ALPHA)
-                .translationY(mCardYDelta);
-    }
-
-    public void reset() {
-        mRecentsRowView.setTransitionAlpha(1f);
-        mRecentsRowView.setTranslationY(0);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
new file mode 100644
index 0000000..278de87
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/HomeRecentsEnterExitAnimationHolder.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.tv.animations;
+
+import android.content.Context;
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.tv.views.TaskCardView;
+import com.android.systemui.recents.tv.views.TaskStackHorizontalGridView;
+
+
+public class HomeRecentsEnterExitAnimationHolder {
+
+    private Context mContext;
+    private TaskStackHorizontalGridView mGridView;
+    private long mDelay;
+    private int mDuration;
+    private int mTranslationX;
+
+    public HomeRecentsEnterExitAnimationHolder(Context context,
+            TaskStackHorizontalGridView gridView) {
+        mContext = context;
+        mGridView = gridView;
+        mTranslationX = mContext.getResources()
+                .getDimensionPixelSize(R.dimen.recents_tv_home_recents_shift);
+        mDelay = mContext.getResources().getInteger(R.integer.recents_home_delay);
+        mDuration =  mContext.getResources().getInteger(R.integer.recents_home_duration);
+    }
+
+    public void startEnterAnimation() {
+        for(int i = 0; i < mGridView.getChildCount(); i++) {
+            TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
+            view.setTranslationX(-mTranslationX);
+            view.setAlpha(0.0f);
+            view.animate()
+                    .alpha(1.0f)
+                    .translationX(0)
+                    .setDuration(mDuration)
+                    .setStartDelay(mDelay * i)
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+        }
+    }
+
+    public void startExitAnimation(DismissRecentsToHomeAnimationStarted dismissEvent) {
+        for(int i = mGridView.getChildCount() - 1; i >= 0; i--) {
+            TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
+            view.animate()
+                    .alpha(0.0f)
+                    .translationXBy(-mTranslationX)
+                    .setDuration(mDuration)
+                    .setStartDelay(mDelay * (mGridView.getChildCount() - 1 - i))
+                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+            if(i == 0) {
+                view.animate().setListener(dismissEvent.getAnimationTrigger()
+                        .decrementOnAnimationEnd());
+                dismissEvent.getAnimationTrigger().increment();
+            }
+        }
+    }
+
+    public void setEnterFromHomeStartingAnimationValues() {
+        for(int i = 0; i < mGridView.getChildCount(); i++) {
+            TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
+            view.setTranslationX(-mTranslationX);
+            view.setAlpha(0.0f);
+        }
+    }
+
+    public void setEnterFromAppStartingAnimationValues() {
+        for(int i = 0; i < mGridView.getChildCount(); i++) {
+            TaskCardView view = (TaskCardView) mGridView.getChildAt(i);
+            view.setTranslationX(0);
+            view.setAlpha(1.0f);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
new file mode 100644
index 0000000..28abc34
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/animations/RecentsRowFocusAnimationHolder.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.recents.tv.animations;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.view.View;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.R;
+import com.android.systemui.recents.tv.views.TaskCardView;
+
+/**
+ * Recents row's focus animation with PIP controls.
+ */
+public class RecentsRowFocusAnimationHolder {
+    private static final float DIM_ALPHA = 0.5f;
+
+    private View mView;
+    private View mTitleView;
+
+    private AnimatorSet mFocusGainAnimatorSet;
+    private AnimatorSet mFocusLoseAnimatorSet;
+
+    public RecentsRowFocusAnimationHolder(View view, View titleView) {
+        mView = view;
+        mTitleView = titleView;
+
+        Resources res = view.getResources();
+        int duration = res.getInteger(R.integer.recents_tv_pip_focus_anim_duration);
+
+        mFocusGainAnimatorSet = new AnimatorSet();
+        mFocusGainAnimatorSet.playTogether(
+                ObjectAnimator.ofFloat(mView, "alpha", 1f),
+                ObjectAnimator.ofFloat(mTitleView, "alpha", 1f));
+        mFocusGainAnimatorSet.setDuration(duration);
+        mFocusGainAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+
+        mFocusLoseAnimatorSet = new AnimatorSet();
+        mFocusLoseAnimatorSet.playTogether(
+                ObjectAnimator.ofFloat(mView, "alpha", DIM_ALPHA),
+                ObjectAnimator.ofFloat(mTitleView, "alpha", 0f));
+        mFocusLoseAnimatorSet.setDuration(duration);
+        mFocusLoseAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+    }
+
+    /**
+     * Returns the Recents row's focus change animation.
+     */
+    public Animator getFocusChangeAnimator(boolean hasFocus) {
+        return hasFocus ? mFocusGainAnimatorSet : mFocusLoseAnimatorSet;
+    }
+
+    /**
+     * Resets the views to the initial state immediately.
+     */
+    public void reset() {
+        mView.setAlpha(1f);
+        mTitleView.setAlpha(1f);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
index a69f8a2..fb1127e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java
@@ -16,8 +16,10 @@
 package com.android.systemui.recents.tv.views;
 
 import android.annotation.Nullable;
+import android.app.Activity;
 import android.app.ActivityOptions;
 import android.content.Context;
+import android.graphics.Bitmap;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Handler;
@@ -120,8 +122,10 @@
         }
         try {
             Rect taskRect = taskView.getFocusedThumbnailRect();
+            Bitmap thumbnail = Bitmap.createScaledBitmap(task.thumbnail, taskRect.width(),
+                    taskRect.height(), false);
             WindowManagerGlobal.getWindowManagerService()
-                    .overridePendingAppTransitionAspectScaledThumb(task.thumbnail, taskRect.left,
+                    .overridePendingAppTransitionAspectScaledThumb(thumbnail, taskRect.left,
                             taskRect.top, taskRect.width(), taskRect.height(), callback, true);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to override transition: " + e);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
index d966614..53fdf62 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java
@@ -18,7 +18,10 @@
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.RecyclerView;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowInsets;
@@ -32,14 +35,15 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
 import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
+import com.android.systemui.recents.events.activity.ExitRecentsWindowFirstAnimationFrameEvent;
 import com.android.systemui.recents.events.activity.LaunchTvTaskEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
-
-import java.util.ArrayList;
-import java.util.List;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
+import android.support.v7.widget.RecyclerView.OnScrollListener;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
 /**
  * Top level layout of recents for TV. This will show the TaskStacks using a HorizontalGridView.
@@ -52,11 +56,12 @@
     private TaskStack mStack;
     private TaskStackHorizontalGridView mTaskStackHorizontalView;
     private View mEmptyView;
+    private RecentsRowFocusAnimationHolder mEmptyViewFocusAnimationHolder;
     private boolean mAwaitingFirstLayout = true;
     private Rect mSystemInsets = new Rect();
     private RecentsTvTransitionHelper mTransitionHelper;
     private Handler mHandler;
-
+    private OnScrollListener mScrollListener;
     public RecentsTvView(Context context) {
         this(context, null);
     }
@@ -77,6 +82,8 @@
         LayoutInflater inflater = LayoutInflater.from(context);
         mEmptyView = inflater.inflate(R.layout.recents_empty, this, false);
         addView(mEmptyView);
+        mEmptyViewFocusAnimationHolder = new RecentsRowFocusAnimationHolder(mEmptyView, null);
+
         mHandler = new Handler();
         mTransitionHelper = new RecentsTvTransitionHelper(mContext, mHandler);
     }
@@ -94,7 +101,6 @@
             mTaskStackHorizontalView.setStack(stack);
         }
 
-
         if (stack.getStackTaskCount() > 0) {
             hideEmptyView();
         } else {
@@ -108,8 +114,7 @@
         if (mTaskStackHorizontalView != null) {
             Task task = mTaskStackHorizontalView.getFocusedTask();
             if (task != null) {
-                SystemServicesProxy ssp = Recents.getSystemServices();
-                ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+                launchTaskFomRecents(task);
                 return true;
             }
         }
@@ -122,30 +127,64 @@
             TaskStack stack = mTaskStackHorizontalView.getStack();
             Task task = stack.getLaunchTarget();
             if (task != null) {
-                SystemServicesProxy ssp = Recents.getSystemServices();
-                ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+                launchTaskFomRecents(task);
                 return true;
             }
         }
         return false;
     }
 
-    /** Launches a given task. */
-    public boolean launchTask(Task task, Rect taskBounds, int destinationStack) {
-        if (mTaskStackHorizontalView != null) {
-            // Iterate the stack views and try and find the given task.
-            List<TaskCardView> taskViews = mTaskStackHorizontalView.getTaskViews();
-            int taskViewCount = taskViews.size();
-            for (int j = 0; j < taskViewCount; j++) {
-                TaskCardView tv = taskViews.get(j);
-                if (tv.getTask() == task) {
-                    SystemServicesProxy ssp = Recents.getSystemServices();
-                    ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
-                    return true;
-                }
+    /**
+     * Launch the given task from recents with animation. If the task is not focused, this will
+     * attempt to scroll to focus the task before launching.
+     * @param task
+     */
+    private void launchTaskFomRecents(final Task task) {
+        if(task != mTaskStackHorizontalView.getFocusedTask()) {
+            if(mScrollListener != null) {
+                mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
             }
+            mScrollListener = new OnScrollListener() {
+                @Override
+                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+                    super.onScrollStateChanged(recyclerView, newState);
+                    if(newState == RecyclerView.SCROLL_STATE_IDLE) {
+                        TaskCardView cardView = mTaskStackHorizontalView.getChildViewForTask(task);
+                        if(cardView != null) {
+                            mTransitionHelper.launchTaskFromRecents(mStack, task,
+                                    mTaskStackHorizontalView, cardView, null, INVALID_STACK_ID);
+                        } else {
+                            // This should not happen normally. If this happens then the data in
+                            // the grid view was altered during the scroll. Log error and launch
+                            // task with no animation.
+                            Log.e(TAG, "Card view for task : " + task + ", returned null.");
+                            SystemServicesProxy ssp = Recents.getSystemServices();
+                            ssp.startActivityFromRecents(getContext(), task.key, task.title, null);
+                        }
+                        mTaskStackHorizontalView.removeOnScrollListener(mScrollListener);
+                    }
+                }
+            };
+            mTaskStackHorizontalView.addOnScrollListener(mScrollListener);
+            mTaskStackHorizontalView.setSelectedPositionSmooth(
+                    ((TaskStackHorizontalViewAdapter) mTaskStackHorizontalView.getAdapter())
+                            .getPositionOfTask(task));
+        } else {
+            mTransitionHelper.launchTaskFromRecents(mStack, task, mTaskStackHorizontalView,
+                    mTaskStackHorizontalView.getChildViewForTask(task), null,
+                    INVALID_STACK_ID);
         }
-        return false;
+    }
+
+    /**
+     * Starts the focus change animation.
+     */
+    public void startRecentsRowFocusAnimation(boolean hasFocus) {
+        if (mEmptyView.getVisibility() == View.VISIBLE) {
+            mEmptyViewFocusAnimationHolder.getFocusChangeAnimator(hasFocus).start();
+        } else {
+            mTaskStackHorizontalView.startRecentsRowFocusAnimation(hasFocus);
+        }
     }
 
     /**
@@ -153,14 +192,15 @@
      */
     public void showEmptyView() {
         mEmptyView.setVisibility(View.VISIBLE);
-        mEmptyView.bringToFront();
+        mTaskStackHorizontalView.setVisibility(View.GONE);
     }
 
     /**
      * Shows the task stack and hides the empty view.
      */
     public void hideEmptyView() {
-        mEmptyView.setVisibility(View.INVISIBLE);
+        mEmptyView.setVisibility(View.GONE);
+        mTaskStackHorizontalView.setVisibility(View.VISIBLE);
     }
 
     /**
@@ -208,9 +248,15 @@
         }
     }
 
-    public void setTaskStackViewAdapter(TaskStackHorizontalViewAdapter taskStackViewAdapter) {
+    public TaskStackHorizontalGridView setTaskStackViewAdapter(
+            TaskStackHorizontalViewAdapter taskStackViewAdapter) {
         if(mTaskStackHorizontalView != null) {
             mTaskStackHorizontalView.setAdapter(taskStackViewAdapter);
         }
+        return mTaskStackHorizontalView;
+    }
+
+    public TaskStackHorizontalGridView getGridView() {
+        return mTaskStackHorizontalView;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
index 3343aec..99d478b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskCardView.java
@@ -24,6 +24,7 @@
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.KeyEvent;
+import android.view.View;
 import android.view.WindowManager;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
@@ -31,6 +32,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.recents.tv.animations.DismissAnimationsHolder;
+import com.android.systemui.recents.tv.animations.RecentsRowFocusAnimationHolder;
 import com.android.systemui.recents.tv.animations.ViewFocusAnimator;
 import com.android.systemui.recents.model.Task;
 
@@ -44,6 +46,7 @@
 
     private ViewFocusAnimator mViewFocusAnimator;
     private DismissAnimationsHolder mDismissAnimationsHolder;
+    private RecentsRowFocusAnimationHolder mRecentsRowFocusAnimationHolder;
 
     public TaskCardView(Context context) {
         this(context, null);
@@ -61,10 +64,13 @@
 
     @Override
     protected void onFinishInflate() {
+        super.onFinishInflate();
         mThumbnailView = (ImageView) findViewById(R.id.card_view_thumbnail);
         mTitleTextView = (TextView) findViewById(R.id.card_title_text);
         mBadgeView = (ImageView) findViewById(R.id.card_extra_badge);
         mDismissAnimationsHolder = new DismissAnimationsHolder(this);
+        View title = findViewById(R.id.card_info_field);
+        mRecentsRowFocusAnimationHolder = new RecentsRowFocusAnimationHolder(this, title);
     }
 
     public void init(Task task) {
@@ -86,13 +92,6 @@
     public Rect getFocusedThumbnailRect() {
         Rect r = new Rect();
         mThumbnailView.getGlobalVisibleRect(r);
-        TypedValue out = new TypedValue();
-        getContext().getResources().getValue(R.integer.selected_scale, out, true);
-        float deltaScale = (out.getFloat() - 1.0f) / 2;
-        r.set((int) (r.left - r.left * deltaScale),
-                (int) (r.top - r.top * deltaScale),
-                (int) (r.right + r.right * deltaScale),
-                (int) (r.bottom + r.bottom * deltaScale));
         return r;
     }
 
@@ -193,6 +192,10 @@
         mDismissAnimationsHolder.startDismissAnimation(listener);
     }
 
+    public RecentsRowFocusAnimationHolder getRecentsRowFocusAnimationHolder() {
+        return mRecentsRowFocusAnimationHolder;
+    }
+
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
index 5eb9fda..603721a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalGridView.java
@@ -15,7 +15,11 @@
  */
 package com.android.systemui.recents.tv.views;
 
+import android.animation.Animator;
+import android.animation.AnimatorSet;
 import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
 import android.support.v17.leanback.widget.HorizontalGridView;
 import android.util.AttributeSet;
 import android.view.View;
@@ -36,10 +40,19 @@
  * Horizontal Grid View Implementation to show the Task Stack for TV.
  */
 public class TaskStackHorizontalGridView extends HorizontalGridView implements TaskStackCallbacks {
-
+    private static final int ANIMATION_DELAY_MS = 50;
+    private static final int MSG_START_RECENT_ROW_FOCUS_ANIMATION = 100;
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_START_RECENT_ROW_FOCUS_ANIMATION) {
+                startRecentsRowFocusAnimation(msg.arg1 == 1);
+            }
+        }
+    };
     private TaskStack mStack;
-    private ArrayList<TaskCardView> mTaskViews = new ArrayList<>();
     private Task mFocusedTask;
+    private AnimatorSet mRecentsRowFocusAnimation;
 
     public TaskStackHorizontalGridView(Context context) {
         this(context, null);
@@ -62,10 +75,18 @@
         super.onDetachedFromWindow();
         EventBus.getDefault().unregister(this);
     }
+
     /**
      * Resets this view for reuse.
      */
     public void reset() {
+        for (int i = 0; i < getChildCount(); i++) {
+            ((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder().reset();
+        }
+        if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+            mRecentsRowFocusAnimation.cancel();
+        }
+        mHandler.removeCallbacksAndMessages(null);
         requestLayout();
     }
 
@@ -119,10 +140,8 @@
      * @return Child view for given task
      */
     public TaskCardView getChildViewForTask(Task task) {
-        List<TaskCardView> taskViews = getTaskViews();
-        int taskViewCount = taskViews.size();
-        for (int i = 0; i < taskViewCount; i++) {
-            TaskCardView tv = taskViews.get(i);
+        for (int i = 0; i < getChildCount(); i++) {
+            TaskCardView tv = (TaskCardView) getChildAt(i);
             if (tv.getTask() == task) {
                 return tv;
             }
@@ -130,12 +149,36 @@
         return null;
     }
 
-    public List<TaskCardView> getTaskViews() {
-        return mTaskViews;
+    /**
+     * Starts the focus change animation.
+     */
+    public void startRecentsRowFocusAnimation(final boolean hasFocus) {
+        if (getChildCount() == 0) {
+            // Animation request may happen before view is attached.
+            // Post again with small dealy so animation can be run again later.
+            if (getAdapter().getItemCount() > 0) {
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                        MSG_START_RECENT_ROW_FOCUS_ANIMATION, hasFocus ? 1 : 0),
+                        ANIMATION_DELAY_MS);
+            }
+            return;
+        }
+        if (mRecentsRowFocusAnimation != null && mRecentsRowFocusAnimation.isStarted()) {
+            mRecentsRowFocusAnimation.cancel();
+        }
+        Animator animator = ((TaskCardView) getChildAt(0)).getRecentsRowFocusAnimationHolder()
+                .getFocusChangeAnimator(hasFocus);
+        mRecentsRowFocusAnimation = new AnimatorSet();
+        AnimatorSet.Builder builder = mRecentsRowFocusAnimation.play(animator);
+        for (int i = 1; i < getChildCount(); i++) {
+            builder.with(((TaskCardView) getChildAt(i)).getRecentsRowFocusAnimationHolder()
+                    .getFocusChangeAnimator(hasFocus));
+        }
+        mRecentsRowFocusAnimation.start();
     }
 
     @Override
-    public void onStackTaskAdded(TaskStack stack, Task newTask){
+    public void onStackTaskAdded(TaskStack stack, Task newTask) {
         getAdapter().notifyItemInserted(stack.getStackTasks().indexOf(newTask));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
index 3788719..97712ea 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/TaskStackHorizontalViewAdapter.java
@@ -131,4 +131,9 @@
         mTaskList.remove(position);
         notifyItemRemoved(position);
     }
+
+    public int getPositionOfTask(Task task) {
+        int position = mTaskList.indexOf(task);
+        return (position >= 0) ? position : 0;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 9eec2ce..fd8df99 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -116,6 +116,7 @@
                     // window transition
                     EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
                     EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
 
                     if (screenPinningRequested) {
                         // Request screen pinning after the animation runs
@@ -133,6 +134,7 @@
                     // window transition
                     EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
                     EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
                 }
             };
         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index ef81f9e..21a43d5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -74,6 +74,8 @@
 import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.FlingAnimationUtils;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -83,6 +85,8 @@
  */
 public class RecentsView extends FrameLayout {
 
+    private static final String TAG = "RecentsView";
+
     private static final int DOCK_AREA_OVERLAY_TRANSITION_DURATION = 135;
     private static final int DEFAULT_UPDATE_SCRIM_DURATION = 200;
     private static final float DEFAULT_SCRIM_ALPHA = 0.33f;
@@ -758,4 +762,22 @@
                 top + mStackActionButton.getMeasuredHeight());
         return actionButtonRect;
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
+        writer.print(" insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (mStack != null) {
+            mStack.dump(innerPrefix, writer);
+        }
+        if (mTaskStackView != null) {
+            mTaskStackView.dump(innerPrefix, writer);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 1c433d8..fe91f42 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -456,7 +456,8 @@
         TaskStack stack = mStackView.getStack();
 
         final float curScroll = stackScroller.getStackScroll();
-        final float newScroll = stackLayout.getStackScrollForTask(newFocusedTask);
+        final float newScroll = stackScroller.getBoundedStackScroll(
+                stackLayout.getStackScrollForTask(newFocusedTask));
         boolean willScrollToFront = newScroll > curScroll;
         boolean willScroll = Float.compare(newScroll, curScroll) != 0;
 
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 9eab0f6..b75a91e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -29,6 +29,7 @@
 
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.recents.RecentsActivity;
 import com.android.systemui.recents.RecentsActivityLaunchState;
 import com.android.systemui.recents.RecentsConfiguration;
 import com.android.systemui.recents.RecentsDebugFlags;
@@ -38,6 +39,7 @@
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -108,6 +110,8 @@
  */
 public class TaskStackLayoutAlgorithm {
 
+    private static final String TAG = "TaskStackLayoutAlgorithm";
+
     // The distribution of view bounds alpha
     // XXX: This is a hack because you can currently set the max alpha to be > 1f
     public static final float OUTLINE_ALPHA_MIN_VALUE = 0f;
@@ -530,8 +534,12 @@
                 ? stack.indexOfStackTask(launchTask)
                 : mNumStackTasks - 1;
         if (getInitialFocusState() == STATE_FOCUSED) {
+            int maxBottomOffset = mStackBottomOffset + mTaskRect.height();
+            float maxBottomNormX = getNormalizedXFromFocusedY(maxBottomOffset, FROM_BOTTOM);
+            mFocusedRange.offset(0f);
             mMinScrollP = 0;
-            mMaxScrollP = Math.max(mMinScrollP, mNumStackTasks - 1);
+            mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
+                    Math.max(0, mFocusedRange.getAbsoluteX(maxBottomNormX)));
             if (launchState.launchedFromHome) {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
             } else {
@@ -555,7 +563,10 @@
                     Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
             boolean scrollToFront = launchState.launchedFromHome ||
                     launchState.launchedViaDockGesture;
-            if (scrollToFront) {
+            if (launchState.launchedWithAltTab) {
+                mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
+                mInitialNormX = null;
+            } else if (scrollToFront) {
                 mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
                 mInitialNormX = null;
             } else {
@@ -652,8 +663,9 @@
      * Returns the default focus state.
      */
     public int getInitialFocusState() {
+        RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
         RecentsDebugFlags debugFlags = Recents.getDebugFlags();
-        if (debugFlags.isPagingEnabled()) {
+        if (debugFlags.isPagingEnabled() || launchState.launchedWithAltTab) {
             return STATE_FOCUSED;
         } else {
             return STATE_UNFOCUSED;
@@ -675,7 +687,6 @@
     }
 
     /**
-     *
      * Returns the current stack state.
      */
     public StackState getStackState() {
@@ -1027,6 +1038,18 @@
     }
 
     /**
+     * Returns the normalized x on the focused curve given an absolute Y position (relative to the
+     * stack height).
+     */
+    private float getNormalizedXFromFocusedY(float y, @AnchorSide int fromSide) {
+        float offset = (fromSide == FROM_TOP)
+                ? mStackRect.height() - y
+                : y;
+        float offsetPct = offset / mStackRect.height();
+        return mFocusedCurveInterpolator.getX(offsetPct);
+    }
+
+    /**
      * Creates a new path for the focused curve.
      */
     private Path constructFocusedCurve() {
@@ -1036,10 +1059,13 @@
         float topPeekHeightPct = (float) mFocusedTopPeekHeight / mStackRect.height();
         float bottomPeekHeightPct = (float) (mStackBottomOffset + mFocusedBottomPeekHeight) /
                 mStackRect.height();
+        float minBottomPeekHeightPct = (float) (mFocusedTopPeekHeight + mTaskRect.height() -
+                mMinMargin) / mStackRect.height();
         Path p = new Path();
         p.moveTo(0f, 1f);
         p.lineTo(0.5f, 1f - topPeekHeightPct);
-        p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), bottomPeekHeightPct);
+        p.lineTo(1f - (0.5f / mFocusedRange.relativeMax), Math.max(1f - minBottomPeekHeightPct,
+                bottomPeekHeightPct));
         p.lineTo(1f, 0f);
         return p;
     }
@@ -1133,4 +1159,44 @@
         mBackOfStackTransform.visible = true;
         mFrontOfStackTransform.visible = true;
     }
-}
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+
+        writer.print(prefix); writer.print(TAG);
+        writer.write(" numStackTasks="); writer.write(mNumStackTasks);
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("insets="); writer.print(Utilities.dumpRect(mSystemInsets));
+        writer.print(" stack="); writer.print(Utilities.dumpRect(mStackRect));
+        writer.print(" task="); writer.print(Utilities.dumpRect(mTaskRect));
+        writer.print(" freeform="); writer.print(Utilities.dumpRect(mFreeformRect));
+        writer.print(" actionButton="); writer.print(Utilities.dumpRect(mStackActionButtonRect));
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("minScroll="); writer.print(mMinScrollP);
+        writer.print(" maxScroll="); writer.print(mMaxScrollP);
+        writer.print(" initialScroll="); writer.print(mInitialScrollP);
+        writer.println();
+
+        writer.print(innerPrefix);
+        writer.print("focusState="); writer.print(mFocusState);
+        writer.println();
+
+        if (mTaskIndexOverrideMap.size() > 0) {
+            for (int i = mTaskIndexOverrideMap.size() - 1; i >= 0; i--) {
+                int taskId = mTaskIndexOverrideMap.keyAt(i);
+                float x = mTaskIndexMap.get(taskId);
+                float overrideX = mTaskIndexOverrideMap.get(taskId, 0f);
+
+                writer.print(innerPrefix);
+                writer.print("taskId= "); writer.print(taskId);
+                writer.print(" x= "); writer.print(x);
+                writer.print(" overrideX= "); writer.print(overrideX);
+                writer.println();
+            }
+        }
+    }
+}
\ No newline at end of file
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 5416a48..13c8403 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -90,6 +90,7 @@
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
+import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -102,6 +103,8 @@
         TaskStackLayoutAlgorithm.TaskStackLayoutAlgorithmCallbacks,
         ViewPool.ViewPoolConsumer<TaskView, Task> {
 
+    private static final String TAG = "TaskStackView";
+
     private final static String KEY_SAVED_STATE_SUPER = "saved_instance_state_super";
     private final static String KEY_SAVED_STATE_LAYOUT_FOCUSED_STATE =
             "saved_instance_state_layout_focused_state";
@@ -2067,4 +2070,37 @@
         mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
                 Settings.System.LOCK_TO_APP_ENABLED) != 0;
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        String innerPrefix = prefix + "  ";
+        String id = Integer.toHexString(System.identityHashCode(this));
+
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" hasDefRelayout=");
+        writer.print(mDeferredTaskViewLayoutAnimation != null ? "Y" : "N");
+        writer.print(" clipDirty="); writer.print(mTaskViewsClipDirty ? "Y" : "N");
+        writer.print(" awaitingFirstLayout="); writer.print(mAwaitingFirstLayout ? "Y" : "N");
+        writer.print(" initialState="); writer.print(mInitialState);
+        writer.print(" inMeasureLayout="); writer.print(mInMeasureLayout ? "Y" : "N");
+        writer.print(" enterAnimCompleted="); writer.print(mEnterAnimationComplete ? "Y" : "N");
+        writer.print(" touchExplorationOn="); writer.print(mTouchExplorationEnabled ? "Y" : "N");
+        writer.print(" screenPinningOn="); writer.print(mScreenPinningEnabled ? "Y" : "N");
+        writer.print(" numIgnoreTasks="); writer.print(mIgnoreTasks.size());
+        writer.print(" numViewPool="); writer.print(mViewPool.getViews().size());
+        writer.print(" stableStackBounds="); writer.print(Utilities.dumpRect(mStableStackBounds));
+        writer.print(" stackBounds="); writer.print(Utilities.dumpRect(mStackBounds));
+        writer.print(" stableWindow="); writer.print(Utilities.dumpRect(mStableWindowRect));
+        writer.print(" window="); writer.print(Utilities.dumpRect(mWindowRect));
+        writer.print(" [0x"); writer.print(id); writer.print("]");
+        writer.println();
+
+        if (mFocusedTask != null) {
+            writer.print(innerPrefix);
+            writer.print("Focused task: ");
+            mFocusedTask.dump(innerPrefix, writer);
+        }
+
+        mLayoutAlgorithm.dump(innerPrefix, writer);
+        mStackScroller.dump(innerPrefix, writer);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
index 583fb88..19b3c94 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java
@@ -30,6 +30,8 @@
 import com.android.systemui.R;
 import com.android.systemui.recents.misc.Utilities;
 
+import java.io.PrintWriter;
+
 /* The scrolling logic for a TaskStackView */
 public class TaskStackViewScroller {
 
@@ -246,4 +248,10 @@
             mScroller.abortAnimation();
         }
     }
+
+    public void dump(String prefix, PrintWriter writer) {
+        writer.print(prefix); writer.print(TAG);
+        writer.print(" stackScroll:"); writer.print(mStackScrollP);
+        writer.println();
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index f8ed700..6e585ae 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -146,6 +146,8 @@
     AnimateableViewBounds mViewBounds;
 
     private AnimatorSet mTransformAnimation;
+    private ObjectAnimator mDimAnimator;
+    private ObjectAnimator mOutlineAnimator;
     private final TaskViewTransform mTargetAnimationTransform = new TaskViewTransform();
     private ArrayList<Animator> mTmpAnimators = new ArrayList<>();
 
@@ -308,14 +310,14 @@
         } else {
             // Both the progress and the update are a function of the bounds movement of the task
             if (Float.compare(getDimAlpha(), toTransform.dimAlpha) != 0) {
-                ObjectAnimator anim = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
+                mDimAnimator = ObjectAnimator.ofFloat(this, DIM_ALPHA, getDimAlpha(),
                         toTransform.dimAlpha);
-                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mDimAnimator));
             }
             if (Float.compare(mViewBounds.getAlpha(), toTransform.viewOutlineAlpha) != 0) {
-                ObjectAnimator anim = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
+                mOutlineAnimator = ObjectAnimator.ofFloat(this, VIEW_OUTLINE_ALPHA,
                         mViewBounds.getAlpha(), toTransform.viewOutlineAlpha);
-                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, anim));
+                mTmpAnimators.add(toAnimation.apply(AnimationProps.BOUNDS, mOutlineAnimator));
             }
             if (updateCallback != null) {
                 ValueAnimator updateCallbackAnim = ValueAnimator.ofInt(0, 1);
@@ -358,6 +360,8 @@
      */
     public void cancelTransformAnimation() {
         Utilities.cancelAnimationWithoutCallbacks(mTransformAnimation);
+        Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
+        Utilities.cancelAnimationWithoutCallbacks(mOutlineAnimator);
     }
 
     /** Enables/disables handling touch on this task view. */
@@ -537,13 +541,15 @@
     @Override
     public void onStartLaunchTargetEnterAnimation(TaskViewTransform transform, int duration,
             boolean screenPinningEnabled, ReferenceCountedTrigger postAnimationTrigger) {
+        Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
+
         // Dim the view after the app window transitions down into recents
         postAnimationTrigger.increment();
         AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
-        Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
                 DIM_ALPHA_WITHOUT_HEADER, getDimAlpha(), transform.dimAlpha));
-        anim.addListener(postAnimationTrigger.decrementOnAnimationEnd());
-        anim.start();
+        mDimAnimator.addListener(postAnimationTrigger.decrementOnAnimationEnd());
+        mDimAnimator.start();
 
         if (screenPinningEnabled) {
             showActionButton(true /* fadeIn */, duration /* fadeInDuration */);
@@ -553,11 +559,13 @@
     @Override
     public void onStartLaunchTargetLaunchAnimation(int duration, boolean screenPinningRequested,
             ReferenceCountedTrigger postAnimationTrigger) {
+        Utilities.cancelAnimationWithoutCallbacks(mDimAnimator);
+
         // Un-dim the view before/while launching the target
         AnimationProps animation = new AnimationProps(duration, Interpolators.ALPHA_OUT);
-        Animator anim = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
+        mDimAnimator = animation.apply(AnimationProps.DIM_ALPHA, ObjectAnimator.ofFloat(this,
                 DIM_ALPHA, getDimAlpha(), 0));
-        anim.start();
+        mDimAnimator.start();
 
         postAnimationTrigger.increment();
         hideActionButton(true /* fadeOut */, duration,
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index ddd3ea1..d294c80 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -34,8 +34,6 @@
  * Controls the docked stack divider.
  */
 public class Divider extends SystemUI {
-    private static final String TAG = "Divider";
-    private int mDividerWindowWidth;
     private DividerWindowManager mWindowManager;
     private DividerView mView;
     private DockDividerVisibilityListener mDockDividerVisibilityListener;
@@ -46,8 +44,6 @@
     @Override
     public void start() {
         mWindowManager = new DividerWindowManager(mContext);
-        mDividerWindowWidth = mContext.getResources().getDimensionPixelSize(
-                com.android.internal.R.dimen.docked_stack_divider_thickness);
         update(mContext.getResources().getConfiguration());
         putComponent(Divider.class, this);
         mDockDividerVisibilityListener = new DockDividerVisibilityListener();
@@ -70,9 +66,11 @@
         mView = (DividerView)
                 LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
+        final int size = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
         final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
-        final int width = landscape ? mDividerWindowWidth : MATCH_PARENT;
-        final int height = landscape ? MATCH_PARENT : mDividerWindowWidth;
+        final int width = landscape ? size : MATCH_PARENT;
+        final int height = landscape ? MATCH_PARENT : size;
         mWindowManager.add(mView, width, height);
         mView.setWindowManager(mWindowManager);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
index f728dab..d4922c3 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivity.java
@@ -23,6 +23,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnTouchListener;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 
@@ -45,6 +46,8 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.forced_resizable_activity);
+        TextView tv = (TextView) findViewById(com.android.internal.R.id.message);
+        tv.setText(R.string.dock_forced_resizable);
         getWindow().getDecorView().setOnTouchListener(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index 9b56037..9294ecd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -20,12 +20,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
-import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.widget.Toast;
 
+import com.android.systemui.R;
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.AppTransitionFinishedEvent;
-import com.android.systemui.recents.events.activity.ForcedResizableEvent;
+import com.android.systemui.recents.misc.SystemServicesProxy;
+import com.android.systemui.recents.misc.SystemServicesProxy.TaskStackListener;
 import com.android.systemui.stackdivider.events.StartedDragingEvent;
 import com.android.systemui.stackdivider.events.StoppedDragingEvent;
 
@@ -53,6 +55,18 @@
     public ForcedResizableInfoActivityController(Context context) {
         mContext = context;
         EventBus.getDefault().register(this);
+        SystemServicesProxy.getInstance(context).registerTaskStackListener(
+                new TaskStackListener() {
+                    @Override
+                    public void onActivityForcedResizable(String packageName, int taskId) {
+                        activityForcedResizable(packageName, taskId);
+                    }
+
+                    @Override
+                    public void onActivityDismissingDockedStack() {
+                        activityDismissingDockedStack();
+                    }
+                });
     }
 
     public void notifyDockedStackExistsChanged(boolean exists) {
@@ -61,14 +75,6 @@
         }
     }
 
-    public final void onBusEvent(ForcedResizableEvent forcedResizableEvent) {
-        if (debounce(forcedResizableEvent.packageName)) {
-            return;
-        }
-        mPendingTaskIds.add(forcedResizableEvent.taskId);
-        postTimeout();
-    }
-
     public final void onBusEvent(AppTransitionFinishedEvent event) {
         if (!mDividerDraging) {
             showPending();
@@ -85,6 +91,20 @@
         showPending();
     }
 
+    private void activityForcedResizable(String packageName, int taskId) {
+        if (debounce(packageName)) {
+            return;
+        }
+        mPendingTaskIds.add(taskId);
+        postTimeout();
+    }
+
+    private void activityDismissingDockedStack() {
+        Toast toast = Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+                Toast.LENGTH_SHORT);
+        toast.show();
+    }
+
     private void showPending() {
         mHandler.removeCallbacks(mTimeoutRunnable);
         for (int i = mPendingTaskIds.size() - 1; i >= 0; i--) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 8102fae..1b2393a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -91,6 +91,7 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -100,6 +101,7 @@
 import com.android.systemui.assist.AssistManager;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationGuts.OnGutsClosedListener;
 import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -114,12 +116,12 @@
 import java.util.Locale;
 
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_HIGH;
-import static com.android.keyguard.KeyguardHostView.OnDismissAction;
 
 public abstract class BaseStatusBar extends SystemUI implements
         CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener,
         ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment,
-        ExpandableNotificationRow.OnExpandClickListener {
+        ExpandableNotificationRow.OnExpandClickListener,
+        OnGutsClosedListener {
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
@@ -285,9 +287,10 @@
     private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
-            // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
-            // so we just dump our cache ...
+            // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
+            // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
             mUsersAllowingPrivateNotifications.clear();
+            mUsersAllowingNotifications.clear();
             // ... and refresh all the notifications
             updateNotifications();
         }
@@ -1010,6 +1013,7 @@
         PackageManager pmUser = getPackageManagerForUser(mContext, sbn.getUser().getIdentifier());
         row.setTag(sbn.getPackageName());
         final NotificationGuts guts = row.getGuts();
+        guts.setClosedListener(this);
         final String pkg = sbn.getPackageName();
         String appname = pkg;
         Drawable pkgicon = null;
@@ -1037,6 +1041,7 @@
             settingsButton.setOnClickListener(new View.OnClickListener() {
                 public void onClick(View v) {
                     MetricsLogger.action(mContext, MetricsEvent.ACTION_NOTE_INFO);
+                    guts.resetFalsingCheck();
                     startAppNotificationSettingsActivity(pkg, appUidF);
                 }
             });
@@ -1047,24 +1052,43 @@
         row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                guts.saveImportance(sbn);
-
-                int[] rowLocation = new int[2];
-                int[] doneLocation = new int[2];
-                row.getLocationOnScreen(rowLocation);
-                v.getLocationOnScreen(doneLocation);
-
-                final int centerX = v.getWidth() / 2;
-                final int centerY = v.getHeight() / 2;
-                final int x = doneLocation[0] - rowLocation[0] + centerX;
-                final int y = doneLocation[1] - rowLocation[1] + centerY;
-                dismissPopups(x, y);
+                // If the user has security enabled, show challenge if the setting is changed.
+                if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
+                        (mState == StatusBarState.KEYGUARD
+                        || mState == StatusBarState.SHADE_LOCKED)) {
+                    OnDismissAction dismissAction = new OnDismissAction() {
+                        @Override
+                        public boolean onDismiss() {
+                            saveImportanceCloseControls(sbn, row, guts, v);
+                            return true;
+                        }
+                    };
+                    onLockedNotificationImportanceChange(dismissAction);
+                } else {
+                    saveImportanceCloseControls(sbn, row, guts, v);
+                }
             }
         });
-
         guts.bindImportance(pmUser, sbn, row, mNotificationData.getImportance(sbn.getKey()));
     }
 
+    private void saveImportanceCloseControls(StatusBarNotification sbn,
+            ExpandableNotificationRow row, NotificationGuts guts, View done) {
+        guts.resetFalsingCheck();
+        guts.saveImportance(sbn);
+
+        int[] rowLocation = new int[2];
+        int[] doneLocation = new int[2];
+        row.getLocationOnScreen(rowLocation);
+        done.getLocationOnScreen(doneLocation);
+
+        final int centerX = done.getWidth() / 2;
+        final int centerY = done.getHeight() / 2;
+        final int x = doneLocation[0] - rowLocation[0] + centerX;
+        final int y = doneLocation[1] - rowLocation[1] + centerY;
+        dismissPopups(x, y);
+    }
+
     protected SwipeHelper.LongPressListener getNotificationLongClicker() {
         return new SwipeHelper.LongPressListener() {
             @Override
@@ -1119,7 +1143,8 @@
                             }
                         });
                         a.start();
-                        guts.setExposed(true);
+                        guts.setExposed(true /* exposed */,
+                                mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
                         row.closeRemoteInput();
                         mStackScroller.onHeightChanged(null, true /* needsAnimation */);
                         mNotificationGutsExposed = guts;
@@ -1147,31 +1172,7 @@
 
     public void dismissPopups(int x, int y, boolean resetGear, boolean animate) {
         if (mNotificationGutsExposed != null) {
-            final NotificationGuts v = mNotificationGutsExposed;
-            mNotificationGutsExposed = null;
-
-            if (v.getWindowToken() == null) return;
-            if (x == -1 || y == -1) {
-                x = (v.getLeft() + v.getRight()) / 2;
-                y = (v.getTop() + v.getHeight() / 2);
-            }
-            final double horz = Math.max(v.getWidth() - x, x);
-            final double vert = Math.max(v.getHeight() - y, y);
-            final float r = (float) Math.hypot(horz, vert);
-            final Animator a = ViewAnimationUtils.createCircularReveal(v,
-                    x, y, r, 0);
-            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-            a.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    super.onAnimationEnd(animation);
-                    v.setVisibility(View.GONE);
-                }
-            });
-            a.start();
-            v.setExposed(false);
-            mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+            mNotificationGutsExposed.closeControls(x, y, true /* notify */);
         }
         if (resetGear) {
             mStackScroller.resetExposedGearView(animate, true /* force */);
@@ -1179,6 +1180,12 @@
     }
 
     @Override
+    public void onGutsClosed(NotificationGuts guts) {
+        mStackScroller.onHeightChanged(null, true /* needsAnimation */);
+        mNotificationGutsExposed = null;
+    }
+
+    @Override
     public void showRecentApps(boolean triggeredFromAltTab) {
         int msg = MSG_SHOW_RECENT_APPS;
         mHandler.removeMessages(msg);
@@ -1450,6 +1457,8 @@
         }
     }
 
+    protected void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {}
+
     protected void onLockedRemoteInput(ExpandableNotificationRow row, View clickedView) {}
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 7ca7d12..e25f9de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -618,6 +618,19 @@
         return mOnKeyguard;
     }
 
+    public void removeAllChildren() {
+        List<ExpandableNotificationRow> notificationChildren
+                = mChildrenContainer.getNotificationChildren();
+        ArrayList<ExpandableNotificationRow> clonedList = new ArrayList<>(notificationChildren);
+        for (int i = 0; i < clonedList.size(); i++) {
+            ExpandableNotificationRow row = clonedList.get(i);
+            mChildrenContainer.removeNotification(row);
+            mHeaderUtil.restoreNotificationHeader(row);
+            row.setIsChildInGroup(false, null);
+        }
+        onChildrenCountChanged();
+    }
+
     public interface ExpansionLogger {
         public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 2b365dc..fff1491 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -17,13 +17,24 @@
 package com.android.systemui.statusbar;
 
 import android.app.AlertDialog;
+import android.app.AppGlobals;
 import android.app.Dialog;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Icon;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
 import android.hardware.input.InputManager;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.ContextThemeWrapper;
@@ -37,9 +48,12 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager.KeyboardShortcutsReceiver;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
 import android.widget.TextView;
 
+import com.android.internal.app.AssistUtils;
 import com.android.systemui.R;
 import com.android.systemui.recents.Recents;
 
@@ -52,161 +66,16 @@
 /**
  * Contains functionality for handling keyboard shortcuts.
  */
-public class KeyboardShortcuts {
+public final class KeyboardShortcuts {
     private static final String TAG = KeyboardShortcuts.class.getSimpleName();
-
-    private static final SparseArray<String> SPECIAL_CHARACTER_NAMES = new SparseArray<>();
-    private static final SparseArray<String> MODIFIER_NAMES = new SparseArray<>();
-
-    private static void loadSpecialCharacterNames(Context context) {
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_PERIOD, ".");
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
-                context.getString(R.string.keyboard_key_media_play_pause));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
-                context.getString(R.string.keyboard_key_media_previous));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_REWIND,
-                context.getString(R.string.keyboard_key_media_rewind));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
-                context.getString(R.string.keyboard_key_media_fast_forward));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_A,
-                context.getString(R.string.keyboard_key_button_template, "A"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_B,
-                context.getString(R.string.keyboard_key_button_template, "B"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_C,
-                context.getString(R.string.keyboard_key_button_template, "C"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_X,
-                context.getString(R.string.keyboard_key_button_template, "X"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Y,
-                context.getString(R.string.keyboard_key_button_template, "Y"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_Z,
-                context.getString(R.string.keyboard_key_button_template, "Z"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L1,
-                context.getString(R.string.keyboard_key_button_template, "L1"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R1,
-                context.getString(R.string.keyboard_key_button_template, "R1"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_L2,
-                context.getString(R.string.keyboard_key_button_template, "L2"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_R2,
-                context.getString(R.string.keyboard_key_button_template, "R2"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_START,
-                context.getString(R.string.keyboard_key_button_template, "Start"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_SELECT,
-                context.getString(R.string.keyboard_key_button_template, "Select"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BUTTON_MODE,
-                context.getString(R.string.keyboard_key_button_template, "Mode"));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_BREAK, "Break");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F1, "F1");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F2, "F2");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F3, "F3");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F4, "F4");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F5, "F5");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F6, "F6");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F7, "F7");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F8, "F8");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F9, "F9");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F10, "F10");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F11, "F11");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_F12, "F12");
-        SPECIAL_CHARACTER_NAMES.put(
-                KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_0,
-                context.getString(R.string.keyboard_key_numpad_template, "0"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_1,
-                context.getString(R.string.keyboard_key_numpad_template, "1"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_2,
-                context.getString(R.string.keyboard_key_numpad_template, "2"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_3,
-                context.getString(R.string.keyboard_key_numpad_template, "3"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_4,
-                context.getString(R.string.keyboard_key_numpad_template, "4"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_5,
-                context.getString(R.string.keyboard_key_numpad_template, "5"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_6,
-                context.getString(R.string.keyboard_key_numpad_template, "6"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_7,
-                context.getString(R.string.keyboard_key_numpad_template, "7"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_8,
-                context.getString(R.string.keyboard_key_numpad_template, "8"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_9,
-                context.getString(R.string.keyboard_key_numpad_template, "9"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
-                context.getString(R.string.keyboard_key_numpad_template, "/"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
-                context.getString(R.string.keyboard_key_numpad_template, "*"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
-                context.getString(R.string.keyboard_key_numpad_template, "-"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ADD,
-                context.getString(R.string.keyboard_key_numpad_template, "+"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_DOT,
-                context.getString(R.string.keyboard_key_numpad_template, "."));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
-                context.getString(R.string.keyboard_key_numpad_template, ","));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
-                context.getString(R.string.keyboard_key_numpad_template,
-                        context.getString(R.string.keyboard_key_enter)));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
-                context.getString(R.string.keyboard_key_numpad_template, "="));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
-                context.getString(R.string.keyboard_key_numpad_template, "("));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
-                context.getString(R.string.keyboard_key_numpad_template, ")"));
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_EISU, "英数");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_HENKAN, "変換");
-        SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
-
-        MODIFIER_NAMES.put(KeyEvent.META_META_ON, "Meta");
-        MODIFIER_NAMES.put(KeyEvent.META_CTRL_ON, "Ctrl");
-        MODIFIER_NAMES.put(KeyEvent.META_ALT_ON, "Alt");
-        MODIFIER_NAMES.put(KeyEvent.META_SHIFT_ON, "Shift");
-        MODIFIER_NAMES.put(KeyEvent.META_SYM_ON, "Sym");
-        MODIFIER_NAMES.put(KeyEvent.META_FUNCTION_ON, "Fn");
-    }
+    private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
+    private final SparseArray<String> mModifierNames = new SparseArray<>();
+    private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
+    private final SparseArray<Drawable> mModifierDrawables = new SparseArray<>();
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final Context mContext;
+    private final IPackageManager mPackageManager;
     private final OnClickListener dialogCloseListener =  new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int id) {
             dismissKeyboardShortcutsDialog();
@@ -218,9 +87,171 @@
 
     public KeyboardShortcuts(Context context) {
         this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
-        if (SPECIAL_CHARACTER_NAMES.size() == 0) {
-            loadSpecialCharacterNames(context);
-        }
+        this.mPackageManager = AppGlobals.getPackageManager();
+        loadResources(context);
+    }
+
+    private void loadResources(Context context) {
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_BACK, context.getString(R.string.keyboard_key_back));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_UP, context.getString(R.string.keyboard_key_dpad_up));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_DOWN, context.getString(R.string.keyboard_key_dpad_down));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_LEFT, context.getString(R.string.keyboard_key_dpad_left));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_RIGHT, context.getString(R.string.keyboard_key_dpad_right));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DPAD_CENTER, context.getString(R.string.keyboard_key_dpad_center));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_PERIOD, ".");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_TAB, context.getString(R.string.keyboard_key_tab));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_SPACE, context.getString(R.string.keyboard_key_space));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_ENTER, context.getString(R.string.keyboard_key_enter));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_DEL, context.getString(R.string.keyboard_key_backspace));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,
+                context.getString(R.string.keyboard_key_media_play_pause));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MEDIA_STOP, context.getString(R.string.keyboard_key_media_stop));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MEDIA_NEXT, context.getString(R.string.keyboard_key_media_next));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_PREVIOUS,
+                context.getString(R.string.keyboard_key_media_previous));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_REWIND,
+                context.getString(R.string.keyboard_key_media_rewind));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD,
+                context.getString(R.string.keyboard_key_media_fast_forward));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_PAGE_UP, context.getString(R.string.keyboard_key_page_up));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_PAGE_DOWN, context.getString(R.string.keyboard_key_page_down));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_A,
+                context.getString(R.string.keyboard_key_button_template, "A"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_B,
+                context.getString(R.string.keyboard_key_button_template, "B"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_C,
+                context.getString(R.string.keyboard_key_button_template, "C"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_X,
+                context.getString(R.string.keyboard_key_button_template, "X"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Y,
+                context.getString(R.string.keyboard_key_button_template, "Y"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_Z,
+                context.getString(R.string.keyboard_key_button_template, "Z"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L1,
+                context.getString(R.string.keyboard_key_button_template, "L1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R1,
+                context.getString(R.string.keyboard_key_button_template, "R1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_L2,
+                context.getString(R.string.keyboard_key_button_template, "L2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_R2,
+                context.getString(R.string.keyboard_key_button_template, "R2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_START,
+                context.getString(R.string.keyboard_key_button_template, "Start"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_SELECT,
+                context.getString(R.string.keyboard_key_button_template, "Select"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BUTTON_MODE,
+                context.getString(R.string.keyboard_key_button_template, "Mode"));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_FORWARD_DEL, context.getString(R.string.keyboard_key_forward_del));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_ESCAPE, "Esc");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_SYSRQ, "SysRq");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_BREAK, "Break");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_SCROLL_LOCK, "Scroll Lock");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MOVE_HOME, context.getString(R.string.keyboard_key_move_home));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_MOVE_END, context.getString(R.string.keyboard_key_move_end));
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_INSERT, context.getString(R.string.keyboard_key_insert));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F1, "F1");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F2, "F2");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F3, "F3");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F4, "F4");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F5, "F5");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F6, "F6");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F7, "F7");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F8, "F8");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F9, "F9");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F10, "F10");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F11, "F11");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_F12, "F12");
+        mSpecialCharacterNames.put(
+                KeyEvent.KEYCODE_NUM_LOCK, context.getString(R.string.keyboard_key_num_lock));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_0,
+                context.getString(R.string.keyboard_key_numpad_template, "0"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_1,
+                context.getString(R.string.keyboard_key_numpad_template, "1"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_2,
+                context.getString(R.string.keyboard_key_numpad_template, "2"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_3,
+                context.getString(R.string.keyboard_key_numpad_template, "3"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_4,
+                context.getString(R.string.keyboard_key_numpad_template, "4"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_5,
+                context.getString(R.string.keyboard_key_numpad_template, "5"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_6,
+                context.getString(R.string.keyboard_key_numpad_template, "6"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_7,
+                context.getString(R.string.keyboard_key_numpad_template, "7"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_8,
+                context.getString(R.string.keyboard_key_numpad_template, "8"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_9,
+                context.getString(R.string.keyboard_key_numpad_template, "9"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DIVIDE,
+                context.getString(R.string.keyboard_key_numpad_template, "/"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_MULTIPLY,
+                context.getString(R.string.keyboard_key_numpad_template, "*"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_SUBTRACT,
+                context.getString(R.string.keyboard_key_numpad_template, "-"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ADD,
+                context.getString(R.string.keyboard_key_numpad_template, "+"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_DOT,
+                context.getString(R.string.keyboard_key_numpad_template, "."));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_COMMA,
+                context.getString(R.string.keyboard_key_numpad_template, ","));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_ENTER,
+                context.getString(R.string.keyboard_key_numpad_template,
+                        context.getString(R.string.keyboard_key_enter)));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_EQUALS,
+                context.getString(R.string.keyboard_key_numpad_template, "="));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_LEFT_PAREN,
+                context.getString(R.string.keyboard_key_numpad_template, "("));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_NUMPAD_RIGHT_PAREN,
+                context.getString(R.string.keyboard_key_numpad_template, ")"));
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_ZENKAKU_HANKAKU, "半角/全角");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_EISU, "英数");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_MUHENKAN, "無変換");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_HENKAN, "変換");
+        mSpecialCharacterNames.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
+
+        mModifierNames.put(KeyEvent.META_META_ON, "Meta");
+        mModifierNames.put(KeyEvent.META_CTRL_ON, "Ctrl");
+        mModifierNames.put(KeyEvent.META_ALT_ON, "Alt");
+        mModifierNames.put(KeyEvent.META_SHIFT_ON, "Shift");
+        mModifierNames.put(KeyEvent.META_SYM_ON, "Sym");
+        mModifierNames.put(KeyEvent.META_FUNCTION_ON, "Fn");
+
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
+        mSpecialCharacterDrawables.put(
+                KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
+
+        mModifierDrawables.put(
+                KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
     }
 
     public void toggleKeyboardShortcuts(int deviceId) {
@@ -234,68 +265,11 @@
                     @Override
                     public void onKeyboardShortcutsReceived(
                             final List<KeyboardShortcutGroup> result) {
-                        KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
-                                mContext.getString(R.string.keyboard_shortcut_group_system), true);
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_home),
-                                KeyEvent.KEYCODE_ENTER, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_back),
-                                KeyEvent.KEYCODE_DEL, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(R.string.keyboard_shortcut_group_system_recents),
-                                KeyEvent.KEYCODE_TAB, KeyEvent.META_ALT_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_notifications),
-                                KeyEvent.KEYCODE_N, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_shortcuts_helper),
-                                KeyEvent.KEYCODE_SLASH, KeyEvent.META_META_ON));
-                        systemGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_system_switch_input),
-                                KeyEvent.KEYCODE_SPACE, KeyEvent.META_META_ON));
-                        result.add(systemGroup);
-
-                        KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
-                                mContext.getString(R.string.keyboard_shortcut_group_applications),
-                                true);
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_assist),
-                                KeyEvent.KEYCODE_UNKNOWN, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_browser),
-                                KeyEvent.KEYCODE_B, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_contacts),
-                                KeyEvent.KEYCODE_C, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_email),
-                                KeyEvent.KEYCODE_E, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_im),
-                                KeyEvent.KEYCODE_T, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_music),
-                                KeyEvent.KEYCODE_P, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_youtube),
-                                KeyEvent.KEYCODE_Y, KeyEvent.META_META_ON));
-                        applicationGroup.addItem(new KeyboardShortcutInfo(
-                                mContext.getString(
-                                        R.string.keyboard_shortcut_group_applications_calendar),
-                                KeyEvent.KEYCODE_L, KeyEvent.META_META_ON));
-                        result.add(applicationGroup);
-
+                        result.add(getSystemShortcuts());
+                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
+                        if (appShortcuts != null) {
+                            result.add(appShortcuts);
+                        }
                         showKeyboardShortcutsDialog(result);
                     }
                 }, deviceId);
@@ -311,6 +285,160 @@
         }
     }
 
+    private KeyboardShortcutGroup getSystemShortcuts() {
+        final KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup(
+                mContext.getString(R.string.keyboard_shortcut_group_system), true);
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_home),
+                KeyEvent.KEYCODE_ENTER,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_back),
+                KeyEvent.KEYCODE_DEL,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(R.string.keyboard_shortcut_group_system_recents),
+                KeyEvent.KEYCODE_TAB,
+                KeyEvent.META_ALT_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_notifications),
+                KeyEvent.KEYCODE_N,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_shortcuts_helper),
+                KeyEvent.KEYCODE_SLASH,
+                KeyEvent.META_META_ON));
+        systemGroup.addItem(new KeyboardShortcutInfo(
+                mContext.getString(
+                        R.string.keyboard_shortcut_group_system_switch_input),
+                KeyEvent.KEYCODE_SPACE,
+                KeyEvent.META_META_ON));
+        return systemGroup;
+    }
+
+    private KeyboardShortcutGroup getDefaultApplicationShortcuts() {
+        final int userId = mContext.getUserId();
+        final KeyboardShortcutGroup applicationGroup = new KeyboardShortcutGroup(
+                mContext.getString(R.string.keyboard_shortcut_group_applications),
+                true);
+
+        // Assist.
+        final AssistUtils assistUtils = new AssistUtils(mContext);
+        final ComponentName assistComponent = assistUtils.getAssistComponentForUser(userId);
+        PackageInfo assistPackageInfo = null;
+        try {
+            assistPackageInfo = mPackageManager.getPackageInfo(
+                    assistComponent.getPackageName(), 0, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManagerService is dead");
+        }
+
+        if (assistPackageInfo != null) {
+            final Icon assistIcon = Icon.createWithResource(
+                    assistPackageInfo.applicationInfo.packageName,
+                    assistPackageInfo.applicationInfo.icon);
+
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_assist),
+                    assistIcon,
+                    KeyEvent.KEYCODE_UNKNOWN,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Browser.
+        final Icon browserIcon = getIconForIntentCategory(Intent.CATEGORY_APP_BROWSER, userId);
+        if (browserIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_browser),
+                    browserIcon,
+                    KeyEvent.KEYCODE_B,
+                    KeyEvent.META_META_ON));
+        }
+
+
+        // Contacts.
+        final Icon contactsIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CONTACTS, userId);
+        if (contactsIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_contacts),
+                    contactsIcon,
+                    KeyEvent.KEYCODE_C,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Email.
+        final Icon emailIcon = getIconForIntentCategory(Intent.CATEGORY_APP_EMAIL, userId);
+        if (emailIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_email),
+                    emailIcon,
+                    KeyEvent.KEYCODE_E,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Messaging.
+        final Icon messagingIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MESSAGING, userId);
+        if (messagingIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_im),
+                    messagingIcon,
+                    KeyEvent.KEYCODE_T,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Music.
+        final Icon musicIcon = getIconForIntentCategory(Intent.CATEGORY_APP_MUSIC, userId);
+        if (musicIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_music),
+                    musicIcon,
+                    KeyEvent.KEYCODE_P,
+                    KeyEvent.META_META_ON));
+        }
+
+        // Calendar.
+        final Icon calendarIcon = getIconForIntentCategory(Intent.CATEGORY_APP_CALENDAR, userId);
+        if (calendarIcon != null) {
+            applicationGroup.addItem(new KeyboardShortcutInfo(
+                    mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
+                    calendarIcon,
+                    KeyEvent.KEYCODE_L,
+                    KeyEvent.META_META_ON));
+        }
+
+        return applicationGroup.getItems().size() == 0 ? null : applicationGroup;
+    }
+
+    private Icon getIconForIntentCategory(String intentCategory, int userId) {
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(intentCategory);
+
+        final PackageInfo packageInfo = getPackageInfoForIntent(intent, userId);
+        if (packageInfo != null && packageInfo.applicationInfo.icon != 0) {
+            return Icon.createWithResource(
+                    packageInfo.applicationInfo.packageName,
+                    packageInfo.applicationInfo.icon);
+        }
+        return null;
+    }
+
+    private PackageInfo getPackageInfoForIntent(Intent intent, int userId) {
+        try {
+            ResolveInfo handler;
+            handler = mPackageManager.resolveIntent(
+                    intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), 0, userId);
+            if (handler == null || handler.activityInfo == null) {
+                return null;
+            }
+            return mPackageManager.getPackageInfo(handler.activityInfo.packageName, 0, userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "PackageManagerService is dead", e);
+            return null;
+        }
+    }
+
     private void showKeyboardShortcutsDialog(
             final List<KeyboardShortcutGroup> keyboardShortcutGroups) {
         // Need to post on the main thread.
@@ -343,6 +471,8 @@
             List<KeyboardShortcutGroup> keyboardShortcutGroups) {
         LayoutInflater inflater = LayoutInflater.from(mContext);
         final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
+        // Needed to be able to scale the image items to the same height as the text items.
+        final int shortcutTextItemHeight = getShortcutTextItemHeight(inflater);
         for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
             KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
             TextView categoryTitle = (TextView) inflater.inflate(
@@ -364,7 +494,7 @@
                     Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
                     continue;
                 }
-                List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
+                List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
                 if (shortcutKeys == null) {
                     // Ignore shortcuts we can't display keys for.
                     Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
@@ -372,19 +502,48 @@
                 }
                 View shortcutView = inflater.inflate(R.layout.keyboard_shortcut_app_item,
                         shortcutContainer, false);
-                TextView textView = (TextView) shortcutView
+
+                if (info.getIcon() != null) {
+                    ImageView shortcutIcon = (ImageView) shortcutView
+                            .findViewById(R.id.keyboard_shortcuts_icon);
+                    shortcutIcon.setImageIcon(info.getIcon());
+                    shortcutIcon.setVisibility(View.VISIBLE);
+                }
+
+                TextView shortcutKeyword = (TextView) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_keyword);
-                textView.setText(info.getLabel());
+                shortcutKeyword.setText(info.getLabel());
+                if (info.getIcon() != null) {
+                    RelativeLayout.LayoutParams lp =
+                            (RelativeLayout.LayoutParams) shortcutKeyword.getLayoutParams();
+                    lp.removeRule(RelativeLayout.ALIGN_PARENT_START);
+                    shortcutKeyword.setLayoutParams(lp);
+                }
 
                 ViewGroup shortcutItemsContainer = (ViewGroup) shortcutView
                         .findViewById(R.id.keyboard_shortcuts_item_container);
                 final int shortcutKeysSize = shortcutKeys.size();
                 for (int k = 0; k < shortcutKeysSize; k++) {
-                    String shortcutKey = shortcutKeys.get(k);
-                    TextView shortcutKeyView = (TextView) inflater.inflate(
-                            R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer, false);
-                    shortcutKeyView.setText(shortcutKey);
-                    shortcutItemsContainer.addView(shortcutKeyView);
+                    StringOrDrawable shortcutRepresentation = shortcutKeys.get(k);
+                    if (shortcutRepresentation.drawable != null) {
+                        ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
+                                R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
+                                false);
+                        Bitmap bitmap = Bitmap.createBitmap(shortcutTextItemHeight,
+                                shortcutTextItemHeight, Bitmap.Config.ARGB_8888);
+                        Canvas canvas = new Canvas(bitmap);
+                        shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
+                                canvas.getHeight());
+                        shortcutRepresentation.drawable.draw(canvas);
+                        shortcutKeyIconView.setImageBitmap(bitmap);
+                        shortcutItemsContainer.addView(shortcutKeyIconView);
+                    } else if (shortcutRepresentation.string != null) {
+                        TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                                R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
+                                false);
+                        shortcutKeyTextView.setText(shortcutRepresentation.string);
+                        shortcutItemsContainer.addView(shortcutKeyTextView);
+                    }
                 }
                 shortcutContainer.addView(shortcutView);
             }
@@ -398,16 +557,29 @@
         }
     }
 
-    private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
-        List<String> shortcutKeys = getHumanReadableModifiers(info);
+    private int getShortcutTextItemHeight(LayoutInflater inflater) {
+        TextView shortcutKeyTextView = (TextView) inflater.inflate(
+                R.layout.keyboard_shortcuts_key_view, null, false);
+        shortcutKeyTextView.measure(
+                View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+        return shortcutKeyTextView.getMeasuredHeight()
+                - shortcutKeyTextView.getPaddingTop()
+                - shortcutKeyTextView.getPaddingBottom();
+    }
+
+    private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
+        List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
         if (shortcutKeys == null) {
             return null;
         }
-        String displayLabelString;
+        String displayLabelString = null;
+        Drawable displayLabelDrawable = null;
         if (info.getBaseCharacter() > Character.MIN_VALUE) {
             displayLabelString = String.valueOf(info.getBaseCharacter());
-        } else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) {
-            displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode());
+        } else if (mSpecialCharacterDrawables.get(info.getKeycode()) != null) {
+            displayLabelDrawable = mSpecialCharacterDrawables.get(info.getKeycode());
+        } else if (mSpecialCharacterNames.get(info.getKeycode()) != null) {
+            displayLabelString = mSpecialCharacterNames.get(info.getKeycode());
         } else {
             // Special case for shortcuts with no base key or keycode.
             if (info.getKeycode() == KeyEvent.KEYCODE_UNKNOWN) {
@@ -422,20 +594,31 @@
                 return null;
             }
         }
-        shortcutKeys.add(displayLabelString.toUpperCase());
+
+        if (displayLabelDrawable != null) {
+            shortcutKeys.add(new StringOrDrawable(displayLabelDrawable));
+        } else if (displayLabelString != null) {
+            shortcutKeys.add(new StringOrDrawable(displayLabelString.toUpperCase()));
+        }
         return shortcutKeys;
     }
 
-    private List<String> getHumanReadableModifiers(KeyboardShortcutInfo info) {
-        final List<String> shortcutKeys = new ArrayList<>();
+    private List<StringOrDrawable> getHumanReadableModifiers(KeyboardShortcutInfo info) {
+        final List<StringOrDrawable> shortcutKeys = new ArrayList<>();
         int modifiers = info.getModifiers();
         if (modifiers == 0) {
             return shortcutKeys;
         }
-        for(int i = 0; i < MODIFIER_NAMES.size(); ++i) {
-            final int supportedModifier = MODIFIER_NAMES.keyAt(i);
+        for(int i = 0; i < mModifierNames.size(); ++i) {
+            final int supportedModifier = mModifierNames.keyAt(i);
             if ((modifiers & supportedModifier) != 0) {
-                shortcutKeys.add(MODIFIER_NAMES.get(supportedModifier).toUpperCase());
+                if (mModifierDrawables.get(supportedModifier) != null) {
+                    shortcutKeys.add(new StringOrDrawable(
+                            mModifierDrawables.get(supportedModifier)));
+                } else {
+                    shortcutKeys.add(new StringOrDrawable(
+                            mModifierNames.get(supportedModifier).toUpperCase()));
+                }
                 modifiers &= ~supportedModifier;
             }
         }
@@ -445,4 +628,17 @@
         }
         return shortcutKeys;
     }
+
+    private static final class StringOrDrawable {
+        public String string;
+        public Drawable drawable;
+
+        public StringOrDrawable(String string) {
+            this.string = string;
+        }
+
+        public StringOrDrawable(Drawable drawable) {
+            this.drawable = drawable;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index c9fe2bd..6570221 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -34,6 +34,7 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Map;
 import java.util.Objects;
 
 /**
@@ -257,16 +258,21 @@
     }
 
     public void add(Entry entry, RankingMap ranking) {
-        mEntries.put(entry.notification.getKey(), entry);
-        updateRankingAndSort(ranking);
+        synchronized (mEntries) {
+            mEntries.put(entry.notification.getKey(), entry);
+        }
         mGroupManager.onEntryAdded(entry);
+        updateRankingAndSort(ranking);
     }
 
     public Entry remove(String key, RankingMap ranking) {
-        Entry removed = mEntries.remove(key);
+        Entry removed = null;
+        synchronized (mEntries) {
+            removed = mEntries.remove(key);
+        }
         if (removed == null) return null;
-        updateRankingAndSort(ranking);
         mGroupManager.onEntryRemoved(removed);
+        updateRankingAndSort(ranking);
         return removed;
     }
 
@@ -316,9 +322,30 @@
         return Ranking.IMPORTANCE_UNSPECIFIED;
     }
 
+    public String getOverrideGroupKey(String key) {
+        if (mRankingMap != null) {
+            mRankingMap.getRanking(key, mTmpRanking);
+            return mTmpRanking.getOverrideGroupKey();
+        }
+         return null;
+    }
+
     private void updateRankingAndSort(RankingMap ranking) {
         if (ranking != null) {
             mRankingMap = ranking;
+            synchronized (mEntries) {
+                final int N = mEntries.size();
+                for (int i = 0; i < N; i++) {
+                    Entry entry = mEntries.valueAt(i);
+                    final StatusBarNotification oldSbn = entry.notification.clone();
+                    final String overrideGroupKey = getOverrideGroupKey(entry.key);
+                    if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+                        entry.notification.setOverrideGroupKey(overrideGroupKey);
+                        mGroupManager.onEntryUpdated(entry, oldSbn);
+                    }
+                    //mGroupManager.onEntryBundlingUpdated(entry, getOverrideGroupKey(entry.key));
+                }
+            }
         }
         filterAndSort();
     }
@@ -328,16 +355,18 @@
     public void filterAndSort() {
         mSortedAndFiltered.clear();
 
-        final int N = mEntries.size();
-        for (int i = 0; i < N; i++) {
-            Entry entry = mEntries.valueAt(i);
-            StatusBarNotification sbn = entry.notification;
+        synchronized (mEntries) {
+            final int N = mEntries.size();
+            for (int i = 0; i < N; i++) {
+                Entry entry = mEntries.valueAt(i);
+                StatusBarNotification sbn = entry.notification;
 
-            if (shouldFilterOut(sbn)) {
-                continue;
+                if (shouldFilterOut(sbn)) {
+                    continue;
+                }
+
+                mSortedAndFiltered.add(entry);
             }
-
-            mSortedAndFiltered.add(entry);
         }
 
         Collections.sort(mSortedAndFiltered, mRankingComparator);
@@ -398,16 +427,17 @@
             NotificationData.Entry e = mSortedAndFiltered.get(active);
             dumpEntry(pw, indent, active, e);
         }
-
-        int M = mEntries.size();
-        pw.print(indent);
-        pw.println("inactive notifications: " + (M - active));
-        int inactiveCount = 0;
-        for (int i = 0; i < M; i++) {
-            Entry entry = mEntries.valueAt(i);
-            if (!mSortedAndFiltered.contains(entry)) {
-                dumpEntry(pw, indent, inactiveCount, entry);
-                inactiveCount++;
+        synchronized (mEntries) {
+            int M = mEntries.size();
+            pw.print(indent);
+            pw.println("inactive notifications: " + (M - active));
+            int inactiveCount = 0;
+            for (int i = 0; i < M; i++) {
+                Entry entry = mEntries.valueAt(i);
+                if (!mSortedAndFiltered.contains(entry)) {
+                    dumpEntry(pw, indent, inactiveCount, entry);
+                    inactiveCount++;
+                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 45a24a0..3c464d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -16,28 +16,35 @@
 
 package com.android.systemui.statusbar;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.app.INotificationManager;
 import android.content.Context;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.view.View;
+import android.view.ViewAnimationUtils;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.RadioButton;
+import android.widget.RadioGroup;
 import android.widget.SeekBar;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
+import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
 import com.android.systemui.tuner.TunerService;
 
 /**
@@ -46,6 +53,8 @@
 public class NotificationGuts extends LinearLayout implements TunerService.Tunable {
     public static final String SHOW_SLIDER = "show_importance_slider";
 
+    private static final long CLOSE_GUTS_DELAY = 8000;
+
     private Drawable mBackground;
     private int mClipTopAmount;
     private int mActualHeight;
@@ -59,10 +68,35 @@
     private RadioButton mSilent;
     private RadioButton mReset;
 
+    private Handler mHandler;
+    private Runnable mFalsingCheck;
+    private boolean mNeedsFalsingProtection;
+    private OnGutsClosedListener mListener;
+
+    public interface OnGutsClosedListener {
+        public void onGutsClosed(NotificationGuts guts);
+    }
+
     public NotificationGuts(Context context, AttributeSet attrs) {
         super(context, attrs);
         setWillNotDraw(false);
         TunerService.get(mContext).addTunable(this, SHOW_SLIDER);
+        mHandler = new Handler();
+        mFalsingCheck = new Runnable() {
+            @Override
+            public void run() {
+                if (mNeedsFalsingProtection && mExposed) {
+                    closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+                }
+            }
+        };
+    }
+
+    public void resetFalsingCheck() {
+        mHandler.removeCallbacks(mFalsingCheck);
+        if (mNeedsFalsingProtection && mExposed) {
+            mHandler.postDelayed(mFalsingCheck, CLOSE_GUTS_DELAY);
+        }
     }
 
     @Override
@@ -130,30 +164,23 @@
             importanceSlider.setVisibility(View.VISIBLE);
             importanceButtons.setVisibility(View.GONE);
         } else {
-            int userImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+            mStartingImportance = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
             try {
-                userImportance =
+                mStartingImportance =
                         mINotificationManager.getImportance(sbn.getPackageName(), sbn.getUid());
             } catch (RemoteException e) {}
-            bindToggles(importanceButtons, userImportance, systemApp);
+            bindToggles(importanceButtons, mStartingImportance, systemApp);
             importanceButtons.setVisibility(View.VISIBLE);
             importanceSlider.setVisibility(View.GONE);
         }
     }
 
+    public boolean hasImportanceChanged() {
+        return mStartingImportance != getSelectedImportance();
+    }
+
     void saveImportance(final StatusBarNotification sbn) {
-        int progress;
-        if (mSeekBar!= null && mSeekBar.isShown()) {
-            progress = mSeekBar.getProgress();
-        } else {
-            if (mBlock.isChecked()) {
-                progress = NotificationListenerService.Ranking.IMPORTANCE_NONE;
-            } else if (mSilent.isChecked()) {
-                progress = NotificationListenerService.Ranking.IMPORTANCE_LOW;
-            } else {
-                progress = NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
-            }
-        }
+        int progress = getSelectedImportance();
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
                 progress - mStartingImportance);
         try {
@@ -163,8 +190,29 @@
         }
     }
 
+    private int getSelectedImportance() {
+        if (mSeekBar!= null && mSeekBar.isShown()) {
+            return mSeekBar.getProgress();
+        } else {
+            if (mBlock.isChecked()) {
+                return NotificationListenerService.Ranking.IMPORTANCE_NONE;
+            } else if (mSilent.isChecked()) {
+                return NotificationListenerService.Ranking.IMPORTANCE_LOW;
+            } else {
+                return NotificationListenerService.Ranking.IMPORTANCE_UNSPECIFIED;
+            }
+        }
+    }
+
     private void bindToggles(final View importanceButtons, final int importance,
             final boolean systemApp) {
+        ((RadioGroup) importanceButtons).setOnCheckedChangeListener(
+                new RadioGroup.OnCheckedChangeListener() {
+                    @Override
+                    public void onCheckedChanged(RadioGroup group, int checkedId) {
+                        resetFalsingCheck();
+                    }
+                });
         mBlock = (RadioButton) importanceButtons.findViewById(R.id.block_importance);
         mSilent = (RadioButton) importanceButtons.findViewById(R.id.silent_importance);
         mReset = (RadioButton) importanceButtons.findViewById(R.id.reset_importance);
@@ -198,6 +246,7 @@
         mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                resetFalsingCheck();
                 if (progress < minProgress) {
                     seekBar.setProgress(minProgress);
                     progress = minProgress;
@@ -210,7 +259,7 @@
 
             @Override
             public void onStartTrackingTouch(SeekBar seekBar) {
-                // no-op
+                resetFalsingCheck();
             }
 
             @Override
@@ -256,6 +305,38 @@
         mSeekBar.setProgress(mStartingImportance);
     }
 
+    public void closeControls(int x, int y, boolean notify) {
+        if (getWindowToken() == null) {
+            if (notify && mListener != null) {
+                mListener.onGutsClosed(this);
+            }
+            return;
+        }
+        if (x == -1 || y == -1) {
+            x = (getLeft() + getRight()) / 2;
+            y = (getTop() + getHeight() / 2);
+        }
+        final double horz = Math.max(getWidth() - x, x);
+        final double vert = Math.max(getHeight() - y, y);
+        final float r = (float) Math.hypot(horz, vert);
+        final Animator a = ViewAnimationUtils.createCircularReveal(this,
+                x, y, r, 0);
+        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+        a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+        a.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                setVisibility(View.GONE);
+            }
+        });
+        a.start();
+        setExposed(false, mNeedsFalsingProtection);
+        if (notify && mListener != null) {
+            mListener.onGutsClosed(this);
+        }
+    }
+
     public void setActualHeight(int actualHeight) {
         mActualHeight = actualHeight;
         invalidate();
@@ -277,8 +358,18 @@
         return false;
     }
 
-    public void setExposed(boolean exposed) {
+    public void setClosedListener(OnGutsClosedListener listener) {
+        mListener = listener;
+    }
+
+    public void setExposed(boolean exposed, boolean needsFalsingProtection) {
         mExposed = exposed;
+        mNeedsFalsingProtection = needsFalsingProtection;
+        if (mExposed && mNeedsFalsingProtection) {
+            resetFalsingCheck();
+        } else {
+            mHandler.removeCallbacks(mFalsingCheck);
+        }
     }
 
     public boolean areGutsExposed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index c70aad2..06d79a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -131,11 +131,8 @@
         mComparators.add(HeaderProcessor.forTextView(mRow,
                 com.android.internal.R.id.app_name_text));
         mComparators.add(HeaderProcessor.forTextView(mRow,
-                com.android.internal.R.id.header_sub_text));
-        mComparators.add(HeaderProcessor.forTextView(mRow,
-                com.android.internal.R.id.header_content_info));
-        mDividers.add(com.android.internal.R.id.sub_text_divider);
-        mDividers.add(com.android.internal.R.id.content_info_divider);
+                com.android.internal.R.id.header_text));
+        mDividers.add(com.android.internal.R.id.header_text_divider);
         mDividers.add(com.android.internal.R.id.time_divider);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 260c969..ec45d60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -182,7 +182,7 @@
 
     private void inflateButtons(String[] buttons, ViewGroup parent, boolean landscape) {
         for (int i = 0; i < buttons.length; i++) {
-            inflateButton(buttons[i], parent, landscape);
+            inflateButton(buttons[i], parent, landscape, i);
         }
     }
 
@@ -195,7 +195,8 @@
     }
 
     @Nullable
-    protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape) {
+    protected View inflateButton(String buttonSpec, ViewGroup parent, boolean landscape,
+            int indexInParent) {
         LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
         float size = extractSize(buttonSpec);
         String button = extractButton(buttonSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 56a7dbe..6859348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -76,6 +76,8 @@
     private Drawable mHomeDefaultIcon, mHomeCarModeIcon;
     private Drawable mRecentIcon;
     private Drawable mDockedIcon;
+    private Drawable mImeIcon;
+    private Drawable mMenuIcon;
 
     private NavigationBarGestureHelper mGestureHelper;
     private DeadZone mDeadZone;
@@ -270,7 +272,8 @@
     }
 
     private void updateIcons(Context ctx, Configuration oldConfig, Configuration newConfig) {
-        if (oldConfig.orientation != newConfig.orientation) {
+        if (oldConfig.orientation != newConfig.orientation
+                || oldConfig.densityDpi != newConfig.densityDpi) {
             mDockedIcon = ctx.getDrawable(R.drawable.ic_sysbar_docked);
         }
         if (oldConfig.densityDpi != newConfig.densityDpi) {
@@ -280,8 +283,10 @@
             mBackAltLandIcon = mBackAltIcon;
 
             mHomeDefaultIcon = ctx.getDrawable(R.drawable.ic_sysbar_home);
-
             mRecentIcon = ctx.getDrawable(R.drawable.ic_sysbar_recent);
+            mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu);
+            mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default);
+
             updateCarModeIcons(ctx);
         }
     }
@@ -348,9 +353,11 @@
 
         final boolean showImeButton = ((hints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0);
         getImeSwitchButton().setVisibility(showImeButton ? View.VISIBLE : View.INVISIBLE);
+        getImeSwitchButton().setImageDrawable(mImeIcon);
 
         // Update menu button in case the IME state has changed.
         setMenuVisibility(mShowMenu, true);
+        getMenuButton().setImageDrawable(mMenuIcon);
 
         setDisabledFlags(mDisabledFlags, true);
     }
@@ -595,14 +602,12 @@
         super.onConfigurationChanged(newConfig);
         boolean uiCarModeChanged = updateCarMode(newConfig);
         updateTaskSwitchHelper();
-        if (uiCarModeChanged) {
-            // uiMode changed either from carmode or to carmode.
-            // replace the nav bar button icons based on which mode
-            // we are switching to.
-            setNavigationIconHints(mNavigationIconHints, true);
-        }
         updateIcons(getContext(), mConfiguration, newConfig);
         updateRecentsIcon();
+        if (uiCarModeChanged || mConfiguration.densityDpi != newConfig.densityDpi) {
+            // If car mode or density changes, we need to reset the icons.
+            setNavigationIconHints(mNavigationIconHints, true);
+        }
         mConfiguration.updateFrom(newConfig);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index f7a6b271..a27ec28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -26,6 +26,7 @@
 
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Objects;
 
 /**
  * A class to handle notifications and their corresponding groups.
@@ -121,6 +122,15 @@
         }
     }
 
+    public void onEntryBundlingUpdated(final NotificationData.Entry updated,
+            final String overrideGroupKey) {
+        final StatusBarNotification oldSbn = updated.notification.clone();
+        if (!Objects.equals(oldSbn.getOverrideGroupKey(), overrideGroupKey)) {
+            updated.notification.setOverrideGroupKey(overrideGroupKey);
+            onEntryUpdated(updated, oldSbn);
+        }
+    }
+
     private void updateSuppression(NotificationGroup group) {
         if (group == null) {
             return;
@@ -129,7 +139,7 @@
         group.suppressed = group.summary != null && !group.expanded
                 && (group.children.size() == 1
                 || (group.children.size() == 0
-                        && !group.summary.notification.getNotification().isGroupChild()
+                        && group.summary.notification.getNotification().isGroupSummary()
                         && hasIsolatedChildren(group)));
         if (prevSuppressed != group.suppressed) {
             mListener.onGroupsChanged();
@@ -173,7 +183,7 @@
 
     public boolean isOnlyChildInSuppressedGroup(StatusBarNotification sbn) {
         return isGroupSuppressed(sbn.getGroupKey())
-                && sbn.getNotification().isGroupChild()
+                && !sbn.getNotification().isGroupSummary()
                 && getTotalNumberOfChildren(sbn) == 1;
     }
 
@@ -278,11 +288,12 @@
         }
         return sbn.getNotification().isGroupSummary();
     }
+
     private boolean isGroupChild(StatusBarNotification sbn) {
         if (isIsolated(sbn)) {
             return false;
         }
-        return sbn.getNotification().isGroupChild();
+        return sbn.isGroup() && !sbn.getNotification().isGroupSummary();
     }
 
     private String getGroupKey(StatusBarNotification sbn) {
@@ -335,7 +346,7 @@
 
     private boolean shouldIsolate(StatusBarNotification sbn) {
         NotificationGroup notificationGroup = mGroupMap.get(sbn.getGroupKey());
-        return sbn.getNotification().isGroupChild()
+        return (sbn.isGroup() && !sbn.getNotification().isGroupSummary())
                 && (sbn.getNotification().fullScreenIntent != null
                         || notificationGroup == null
                         || !notificationGroup.expanded
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 45e94f7..62c0fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -306,7 +306,7 @@
 
         // Calculate quick setting heights.
         int oldMaxHeight = mQsMaxExpansionHeight;
-        mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getHeader().getHeight();
+        mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQsContainer.getQsMinExpansionHeight();
         mQsMaxExpansionHeight = mQsContainer.getDesiredHeight();
         positionClockAndNotifications();
         if (mQsExpanded && mQsFullyExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index c9bb15d..c4b7932 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -845,15 +845,19 @@
     };
 
     public void cancelPeek() {
+        boolean cancelled = mPeekPending;
         if (mPeekAnimator != null) {
+            cancelled = true;
             mPeekAnimator.cancel();
         }
         removeCallbacks(mPeekRunnable);
         mPeekPending = false;
 
-        // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
-        // notify mBar that we might have closed ourselves.
-        notifyBarPanelExpansionChanged();
+        if (cancelled) {
+            // When peeking, we already tell mBar that we expanded ourselves. Make sure that we also
+            // notify mBar that we might have closed ourselves.
+            notifyBarPanelExpansionChanged();
+        }
     }
 
     public void expand(final boolean animate) {
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 fce893e..d036fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -1525,6 +1525,9 @@
                 // we are only transfering this notification to its parent, don't generate an animation
                 mStackScroller.setChildTransferInProgress(true);
             }
+            if (remove.isSummaryWithChildren()) {
+                remove.removeAllChildren();
+            }
             mStackScroller.removeView(remove);
             mStackScroller.setChildTransferInProgress(false);
         }
@@ -3303,10 +3306,14 @@
     protected void loadDimens() {
         final Resources res = mContext.getResources();
 
+        int oldBarHeight = mNaturalBarHeight;
         mNaturalBarHeight = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.status_bar_height);
-
-        mMaxAllowedKeyguardNotifications = res.getInteger(R.integer.keyguard_max_notification_count);
+        if (mStatusBarWindowManager != null && mNaturalBarHeight != oldBarHeight) {
+            mStatusBarWindowManager.setBarHeight(mNaturalBarHeight);
+        }
+        mMaxAllowedKeyguardNotifications = res.getInteger(
+                R.integer.keyguard_max_notification_count);
 
         if (DEBUG) Log.v(TAG, "updateResources");
     }
@@ -4193,6 +4200,12 @@
     }
 
     @Override
+    public void onLockedNotificationImportanceChange(OnDismissAction dismissAction) {
+        mLeaveOpenOnKeyguardHide = true;
+        dismissKeyguardThenExecute(dismissAction, true /* afterKeyguardGone */);
+    }
+
+    @Override
     protected void onLockedRemoteInput(ExpandableNotificationRow row, View clicked) {
         mLeaveOpenOnKeyguardHide = true;
         showBouncer();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 902fd3d..cc3b4bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -41,7 +41,9 @@
     private Runnable mHideExpandedRunnable = new Runnable() {
         @Override
         public void run() {
-            mBar.makeExpandedInvisible();
+            if (mPanelFraction == 0.0f) {
+                mBar.makeExpandedInvisible();
+            }
         }
     };
 
@@ -135,6 +137,7 @@
         super.onTrackingStarted();
         mBar.onTrackingStarted();
         mScrimController.onTrackingStarted();
+        removePendingHideExpandedRunnables();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
index f3aba4f..ea9a5a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java
@@ -45,7 +45,7 @@
 import com.android.systemui.tuner.TunerService;
 
 public class QuickStatusBarHeader extends BaseStatusBarHeader implements
-        NextAlarmChangeCallback, OnClickListener {
+        NextAlarmChangeCallback, OnClickListener, OnUserInfoChangedListener {
 
     private static final String TAG = "QuickStatusBarHeader";
 
@@ -54,7 +54,7 @@
     private ActivityStarter mActivityStarter;
     private NextAlarmController mNextAlarmController;
     private SettingsButton mSettingsButton;
-    private View mSettingsContainer;
+    protected View mSettingsContainer;
 
     private TextView mAlarmStatus;
     private TextView mAlarmStatusCollapsed;
@@ -75,19 +75,19 @@
 
     private QuickQSPanel mHeaderQsPanel;
     private boolean mShowEmergencyCallsOnly;
-    private MultiUserSwitch mMultiUserSwitch;
+    protected MultiUserSwitch mMultiUserSwitch;
     private ImageView mMultiUserAvatar;
 
     private float mDateTimeTranslation;
     private float mDateTimeAlarmTranslation;
     private float mDateScaleFactor;
-    private float mGearTranslation;
+    protected float mGearTranslation;
 
     private TouchAnimator mSecondHalfAnimator;
     private TouchAnimator mFirstHalfAnimator;
     private TouchAnimator mDateSizeAnimator;
     private TouchAnimator mAlarmTranslation;
-    private TouchAnimator mSettingsAlpha;
+    protected TouchAnimator mSettingsAlpha;
     private float mExpansionAmount;
     private QSTileHost mHost;
 
@@ -172,6 +172,11 @@
                 .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor)
                 .setStartDelay(.36f)
                 .build();
+
+        updateSettingsAnimator();
+    }
+
+    protected void updateSettingsAnimator() {
         mSettingsAlpha = new TouchAnimator.Builder()
                 .addFloat(mSettingsContainer, "translationY", -mGearTranslation, 0)
                 .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0)
@@ -241,7 +246,7 @@
     @Override
     protected void onDetachedFromWindow() {
         setListening(false);
-        mHost.getUserInfoController().remListener(mUserListener);
+        mHost.getUserInfoController().remListener(this);
         mHost.getNetworkController().removeEmergencyListener(this);
         super.onDetachedFromWindow();
     }
@@ -275,7 +280,7 @@
         updateVisibilities();
     }
 
-    private void updateVisibilities() {
+    protected void updateVisibilities() {
         updateAlarmVisibilities();
         mEmergencyOnly.setVisibility(mExpanded && mShowEmergencyCallsOnly
                 ? View.VISIBLE : View.INVISIBLE);
@@ -309,7 +314,7 @@
 
     public void setupHost(final QSTileHost host) {
         mHost = host;
-        host.setHeaderView(this);
+        host.setHeaderView(mExpandIndicator);
         mHeaderQsPanel.setQSPanelAndHeader(mQsPanel, this);
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
         setUserInfoController(host.getUserInfoController());
@@ -365,7 +370,7 @@
 
     @Override
     public void setUserInfoController(UserInfoController userInfoController) {
-        userInfoController.addListener(mUserListener);
+        userInfoController.addListener(this);
     }
 
     @Override
@@ -379,10 +384,8 @@
         }
     }
 
-    private final OnUserInfoChangedListener mUserListener = new OnUserInfoChangedListener() {
-        @Override
-        public void onUserInfoChanged(String name, Drawable picture) {
-            mMultiUserAvatar.setImageDrawable(picture);
-        }
-    };
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture) {
+        mMultiUserAvatar.setImageDrawable(picture);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index b271380..888e19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -323,6 +323,11 @@
         apply(mCurrentState);
     }
 
+    public void setBarHeight(int barHeight) {
+        mBarHeight = barHeight;
+        apply(mCurrentState);
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("StatusBarWindowManager state:");
         pw.println(mCurrentState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
index 093a827..dc1b35d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -17,69 +17,57 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
-import android.graphics.BitmapShader;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
 import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 /**
- * A view that displays a user image cropped to a circle with a frame.
+ * A view that displays a user image cropped to a circle with an optional frame.
  */
 public class UserAvatarView extends View {
 
-    private int mActiveFrameColor;
-    private int mFrameColor;
-    private float mFrameWidth;
-    private float mFramePadding;
-    private Bitmap mBitmap;
-    private Drawable mDrawable;
-    private boolean mIsDisabled;
-
-    private final Paint mFramePaint = new Paint();
-    private final Paint mBitmapPaint = new Paint();
-    private final Matrix mDrawMatrix = new Matrix();
-
-    private float mScale = 1;
+    private final UserIconDrawable mDrawable = new UserIconDrawable();
 
     public UserAvatarView(Context context, AttributeSet attrs,
             int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
+
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
         final int N = a.getIndexCount();
         for (int i = 0; i < N; i++) {
             int attr = a.getIndex(i);
             switch (attr) {
+                case R.styleable.UserAvatarView_avatarPadding:
+                    setAvatarPadding(a.getDimension(attr, 0));
+                    break;
                 case R.styleable.UserAvatarView_frameWidth:
                     setFrameWidth(a.getDimension(attr, 0));
                     break;
                 case R.styleable.UserAvatarView_framePadding:
                     setFramePadding(a.getDimension(attr, 0));
                     break;
-                case R.styleable.UserAvatarView_activeFrameColor:
-                    setActiveFrameColor(a.getColor(attr, 0));
-                    break;
                 case R.styleable.UserAvatarView_frameColor:
-                    setFrameColor(a.getColor(attr, 0));
+                    setFrameColor(a.getColorStateList(attr));
+                    break;
+                case R.styleable.UserAvatarView_badgeDiameter:
+                    setBadgeDiameter(a.getDimension(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_badgeMargin:
+                    setBadgeMargin(a.getDimension(attr, 0));
                     break;
             }
         }
         a.recycle();
-
-        mFramePaint.setAntiAlias(true);
-        mFramePaint.setStyle(Paint.Style.STROKE);
-        mBitmapPaint.setAntiAlias(true);
+        setBackground(mDrawable);
     }
 
     public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
@@ -94,180 +82,61 @@
         this(context, null);
     }
 
+    /**
+     * @deprecated use {@link #setAvatar(Bitmap)} instead.
+     */
+    @Deprecated
     public void setBitmap(Bitmap bitmap) {
-        setDrawable(null);
-        mBitmap = bitmap;
-        if (mBitmap != null) {
-            mBitmapPaint.setShader(new BitmapShader(
-                    bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
-        } else {
-            mBitmapPaint.setShader(null);
-        }
-        configureBounds();
-        invalidate();
+        setAvatar(bitmap);
     }
 
-    public void setFrameColor(int frameColor) {
-        mFrameColor = frameColor;
-        invalidate();
-    }
-
-    public void setActiveFrameColor(int activeFrameColor) {
-        mActiveFrameColor = activeFrameColor;
-        invalidate();
+    public void setFrameColor(ColorStateList color) {
+        mDrawable.setFrameColor(color);
     }
 
     public void setFrameWidth(float frameWidth) {
-        mFrameWidth = frameWidth;
-        invalidate();
+        mDrawable.setFrameWidth(frameWidth);
     }
 
     public void setFramePadding(float framePadding) {
-        mFramePadding = framePadding;
-        invalidate();
+        mDrawable.setFramePadding(framePadding);
     }
 
-    @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        configureBounds();
+    public void setAvatarPadding(float avatarPadding) {
+        mDrawable.setPadding(avatarPadding);
     }
 
-    public void configureBounds() {
-        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
-        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
-
-        int dwidth;
-        int dheight;
-        if (mBitmap != null) {
-            dwidth = mBitmap.getWidth();
-            dheight = mBitmap.getHeight();
-        } else if (mDrawable != null) {
-            vwidth -= 2 * (mFrameWidth - 1);
-            vheight -= 2 * (mFrameWidth - 1);
-            dwidth = vwidth;
-            dheight = vheight;
-            mDrawable.setBounds(0, 0, dwidth, dheight);
-        } else {
-            return;
-        }
-
-        float scale;
-        float dx;
-        float dy;
-
-        scale = Math.min((float) vwidth / (float) dwidth,
-                (float) vheight / (float) dheight);
-
-        dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
-        dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
-
-        mDrawMatrix.setScale(scale, scale);
-        mDrawMatrix.postTranslate(dx, dy);
-        mScale = scale;
+    public void setBadgeDiameter(float diameter) {
+        mDrawable.setBadgeRadius(diameter * 0.5f);
     }
 
-    @Override
-    protected void onDraw(Canvas canvas) {
-        int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
-        float halfW = getWidth() / 2f;
-        float halfH = getHeight() / 2f;
-        float halfSW = Math.min(halfH, halfW);
-        updateDrawableIfDisabled();
-        if (mBitmap != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.concat(mDrawMatrix);
-            float halfBW = mBitmap.getWidth() / 2f;
-            float halfBH = mBitmap.getHeight() / 2f;
-            float halfBSW = Math.min(halfBH, halfBW);
-            canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
-            canvas.restoreToCount(saveCount);
-        } else if (mDrawable != null && mScale > 0) {
-            int saveCount = canvas.getSaveCount();
-            canvas.save();
-            canvas.translate(mPaddingLeft, mPaddingTop);
-            canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
-            canvas.concat(mDrawMatrix);
-            mDrawable.draw(canvas);
-            canvas.restoreToCount(saveCount);
-        }
-        if (frameColor != 0) {
-            mFramePaint.setColor(frameColor);
-            mFramePaint.setStrokeWidth(mFrameWidth);
-            canvas.drawCircle(halfW, halfH, halfSW + (mFramePadding - mFrameWidth) / 2f,
-                    mFramePaint);
-        }
+    public void setBadgeMargin(float margin) {
+        mDrawable.setBadgeMargin(margin);
+    }
+
+    public void setAvatar(Bitmap avatar) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadge(null);
+    }
+
+    public void setAvatarWithBadge(Bitmap avatar, int userId) {
+        mDrawable.setIcon(avatar);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 
     public void setDrawable(Drawable d) {
-        if (mDrawable != null) {
-            mDrawable.setCallback(null);
-            unscheduleDrawable(mDrawable);
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-        mDrawable = d;
-        if (d != null) {
-            d.setCallback(this);
-            if (d.isStateful()) {
-                d.setState(getDrawableState());
-            }
-            d.setLayoutDirection(getLayoutDirection());
-            configureBounds();
-        }
-        if (d != null) {
-            mBitmap = null;
-        }
-        configureBounds();
-        invalidate();
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadge(null);
     }
 
-    @Override
-    public void invalidateDrawable(Drawable dr) {
-        if (dr == mDrawable) {
-            invalidate();
-        } else {
-            super.invalidateDrawable(dr);
+    public void setDrawableWithBadge(Drawable d, int userId) {
+        if (d instanceof UserIconDrawable) {
+            throw new RuntimeException("Recursively adding UserIconDrawable");
         }
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mDrawable || super.verifyDrawable(who);
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        super.drawableStateChanged();
-        if (mDrawable != null && mDrawable.isStateful()) {
-            mDrawable.setState(getDrawableState());
-        }
-    }
-
-    public void setDisabled(boolean disabled) {
-        if (mIsDisabled == disabled) {
-            return;
-        }
-        mIsDisabled = disabled;
-        invalidate();
-    }
-
-    private void updateDrawableIfDisabled() {
-        int disabledColor = getContext().getColor(R.color.qs_tile_disabled_color);
-        PorterDuffColorFilter filter = new PorterDuffColorFilter(disabledColor,
-                PorterDuff.Mode.SRC_ATOP);
-        if (mBitmap != null) {
-            if (mIsDisabled) {
-                mBitmapPaint.setColorFilter(filter);
-            } else {
-                mBitmapPaint.setColorFilter(null);
-            }
-        } else if (mDrawable != null) {
-            if (mIsDisabled) {
-                mDrawable.setColorFilter(filter);
-            } else {
-                mDrawable.setColorFilter(null);
-            }
-        }
+        mDrawable.setIconDrawable(d);
+        mDrawable.setBadgeIfManagedUser(getContext(), userId);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
index fb310a6..c39d718 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -253,17 +253,13 @@
             UserDetailItemView v = (UserDetailItemView) convertView;
 
             String name = getName(mContext, item);
-            Drawable drawable;
             if (item.picture == null) {
-                drawable = getDrawable(mContext, item).mutate();
+                v.bind(name, getDrawable(mContext, item).mutate(), item.resolveId());
             } else {
-                drawable = new BitmapDrawable(mContext.getResources(), item.picture);
+                v.bind(name, item.picture, item.info.id);
             }
             // Disable the icon if switching is disabled
-            if (!item.isSwitchToEnabled) {
-                drawable.setTint(mContext.getColor(R.color.qs_tile_disabled_color));
-            }
-            v.bind(name, drawable);
+            v.setAvatarEnabled(item.isSwitchToEnabled);
             convertView.setActivated(item.isCurrent);
             convertView.setTag(item);
             return convertView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
index 85ac755..bae5bda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserInfoController.java
@@ -26,7 +26,6 @@
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.RemoteException;
@@ -37,7 +36,7 @@
 import android.util.Pair;
 
 import com.android.internal.util.UserIcons;
-import com.android.systemui.BitmapHelper;
+import com.android.settingslib.drawable.UserIconDrawable;
 import com.android.systemui.R;
 
 import java.util.ArrayList;
@@ -155,8 +154,8 @@
                 Drawable avatar = null;
                 Bitmap rawAvatar = um.getUserIcon(userId);
                 if (rawAvatar != null) {
-                    avatar = new BitmapDrawable(mContext.getResources(),
-                            BitmapHelper.createCircularClip(rawAvatar, avatarSize, avatarSize));
+                    avatar = new UserIconDrawable(avatarSize)
+                            .setIcon(rawAvatar).setBadgeIfManagedUser(mContext, userId).bake();
                 } else {
                     avatar = UserIcons.getDefaultUserIcon(isGuest? UserHandle.USER_NULL : userId,
                             /* light= */ true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index ab44b6a..c82ba3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -23,6 +23,7 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
@@ -49,9 +50,9 @@
 import com.android.internal.logging.MetricsProto.MetricsEvent;
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.RestrictedLockUtils;
-import com.android.systemui.BitmapHelper;
 import com.android.systemui.GuestResumeSessionReceiver;
 import com.android.systemui.R;
+import com.android.systemui.SystemUISecondaryUserService;
 import com.android.systemui.qs.QSTile;
 import com.android.systemui.qs.tiles.UserDetailView;
 import com.android.systemui.statusbar.phone.ActivityStarter;
@@ -101,6 +102,8 @@
     private boolean mSimpleUserSwitcher;
     private boolean mAddUsersWhenLocked;
     private boolean mPauseRefreshUsers;
+    private int mSecondaryUser = UserHandle.USER_NULL;
+    private Intent mSecondaryUserServiceIntent;
     private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
 
     public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
@@ -121,6 +124,8 @@
         mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
                 null /* permission */, null /* scheduler */);
 
+        mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
+
         filter = new IntentFilter();
         filter.addAction(ACTION_REMOVE_GUEST);
         filter.addAction(ACTION_LOGOUT_USER);
@@ -191,8 +196,6 @@
                 boolean canSwitchUsers = mUserManager.canSwitchUsers();
                 UserInfo currentUserInfo = null;
                 UserRecord guestRecord = null;
-                int avatarSize = mContext.getResources()
-                        .getDimensionPixelSize(R.dimen.max_avatar_size);
 
                 for (UserInfo info : infos) {
                     boolean isCurrent = currentId == info.id;
@@ -213,8 +216,10 @@
                                 picture = mUserManager.getUserIcon(info.id);
 
                                 if (picture != null) {
-                                    picture = BitmapHelper.createCircularClip(
-                                            picture, avatarSize, avatarSize);
+                                    int avatarSize = mContext.getResources()
+                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
+                                    picture = Bitmap.createScaledBitmap(
+                                            picture, avatarSize, avatarSize, true);
                                 }
                             }
                             int index = isCurrent ? 0 : records.size();
@@ -477,6 +482,20 @@
                 }
                 notifyAdapters();
 
+                // Disconnect from the old secondary user's service
+                if (mSecondaryUser != UserHandle.USER_NULL) {
+                    context.stopServiceAsUser(mSecondaryUserServiceIntent,
+                            UserHandle.of(mSecondaryUser));
+                    mSecondaryUser = UserHandle.USER_NULL;
+                }
+                // Connect to the new secondary user's service (purely to ensure that a persistent
+                // SystemUI application is created for that user)
+                if (userInfo != null && !userInfo.isPrimary()) {
+                    context.startServiceAsUser(mSecondaryUserServiceIntent,
+                            UserHandle.of(userInfo.id));
+                    mSecondaryUser = userInfo.id;
+                }
+
                 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
                         && userInfo.id != UserHandle.USER_SYSTEM) {
                     showLogoutNotification(currentId);
@@ -644,8 +663,7 @@
             if (item.isAddUser) {
                 return context.getDrawable(R.drawable.ic_add_circle_qs);
             }
-            return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
-                    /* light= */ true);
+            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
         }
 
         public void refresh() {
@@ -698,6 +716,13 @@
                     isSwitchToEnabled);
         }
 
+        public int resolveId() {
+            if (isGuest || info == null) {
+                return UserHandle.USER_NULL;
+            }
+            return info.id;
+        }
+
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append("UserRecord(");
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 2f4e799..ee483e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationChildrenContainer.java
@@ -640,7 +640,7 @@
                 firstChild = false;
             }
             ExpandableNotificationRow child = mChildren.get(i);
-            minExpandHeight += child.getMinHeight();
+            minExpandHeight += child.getSingleLineView().getHeight();
             visibleChildren++;
         }
         minExpandHeight += mCollapsedBottompadding;
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 46a49ee..7c5cdfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -299,7 +299,7 @@
             setDimAmount((Float) animation.getAnimatedValue());
         }
     };
-    private ViewGroup mQsContainer;
+    protected ViewGroup mQsContainer;
     private boolean mContinuousShadowUpdate;
     private ViewTreeObserver.OnPreDrawListener mShadowUpdater
             = new ViewTreeObserver.OnPreDrawListener() {
@@ -918,7 +918,7 @@
         int positionInLinearLayout = getPositionInLinearLayout(v);
 
         int targetScroll = positionInLinearLayout + expandableView.getActualHeight() +
-                mBottomInset - getHeight() + getTopPadding();
+                getImeInset() - getHeight() + getTopPadding();
         if (mOwnScrollY < targetScroll) {
             mScroller.startScroll(mScrollX, mOwnScrollY, 0, targetScroll - mOwnScrollY);
             mDontReportNextOverScroll = true;
@@ -928,8 +928,7 @@
 
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
-        mBottomInset = Math.max(0, insets.getSystemWindowInsetBottom()
-                - (getRootView().getHeight() - getHeight()));
+        mBottomInset = insets.getSystemWindowInsetBottom();
 
         int range = getScrollRange();
         if (mOwnScrollY > range) {
@@ -987,7 +986,8 @@
     }
 
     public void dismissViewAnimated(View child, Runnable endRunnable, int delay, long duration) {
-        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration);
+        mSwipeHelper.dismissChild(child, 0, endRunnable, delay, true, duration,
+                true /* isDismissAll */);
     }
 
     public void snapViewIfNeeded(View child) {
@@ -1498,23 +1498,17 @@
     }
 
     private int getScrollRange() {
-        int scrollRange = 0;
-        ExpandableView firstChild = (ExpandableView) getFirstChildNotGone();
-        if (firstChild != null) {
-            int contentHeight = getContentHeight();
-            scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
-                    + mBottomStackSlowDownHeight);
-            if (scrollRange > 0) {
-                int firstChildMaxExpandHeight = getMaxExpandHeight(firstChild);
-                // We want to at least be able collapse the first item and not ending in a weird
-                // end state.
-                scrollRange = Math.max(scrollRange, firstChildMaxExpandHeight
-                        - firstChild.getMinHeight());
-            }
-        }
-        int imeOverlap = Math.max(0,
-                getContentHeight() - (getHeight() - mBottomInset));
-        return scrollRange + imeOverlap;
+        int contentHeight = getContentHeight();
+        int scrollRange = Math.max(0, contentHeight - mMaxLayoutHeight + mBottomStackPeekSize
+                + mBottomStackSlowDownHeight);
+        int imeInset = getImeInset();
+        scrollRange += Math.min(imeInset, Math.max(0,
+                getContentHeight() - (getHeight() - imeInset)));
+        return scrollRange;
+    }
+
+    private int getImeInset() {
+        return Math.max(0, mBottomInset - (getRootView().getHeight() - getHeight()));
     }
 
     /**
@@ -3201,9 +3195,6 @@
             disableClipOptimization();
         }
         handleDismissAllClipping();
-        if (mCurrIconRow != null && mCurrIconRow.isVisible()) {
-            mCurrIconRow.getNotificationParent().animateTranslateNotification(0 /* left target */);
-        }
     }
 
     private void handleDismissAllClipping() {
@@ -3463,8 +3454,10 @@
     }
 
     private class NotificationSwipeHelper extends SwipeHelper {
-        private static final long GEAR_SHOW_DELAY = 60;
+        private static final long SHOW_GEAR_DELAY = 60;
+        private static final long COVER_GEAR_DELAY = 4000;
         private CheckForDrag mCheckForDrag;
+        private Runnable mFalsingCheck;
         private Handler mHandler;
         private boolean mGearSnappedTo;
         private boolean mGearSnappedOnLeft;
@@ -3472,6 +3465,12 @@
         public NotificationSwipeHelper(int swipeDirection, Callback callback, Context context) {
             super(swipeDirection, callback, context);
             mHandler = new Handler();
+            mFalsingCheck = new Runnable() {
+                @Override
+                public void run() {
+                    resetExposedGearView(true /* animate */, true /* force */);
+                }
+            };
         }
 
         @Override
@@ -3486,6 +3485,7 @@
             }
             mCheckForDrag = null;
             mCurrIconRow = null;
+            mHandler.removeCallbacks(mFalsingCheck);
 
             // Slide back any notifications that might be showing a gear
             resetExposedGearView(true /* animate */, false /* force */);
@@ -3499,6 +3499,8 @@
 
         @Override
         public void onMoveUpdate(View view, float translation, float delta) {
+            mHandler.removeCallbacks(mFalsingCheck);
+
             if (mCurrIconRow != null) {
                 mCurrIconRow.setSnapping(false); // If we're moving, we're not snapping.
 
@@ -3624,6 +3626,12 @@
                 setSnappedToGear(true);
             }
             onDragCancelled(animView);
+
+            // If we're on the lockscreen we want to false this.
+            if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+                mHandler.removeCallbacks(mFalsingCheck);
+                mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
+            }
             super.snapChild(animView, target, velocity);
         }
 
@@ -3631,25 +3639,24 @@
             if (mTranslatingParentView == null) {
                 return false;
             }
-            final float snapBackThreshold = getSpaceForGear(animView);
+            // If the notification can't be dismissed then how far it can move is
+            // restricted -- reduce the distance it needs to move in this case.
+            final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
+            final float snapBackThreshold = getSpaceForGear(animView) * multiplier;
             final float translation = getTranslation(animView);
             final boolean fromLeft = translation > 0;
             final float absTrans = Math.abs(translation);
             final float notiThreshold = getSize(mTranslatingParentView) * 0.4f;
 
-            // If the notification can't be dismissed then how far it can move is
-            // restricted -- reduce the distance it needs to move in this case.
-            final float multiplier = canChildBeDismissed(animView) ? 0.4f : 0.2f;
-            return absTrans >= snapBackThreshold * 0.4f && absTrans <= notiThreshold;
+            return mCurrIconRow.isVisible() && (mCurrIconRow.isIconOnLeft()
+                    ? (translation > snapBackThreshold && translation <= notiThreshold)
+                    : (translation < -snapBackThreshold && translation >= -notiThreshold));
         }
 
         @Override
         public Animator getViewTranslationAnimator(View v, float target,
                 AnimatorUpdateListener listener) {
-            if (mDismissAllInProgress) {
-                // When dismissing all, we translate the entire view instead.
-                return super.getViewTranslationAnimator(v, target, listener);
-            } else if (v instanceof ExpandableNotificationRow) {
+            if (v instanceof ExpandableNotificationRow) {
                 return ((ExpandableNotificationRow) v).getTranslateViewAnimator(target, listener);
             } else {
                 return super.getViewTranslationAnimator(v, target, listener);
@@ -3658,22 +3665,12 @@
 
         @Override
         public void setTranslation(View v, float translate) {
-            if (mDismissAllInProgress) {
-                // When dismissing all, we translate the entire view instead.
-                super.setTranslation(v, translate);
-            } else {
-                ((ExpandableView) v).setTranslation(translate);
-            }
+            ((ExpandableView) v).setTranslation(translate);
         }
 
         @Override
         public float getTranslation(View v) {
-            if (mDismissAllInProgress) {
-                // When dismissing all, we translate the entire view instead.
-                return super.getTranslation(v);
-            } else {
-                return ((ExpandableView) v).getTranslation();
-            }
+            return ((ExpandableView) v).getTranslation();
         }
 
         public void closeControlsIfOutsideTouch(MotionEvent ev) {
@@ -3740,7 +3737,7 @@
         private void checkForDrag() {
             if (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag)) {
                 mCheckForDrag = new CheckForDrag();
-                mHandler.postDelayed(mCheckForDrag, GEAR_SHOW_DELAY);
+                mHandler.postDelayed(mCheckForDrag, SHOW_GEAR_DELAY);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index f9bb5e3..450001f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.view.View;
@@ -29,12 +30,22 @@
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.tv.pip.PipManager;
 
-/*
+/**
  * Status bar implementation for "large screen" products that mostly present no on-screen nav
  */
 
 public class TvStatusBar extends BaseStatusBar {
 
+    /**
+     * Tracking calls to View.setSystemUiVisibility().
+     */
+    int mSystemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE;
+
+    /**
+     * Last value sent to window manager.
+     */
+    private int mLastDispatchedSystemUiVisibility = ~View.SYSTEM_UI_FLAG_VISIBLE;
+
     @Override
     public void setIcon(String slot, StatusBarIcon icon) {
     }
@@ -207,4 +218,30 @@
     @Override
     public void clickTile(ComponentName tile) {
     }
+
+    @Override
+    public void start() {
+        super.start();
+        putComponent(TvStatusBar.class, this);
+    }
+
+    public void updateRecentsVisibility(boolean visible) {
+        // Update the recents visibility flag
+        if (visible) {
+            mSystemUiVisibility |= View.RECENT_APPS_VISIBLE;
+        } else {
+            mSystemUiVisibility &= ~View.RECENT_APPS_VISIBLE;
+        }
+        notifyUiVisibilityChanged(mSystemUiVisibility);
+    }
+
+    private void notifyUiVisibilityChanged(int vis) {
+        try {
+            if (mLastDispatchedSystemUiVisibility != vis) {
+                mWindowManagerService.statusBarVisibilityChanged(vis);
+                mLastDispatchedSystemUiVisibility = vis;
+            }
+        } catch (RemoteException ex) {
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
index fe54090..a445e77 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipManager.java
@@ -36,6 +36,7 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.util.Log;
+import android.util.Pair;
 
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
@@ -63,6 +64,20 @@
     private static final int MAX_RUNNING_TASKS_COUNT = 10;
 
     /**
+     * List of package and class name which are considered as Settings,
+     * so PIP location should be adjusted to the left of the side panel.
+     */
+    private static final List<Pair<String, String>> sSettingsPackageAndClassNamePairList;
+    static {
+        sSettingsPackageAndClassNamePairList = new ArrayList<>();
+        sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
+                "com.android.tv.settings", null));
+        sSettingsPackageAndClassNamePairList.add(new Pair<String, String>(
+                "com.google.android.leanbacklauncher",
+                "com.google.android.leanbacklauncher.settings.HomeScreenSettingsActivity"));
+    }
+
+    /**
      * State when there's no PIP.
      */
     public static final int STATE_NO_PIP = 0;
@@ -108,6 +123,7 @@
     private int mSuspendPipResizingReason;
 
     private Context mContext;
+    private SystemServicesProxy mSystemServiceProxy;
     private PipRecentsOverlayManager mPipRecentsOverlayManager;
     private IActivityManager mActivityManager;
     private MediaSessionManager mMediaSessionManager;
@@ -117,6 +133,8 @@
     private List<MediaListener> mMediaListeners = new ArrayList<>();
     private Rect mCurrentPipBounds;
     private Rect mPipBounds;
+    private Rect mDefaultPipBounds;
+    private Rect mSettingsPipBounds;
     private Rect mMenuModePipBounds;
     private Rect mRecentsPipBounds;
     private Rect mRecentsFocusedPipBounds;
@@ -176,8 +194,10 @@
         mInitialized = true;
         mContext = context;
         Resources res = context.getResources();
-        mPipBounds = Rect.unflattenFromString(res.getString(
+        mDefaultPipBounds = Rect.unflattenFromString(res.getString(
                 com.android.internal.R.string.config_defaultPictureInPictureBounds));
+        mSettingsPipBounds = Rect.unflattenFromString(res.getString(
+                R.string.pip_settings_bounds));
         mMenuModePipBounds = Rect.unflattenFromString(res.getString(
                 R.string.pip_menu_bounds));
         mRecentsPipBounds = Rect.unflattenFromString(res.getString(
@@ -186,9 +206,11 @@
                 R.string.pip_recents_focused_bounds));
         mRecentsFocusChangedAnimationDurationMs = res.getInteger(
                 R.integer.recents_tv_pip_focus_anim_duration);
+        mPipBounds = mDefaultPipBounds;
 
         mActivityManager = ActivityManagerNative.getDefault();
-        SystemServicesProxy.getInstance(context).registerTaskStackListener(mTaskStackListener);
+        mSystemServiceProxy = SystemServicesProxy.getInstance(context);
+        mSystemServiceProxy.registerTaskStackListener(mTaskStackListener);
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
         mContext.registerReceiver(mBroadcastReceiver, intentFilter);
@@ -522,10 +544,25 @@
         return PLAYBACK_STATE_UNAVAILABLE;
     }
 
+    private static boolean isSettingsShown(ComponentName topActivity) {
+        for (Pair<String, String> componentName : sSettingsPackageAndClassNamePairList) {
+            String packageName = componentName.first;
+            if (topActivity.getPackageName().equals(componentName.first)) {
+                String className = componentName.second;
+                if (className == null || topActivity.getClassName().equals(className)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private TaskStackListener mTaskStackListener = new TaskStackListener() {
         @Override
         public void onTaskStackChanged() {
             if (mState != STATE_NO_PIP) {
+                boolean hasPip = false;
+
                 StackInfo stackInfo = null;
                 try {
                     stackInfo = mActivityManager.getStackInfo(PINNED_STACK_ID);
@@ -541,11 +578,32 @@
                 for (int i = stackInfo.taskIds.length - 1; i >= 0; --i) {
                     if (stackInfo.taskIds[i] == mPipTaskId) {
                         // PIP task is still alive.
-                        return;
+                        hasPip = true;
+                        break;
                     }
                 }
-                // PIP task doesn't exist anymore in PINNED_STACK.
-                closePipInternal(true);
+                if (!hasPip) {
+                    // PIP task doesn't exist anymore in PINNED_STACK.
+                    closePipInternal(true);
+                    return;
+                }
+            }
+            if (mState == STATE_PIP_OVERLAY) {
+                try {
+                    List<RunningTaskInfo> runningTasks = mActivityManager.getTasks(1, 0);
+                    if (runningTasks == null || runningTasks.size() == 0) {
+                        return;
+                    }
+                    RunningTaskInfo topTask = runningTasks.get(0);
+                    Rect bounds = isSettingsShown(topTask.topActivity)
+                          ? mSettingsPipBounds : mDefaultPipBounds;
+                    if (mPipBounds != bounds) {
+                        mPipBounds = bounds;
+                        resizePinnedStack(STATE_PIP_OVERLAY);
+                    }
+                } catch (RemoteException e) {
+                    Log.d(TAG, "Failed to detect top activity", e);
+                }
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
index b90b727..47cd8e5 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipRecentsOverlayManager.java
@@ -131,9 +131,9 @@
      * Called when the PIP view in {@link com.android.systemui.recents.tv.RecentsTvActivity}
      * is focused.
      * This should be called only by {@link com.android.systemui.recents.tv.RecentsTvActivity}.
-     * @param hasRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
+     * @param allowRecentsFocusable {@code true} if Recents can have focus. (i.e. Has a recent task)
      */
-    public void requestFocus(boolean hasRecentsFocusable) {
+    public void requestFocus(boolean allowRecentsFocusable) {
         if (!mIsRecentsShown || mIsPipFocusedInRecent) {
             return;
         }
@@ -143,7 +143,7 @@
         mWindowManager.updateViewLayout(mOverlayView, mPipRecentsControlsViewFocusedLayoutParams);
         mPipControlsView.requestFocus();
         mPipControlsView.startFocusGainAnimation();
-        mRecentsView.setVisibility(hasRecentsFocusable ? View.VISIBLE : View.GONE);
+        mRecentsView.setVisibility(allowRecentsFocusable ? View.VISIBLE : View.GONE);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
index 1d5ca04..91a8493 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java
@@ -761,7 +761,37 @@
                 : (iconRes == R.drawable.ic_volume_media_bt || iconRes == row.iconRes)
                         ? Events.ICON_STATE_UNMUTE
                 : Events.ICON_STATE_UNKNOWN;
-        row.icon.setContentDescription(ss.name);
+        if (iconEnabled) {
+            if (isRingStream) {
+                if (isRingVibrate) {
+                    row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_unmute,
+                            ss.name));
+                } else {
+                    if (mController.hasVibrator()) {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_vibrate,
+                                ss.name));
+                    } else {
+                        row.icon.setContentDescription(mContext.getString(
+                                R.string.volume_stream_content_description_mute,
+                                ss.name));
+                    }
+                }
+            } else {
+                if (ss.muted || mAutomute && ss.level == 0) {
+                   row.icon.setContentDescription(mContext.getString(
+                           R.string.volume_stream_content_description_unmute,
+                           ss.name));
+                } else {
+                    row.icon.setContentDescription(mContext.getString(
+                            R.string.volume_stream_content_description_mute,
+                            ss.name));
+                }
+            }
+        } else {
+            row.icon.setContentDescription(ss.name);
+        }
 
         // update slider
         final boolean enableSlider = !zenMuted;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
index 6a81659..a30f507 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -198,7 +198,7 @@
     }
 
     private void waitForCallback(String callback) {
-        for (int i = 0; i < 25; i++) {
+        for (int i = 0; i < 50; i++) {
             if (mCallbacks.contains(callback)) {
                 mCallbacks.remove(callback);
                 return;
@@ -229,7 +229,7 @@
                 }
             });
             try {
-                lock.wait(5000);
+                lock.wait(10000);
             } catch (InterruptedException e) {
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
index f24b541..1e27603 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
@@ -18,7 +18,6 @@
 import android.content.ComponentName;
 import android.os.Handler;
 import android.os.HandlerThread;
-import android.service.quicksettings.TileService;
 import android.test.suitebuilder.annotation.SmallTest;
 import com.android.systemui.SysuiTestCase;
 import org.mockito.ArgumentCaptor;
@@ -42,11 +41,10 @@
         mTileServices = Mockito.mock(TileServices.class);
         Mockito.when(mTileServices.getContext()).thenReturn(mContext);
         mTileLifecycle = Mockito.mock(TileLifecycleManager.class);
+        Mockito.when(mTileLifecycle.isActiveTile()).thenReturn(false);
         ComponentName componentName = new ComponentName(mContext,
                 TileServiceManagerTests.class);
         Mockito.when(mTileLifecycle.getComponent()).thenReturn(componentName);
-        mContext.getSharedPreferences(TileServiceManager.PREFS_FILE, 0).edit()
-                .putInt(componentName.flattenToString(), TileService.TILE_MODE_PASSIVE).commit();
         mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mTileLifecycle);
     }
 
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index b3613df..ea3cffe 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2135,6 +2135,9 @@
     // Suggestion -> Overflow -> Remove.
     ACTION_SETTINGS_DISMISS_SUGGESTION = 387;
 
+    // Settings > Apps > Gear > Special Access > Premium SMS access
+    PREMIUM_SMS_ACCESS = 388;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 3e7466f..613f890 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -363,12 +363,6 @@
     private void enableFeatures() {
         resetStreamState();
 
-        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
-            mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
-            addFirstEventHandler(mMotionEventInjector);
-            mAms.setMotionEventInjector(mMotionEventInjector);
-        }
-
         if ((mEnabledFeatures & FLAG_FEATURE_AUTOCLICK) != 0) {
             mAutoclickController = new AutoclickController(mContext, mUserId);
             addFirstEventHandler(mAutoclickController);
@@ -384,6 +378,12 @@
             addFirstEventHandler(mMagnificationGestureHandler);
         }
 
+        if ((mEnabledFeatures & FLAG_FEATURE_INJECT_MOTION_EVENTS) != 0) {
+            mMotionEventInjector = new MotionEventInjector(mContext.getMainLooper());
+            addFirstEventHandler(mMotionEventInjector);
+            mAms.setMotionEventInjector(mMotionEventInjector);
+        }
+
         if ((mEnabledFeatures & FLAG_FEATURE_FILTER_KEY_EVENTS) != 0) {
             mKeyboardInterceptor = new KeyboardInterceptor(mAms);
             addFirstEventHandler(mKeyboardInterceptor);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index e256ecd..ca17c43 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -439,7 +439,7 @@
             }
             if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
                 mSecurityPolicy.updateActiveAndAccessibilityFocusedWindowLocked(event.getWindowId(),
-                        event.getSourceNodeId(), event.getEventType());
+                        event.getSourceNodeId(), event.getEventType(), event.getAction());
                 mSecurityPolicy.updateEventSourceLocked(event);
                 notifyAccessibilityServicesDelayedLocked(event, false);
                 notifyAccessibilityServicesDelayedLocked(event, true);
@@ -1795,9 +1795,8 @@
                 }
                 userState.mSoftKeyboardShowMode = 0;
                 userState.mServiceChangingSoftKeyboardMode = null;
+                notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
             }
-
-            notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
         }
     }
 
@@ -2151,7 +2150,7 @@
     MagnificationController getMagnificationController() {
         synchronized (mLock) {
             if (mMagnificationController == null) {
-                mMagnificationController = new MagnificationController(mContext, this);
+                mMagnificationController = new MagnificationController(mContext, this, mLock);
                 mMagnificationController.register();
                 mMagnificationController.setUserId(mCurrentUserId);
             }
@@ -3829,7 +3828,7 @@
         }
 
         public void updateActiveAndAccessibilityFocusedWindowLocked(int windowId, long nodeId,
-                int eventType) {
+                int eventType, int eventAction) {
             // The active window is either the window that has input focus or
             // the window that the user is currently touching. If the user is
             // touching a window that does not have input focus as soon as the
@@ -3882,8 +3881,12 @@
                         if (mAccessibilityFocusNodeId == nodeId) {
                             mAccessibilityFocusNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                         }
-                        if (mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
-                                && mAccessibilityFocusedWindowId == windowId) {
+                        // Clear the window with focus if it no longer has focus and we aren't
+                        // just moving focus from one view to the other in the same window
+                        if ((mAccessibilityFocusNodeId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)
+                                && (mAccessibilityFocusedWindowId == windowId)
+                                && (eventAction != AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS)
+                                ) {
                             mAccessibilityFocusedWindowId = INVALID_WINDOW_ID;
                         }
                     }
@@ -4355,6 +4358,7 @@
                     }
                 } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
                     if (readSoftKeyboardShowModeChangedLocked(userState)) {
+                        notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
                         onUserStateChangedLocked(userState);
                     }
                 }
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index e15b785..fb1ef37 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -72,7 +72,7 @@
      */
     private static final float MIN_PERSISTED_SCALE = 2.0f;
 
-    private final Object mLock = new Object();
+    private final Object mLock;
 
     /**
      * The current magnification spec. If an animation is running, this
@@ -97,12 +97,13 @@
 
     private int mUserId;
 
-    public MagnificationController(Context context, AccessibilityManagerService ams) {
+    public MagnificationController(Context context, AccessibilityManagerService ams, Object lock) {
         mAms = ams;
         mContentResolver = context.getContentResolver();
         mScreenStateObserver = new ScreenStateObserver(context, this);
         mWindowStateObserver = new WindowStateObserver(context, this);
         mSpecAnimationBridge = new SpecAnimationBridge(context);
+        mLock = lock;
     }
 
     /**
@@ -155,10 +156,10 @@
             final float offsetY = sentSpec.offsetY;
 
             // Compute the new center and update spec as needed.
-            final float centerX = (mMagnifiedBounds.width() / 2.0f
-                    + mMagnifiedBounds.left - offsetX) / scale;
-            final float centerY = (mMagnifiedBounds.height() / 2.0f
-                    + mMagnifiedBounds.top - offsetY) / scale;
+            final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale
+                    + mMagnifiedBounds.left;
+            final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale
+                    + mMagnifiedBounds.top;
             if (updateSpec) {
                 setScaleAndCenter(scale, centerX, centerY, false);
             } else {
@@ -255,7 +256,7 @@
     public float getCenterX() {
         synchronized (mLock) {
             return  (mMagnifiedBounds.width() / 2.0f
-                    + mMagnifiedBounds.left - getOffsetX()) / getScale();
+                   - getOffsetX()) / getScale() + mMagnifiedBounds.left;
         }
     }
 
@@ -278,7 +279,7 @@
     public float getCenterY() {
         synchronized (mLock) {
             return (mMagnifiedBounds.height() / 2.0f
-                    + mMagnifiedBounds.top - getOffsetY()) / getScale();
+                    - getOffsetY()) / getScale() + mMagnifiedBounds.top;
         }
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 1b0d3ac..f93fb1b 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -596,27 +596,28 @@
         }
         final boolean showBadge;
         final Intent onClickIntent;
-        if (provider.maskedBySuspendedPackage) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            if (provider.maskedBySuspendedPackage) {
                 UserInfo userInfo = mUserManager.getUserInfo(providerUserId);
                 showBadge = userInfo.isManagedProfile();
                 onClickIntent = mDevicePolicyManagerInternal.createPackageSuspendedDialogIntent(
                         providerPackage, providerUserId);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
+            } else if (provider.maskedByQuietProfile) {
+                showBadge = true;
+                onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
+                        providerUserId);
+            } else /* provider.maskedByLockedProfile */ {
+                showBadge = true;
+                onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
+                        providerUserId);
+                if (onClickIntent != null) {
+                    onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK
+                            | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                }
             }
-        } else if (provider.maskedByQuietProfile) {
-            showBadge = true;
-            onClickIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(
-                    providerUserId);
-        } else /* provider.maskedByLockedProfile */ {
-            showBadge = true;
-            onClickIntent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null,
-                    providerUserId);
-            if (onClickIntent != null) {
-                onClickIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
 
         for (int j = 0; j < widgetCount; j++) {
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index a0b5c15..e98b4aa 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -108,63 +108,71 @@
     public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
             DeviceIdleCallback callback, float thresholdAngle) {
         if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
-        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-        mWakeLock.setReferenceCounted(false);
-        mHandler = handler;
-        mSensorManager = sm;
-        mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
-        mMeasurementInProgress = false;
-        mState = STATE_INACTIVE;
-        mCallback = callback;
-        mThresholdAngle = thresholdAngle;
-        mRunningStats = new RunningSignalStats();
-        mNumSufficientSamples = (int) Math.ceil(
-                ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
-        if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+        synchronized (mLock) {
+            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+            mWakeLock.setReferenceCounted(false);
+            mHandler = handler;
+            mSensorManager = sm;
+            mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+            mMeasurementInProgress = false;
+            mState = STATE_INACTIVE;
+            mCallback = callback;
+            mThresholdAngle = thresholdAngle;
+            mRunningStats = new RunningSignalStats();
+            mNumSufficientSamples = (int) Math.ceil(
+                    ((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
+            if (DEBUG) Slog.d(TAG, "mNumSufficientSamples = " + mNumSufficientSamples);
+        }
     }
 
     /*
      * Acquire accel data until we determine AnyMotion status.
      */
     public void checkForAnyMotion() {
-      if (DEBUG) Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+        if (DEBUG) {
+            Slog.d(TAG, "checkForAnyMotion(). mState = " + mState);
+        }
         if (mState != STATE_ACTIVE) {
-            mState = STATE_ACTIVE;
-            if (DEBUG) Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
-            mCurrentGravityVector = null;
-            mPreviousGravityVector = null;
-            startOrientationMeasurement();
+            synchronized (mLock) {
+                mState = STATE_ACTIVE;
+                if (DEBUG) {
+                    Slog.d(TAG, "Moved from STATE_INACTIVE to STATE_ACTIVE.");
+                }
+                mCurrentGravityVector = null;
+                mPreviousGravityVector = null;
+                mWakeLock.acquire();
+                startOrientationMeasurementLocked();
+            }
         }
     }
 
     public void stop() {
         if (mState == STATE_ACTIVE) {
-            mState = STATE_INACTIVE;
-            if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
-            if (mMeasurementInProgress) {
-                mMeasurementInProgress = false;
-                mSensorManager.unregisterListener(mListener);
+            synchronized (mLock) {
+                mState = STATE_INACTIVE;
+                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
+                if (mMeasurementInProgress) {
+                    mMeasurementInProgress = false;
+                    mSensorManager.unregisterListener(mListener);
+                }
+                mHandler.removeCallbacks(mMeasurementTimeout);
+                mHandler.removeCallbacks(mSensorRestart);
+                mCurrentGravityVector = null;
+                mPreviousGravityVector = null;
+                mWakeLock.release();
             }
-            mHandler.removeCallbacks(mMeasurementTimeout);
-            mHandler.removeCallbacks(mSensorRestart);
-            mWakeLock.release();
-            mCurrentGravityVector = null;
-            mPreviousGravityVector = null;
         }
     }
 
-    private void startOrientationMeasurement() {
-        if (DEBUG) Slog.d(TAG, "startOrientationMeasurement: mMeasurementInProgress=" +
+    private void startOrientationMeasurementLocked() {
+        if (DEBUG) Slog.d(TAG, "startOrientationMeasurementLocked: mMeasurementInProgress=" +
             mMeasurementInProgress + ", (mAccelSensor != null)=" + (mAccelSensor != null));
-
         if (!mMeasurementInProgress && mAccelSensor != null) {
             if (mSensorManager.registerListener(mListener, mAccelSensor,
                     SAMPLING_INTERVAL_MILLIS * 1000)) {
-                mWakeLock.acquire();
                 mMeasurementInProgress = true;
                 mRunningStats.reset();
             }
-
             Message msg = Message.obtain(mHandler, mMeasurementTimeout);
             msg.setAsynchronous(true);
             mHandler.sendMessageDelayed(msg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
@@ -178,7 +186,6 @@
         if (mMeasurementInProgress) {
             mSensorManager.unregisterListener(mListener);
             mHandler.removeCallbacks(mMeasurementTimeout);
-            mWakeLock.release();
             long detectionEndTime = SystemClock.elapsedRealtime();
             mMeasurementInProgress = false;
             mPreviousGravityVector = mCurrentGravityVector;
@@ -196,8 +203,10 @@
             status = getStationaryStatus();
             if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
             if (status != RESULT_UNKNOWN) {
-                if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " +
-                        status);
+                mWakeLock.release();
+                if (DEBUG) {
+                    Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
+                }
                 mState = STATE_INACTIVE;
             } else {
                 /*
@@ -275,7 +284,7 @@
         @Override
         public void run() {
             synchronized (mLock) {
-                startOrientationMeasurement();
+                startOrientationMeasurementLocked();
             }
         }
     };
@@ -442,4 +451,4 @@
             return msg;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 377a9e2..0a2153e 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -979,6 +979,7 @@
                 usageRestrictions.put(usage, r);
             }
         }
+        notifyWatchersOfChange(code);
     }
 
     @Override
@@ -2284,6 +2285,10 @@
             pruneUserRestrictionsForToken(token, userHandle);
         }
 
+        notifyWatchersOfChange(code);
+    }
+
+    private void notifyWatchersOfChange(int code) {
         final ArrayList<Callback> clonedCallbacks;
         synchronized (this) {
             ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 48e96aa..8753992 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -124,6 +124,7 @@
     private boolean mLastBatteryLevelCritical;
     private int mLastMaxChargingCurrent;
     private int mLastMaxChargingVoltage;
+    private int mLastChargeCounter;
 
     private int mInvalidCharger;
     private int mLastInvalidCharger;
@@ -341,6 +342,7 @@
                     + ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
                     + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
                     + ", maxChargingVoltage" + mBatteryProps.maxChargingVoltage
+                    + ", chargeCounter" + mBatteryProps.batteryChargeCounter
                     + ", batteryStatus=" + mBatteryProps.batteryStatus
                     + ", batteryHealth=" + mBatteryProps.batteryHealth
                     + ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -373,6 +375,7 @@
                 mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
                 mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
                 mBatteryProps.maxChargingVoltage != mLastMaxChargingVoltage ||
+                mBatteryProps.batteryChargeCounter != mLastChargeCounter ||
                 mInvalidCharger != mLastInvalidCharger)) {
 
             if (mPlugType != mLastPlugType) {
@@ -501,6 +504,7 @@
             mLastBatteryTemperature = mBatteryProps.batteryTemperature;
             mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
             mLastMaxChargingVoltage = mBatteryProps.maxChargingVoltage;
+            mLastChargeCounter = mBatteryProps.batteryChargeCounter;
             mLastBatteryLevelCritical = mBatteryLevelCritical;
             mLastInvalidCharger = mInvalidCharger;
         }
@@ -527,6 +531,7 @@
         intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
         intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
+        intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);
         if (DEBUG) {
             Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED.  level:" + mBatteryProps.batteryLevel +
                     ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
@@ -540,7 +545,8 @@
                     ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
                     ", icon:" + icon  + ", invalid charger:" + mInvalidCharger +
                     ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent +
-                    ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage);
+                    ", maxChargingVoltage:" + mBatteryProps.maxChargingVoltage +
+                    ", chargeCounter:" + mBatteryProps.batteryChargeCounter);
         }
 
         mHandler.post(new Runnable() {
@@ -772,6 +778,7 @@
                 pw.println("  Wireless powered: " + mBatteryProps.chargerWirelessOnline);
                 pw.println("  Max charging current: " + mBatteryProps.maxChargingCurrent);
                 pw.println("  Max charging voltage: " + mBatteryProps.maxChargingVoltage);
+                pw.println("  Charge counter: " + mBatteryProps.batteryChargeCounter);
                 pw.println("  status: " + mBatteryProps.batteryStatus);
                 pw.println("  health: " + mBatteryProps.batteryHealth);
                 pw.println("  present: " + mBatteryProps.batteryPresent);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 82a36b4..966deb6 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -991,7 +991,16 @@
     @Override
     public Network getActiveNetwork() {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
+        return getActiveNetworkForUidInternal(Binder.getCallingUid());
+    }
+
+    @Override
+    public Network getActiveNetworkForUid(int uid) {
+        enforceConnectivityInternalPermission();
+        return getActiveNetworkForUidInternal(uid);
+    }
+
+    private Network getActiveNetworkForUidInternal(final int uid) {
         final int user = UserHandle.getUserId(uid);
         int vpnNetId = NETID_UNSET;
         synchronized (mVpns) {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 423f945..6a08191 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -39,7 +39,9 @@
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
+import android.net.ConnectivityManager;
 import android.net.INetworkPolicyManager;
+import android.net.NetworkInfo;
 import android.net.Uri;
 import android.os.BatteryStats;
 import android.os.Binder;
@@ -114,6 +116,7 @@
     private IBatteryStats mBatteryStats;
     private PowerManagerInternal mLocalPowerManager;
     private PowerManager mPowerManager;
+    private ConnectivityService mConnectivityService;
     private AlarmManagerService.LocalService mLocalAlarmManager;
     private INetworkPolicyManager mNetworkPolicyManager;
     private DisplayManager mDisplayManager;
@@ -128,6 +131,7 @@
     private boolean mLightEnabled;
     private boolean mDeepEnabled;
     private boolean mForceIdle;
+    private boolean mNetworkConnected;
     private boolean mScreenOn;
     private boolean mCharging;
     private boolean mNotMoving;
@@ -173,16 +177,20 @@
     private static final int LIGHT_STATE_PRE_IDLE = 3;
     /** Device is in the light idle state, trying to stay asleep as much as possible. */
     private static final int LIGHT_STATE_IDLE = 4;
+    /** Device is in the light idle state, we want to go in to idle maintenance but are
+     * waiting for network connectivity before doing so. */
+    private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
     /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
-    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 5;
+    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
     /** Device light idle state is overriden, now applying deep doze state. */
-    private static final int LIGHT_STATE_OVERRIDE = 6;
+    private static final int LIGHT_STATE_OVERRIDE = 7;
     private static String lightStateToString(int state) {
         switch (state) {
             case LIGHT_STATE_ACTIVE: return "ACTIVE";
             case LIGHT_STATE_INACTIVE: return "INACTIVE";
             case LIGHT_STATE_PRE_IDLE: return "PRE_IDLE";
             case LIGHT_STATE_IDLE: return "IDLE";
+            case LIGHT_STATE_WAITING_FOR_NETWORK: return "WAITING_FOR_NETWORK";
             case LIGHT_STATE_IDLE_MAINTENANCE: return "IDLE_MAINTENANCE";
             case LIGHT_STATE_OVERRIDE: return "OVERRIDE";
             default: return Integer.toString(state);
@@ -198,6 +206,7 @@
     private long mNextIdleDelay;
     private long mNextLightIdleDelay;
     private long mNextLightAlarmTime;
+    private long mNextSensingTimeoutAlarmTime;
     private long mCurIdleBudget;
     private long mMaintenanceStartTime;
 
@@ -314,17 +323,27 @@
 
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
-                int plugged = intent.getIntExtra("plugged", 0);
-                updateChargingLocked(plugged != 0);
-            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    Uri data = intent.getData();
-                    String ssp;
-                    if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
-                        removePowerSaveWhitelistAppInternal(ssp);
+            switch (intent.getAction()) {
+                case ConnectivityManager.CONNECTIVITY_ACTION: {
+                    synchronized (DeviceIdleController.this) {
+                        updateConnectivityStateLocked(intent);
                     }
-                }
+                } break;
+                case Intent.ACTION_BATTERY_CHANGED: {
+                    synchronized (DeviceIdleController.this) {
+                        int plugged = intent.getIntExtra("plugged", 0);
+                        updateChargingLocked(plugged != 0);
+                    }
+                } break;
+                case Intent.ACTION_PACKAGE_REMOVED: {
+                    if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                        Uri data = intent.getData();
+                        String ssp;
+                        if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
+                            removePowerSaveWhitelistAppInternal(ssp);
+                        }
+                    }
+                } break;
             }
         }
     };
@@ -339,6 +358,18 @@
         }
     };
 
+    private final AlarmManager.OnAlarmListener mSensingTimeoutAlarmListener
+            = new AlarmManager.OnAlarmListener() {
+        @Override
+        public void onAlarm() {
+            if (mState == STATE_SENSING) {
+                synchronized (DeviceIdleController.this) {
+                    becomeInactiveIfAppropriateLocked();
+                }
+            }
+        }
+    };
+
     private final AlarmManager.OnAlarmListener mDeepAlarmListener
             = new AlarmManager.OnAlarmListener() {
         @Override
@@ -924,6 +955,11 @@
     @Override
     public void onAnyMotionResult(int result) {
         if (DEBUG) Slog.d(TAG, "onAnyMotionResult(" + result + ")");
+        if (result != AnyMotionDetector.RESULT_UNKNOWN) {
+            synchronized (this) {
+                cancelSensingTimeoutAlarmLocked();
+            }
+        }
         if (result == AnyMotionDetector.RESULT_MOVED) {
             if (DEBUG) Slog.d(TAG, "RESULT_MOVED received.");
             synchronized (this) {
@@ -1300,6 +1336,7 @@
             readConfigFileLocked();
             updateWhitelistAppIdsLocked();
 
+            mNetworkConnected = true;
             mScreenOn = true;
             // Start out assuming we are charging.  If we aren't, we will at least get
             // a battery update the next time the level drops.
@@ -1325,6 +1362,8 @@
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_maint");
                 mActiveIdleWakeLock.setReferenceCounted(false);
+                mConnectivityService = (ConnectivityService)ServiceManager.getService(
+                        Context.CONNECTIVITY_SERVICE);
                 mLocalAlarmManager = getLocalService(AlarmManagerService.LocalService.class);
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
@@ -1377,11 +1416,14 @@
                 filter = new IntentFilter();
                 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                 filter.addDataScheme("package");
+                filter = new IntentFilter();
+                filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
                 getContext().registerReceiver(mReceiver, filter);
 
                 mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray);
                 mLocalAlarmManager.setDeviceIdleUserWhitelist(mPowerSaveWhitelistUserAppIdArray);
                 mDisplayManager.registerDisplayListener(mDisplayListener, null);
+                updateConnectivityStateLocked(null);
                 updateDisplayLocked();
             }
         }
@@ -1662,6 +1704,35 @@
         }
     }
 
+    void updateConnectivityStateLocked(Intent connIntent) {
+        if (mConnectivityService != null) {
+            NetworkInfo ni = mConnectivityService.getActiveNetworkInfo();
+            boolean conn;
+            if (ni == null) {
+                conn = false;
+            } else {
+                if (connIntent == null) {
+                    conn = ni.isConnected();
+                } else {
+                    final int networkType =
+                            connIntent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
+                                    ConnectivityManager.TYPE_NONE);
+                    if (ni.getType() != networkType) {
+                        return;
+                    }
+                    conn = !connIntent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+                            false);
+                }
+            }
+            if (conn != mNetworkConnected) {
+                mNetworkConnected = conn;
+                if (conn && mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+                    stepLightIdleStateLocked("network");
+                }
+            }
+        }
+    }
+
     void updateDisplayLocked() {
         mCurDisplay = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
         // We consider any situation where the display is showing something to be it on,
@@ -1746,6 +1817,7 @@
         mNextIdleDelay = 0;
         mNextLightIdleDelay = 0;
         cancelAlarmLocked();
+        cancelSensingTimeoutAlarmLocked();
         cancelLocatingLocked();
         stopMonitoringMotionLocked();
         mAnyMotionDetector.stop();
@@ -1759,7 +1831,7 @@
         if (mForceIdle) {
             mForceIdle = false;
             if (mScreenOn || mCharging) {
-                becomeActiveLocked("exit-force-idle", Process.myUid());
+                becomeActiveLocked("exit-force", Process.myUid());
             }
         }
     }
@@ -1815,22 +1887,33 @@
                 mHandler.sendEmptyMessage(MSG_REPORT_IDLE_ON_LIGHT);
                 break;
             case LIGHT_STATE_IDLE:
-                // We have been idling long enough, now it is time to do some work.
-                mActiveIdleOpCount = 1;
-                mActiveIdleWakeLock.acquire();
-                mMaintenanceStartTime = SystemClock.elapsedRealtime();
-                if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
-                    mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
-                } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
-                    mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+            case LIGHT_STATE_WAITING_FOR_NETWORK:
+                if (mNetworkConnected || mLightState == LIGHT_STATE_WAITING_FOR_NETWORK) {
+                    // We have been idling long enough, now it is time to do some work.
+                    mActiveIdleOpCount = 1;
+                    mActiveIdleWakeLock.acquire();
+                    mMaintenanceStartTime = SystemClock.elapsedRealtime();
+                    if (mCurIdleBudget < mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET) {
+                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+                    } else if (mCurIdleBudget > mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET) {
+                        mCurIdleBudget = mConstants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+                    }
+                    scheduleLightAlarmLocked(mCurIdleBudget);
+                    if (DEBUG) Slog.d(TAG,
+                            "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
+                    mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
+                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
+                    addEvent(EVENT_LIGHT_MAINTENANCE);
+                    mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
+                } else {
+                    // We'd like to do maintenance, but currently don't have network
+                    // connectivity...  let's try to wait until the network comes back.
+                    // We'll only wait for another full idle period, however, and then give up.
+                    scheduleLightAlarmLocked(mNextLightIdleDelay);
+                    if (DEBUG) Slog.d(TAG, "Moved to LIGHT_WAITING_FOR_NETWORK.");
+                    mLightState = LIGHT_STATE_WAITING_FOR_NETWORK;
+                    EventLogTags.writeDeviceIdleLight(mLightState, reason);
                 }
-                scheduleLightAlarmLocked(mCurIdleBudget);
-                if (DEBUG) Slog.d(TAG,
-                        "Moved from LIGHT_STATE_IDLE to LIGHT_STATE_IDLE_MAINTENANCE.");
-                mLightState = LIGHT_STATE_IDLE_MAINTENANCE;
-                EventLogTags.writeDeviceIdleLight(mLightState, reason);
-                addEvent(EVENT_LIGHT_MAINTENANCE);
-                mHandler.sendEmptyMessage(MSG_REPORT_IDLE_OFF);
                 break;
         }
     }
@@ -1866,15 +1949,16 @@
                 mState = STATE_SENSING;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING.");
                 EventLogTags.writeDeviceIdle(mState, reason);
-                scheduleAlarmLocked(mConstants.SENSING_TIMEOUT, false);
+                scheduleSensingTimeoutAlarmLocked(mConstants.SENSING_TIMEOUT);
                 cancelLocatingLocked();
-                mAnyMotionDetector.checkForAnyMotion();
                 mNotMoving = false;
                 mLocated = false;
                 mLastGenericLocation = null;
                 mLastGpsLocation = null;
+                mAnyMotionDetector.checkForAnyMotion();
                 break;
             case STATE_SENSING:
+                cancelSensingTimeoutAlarmLocked();
                 mState = STATE_LOCATING;
                 if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
                 EventLogTags.writeDeviceIdle(mState, reason);
@@ -2161,6 +2245,13 @@
         }
     }
 
+    void cancelSensingTimeoutAlarmLocked() {
+        if (mNextSensingTimeoutAlarmTime != 0) {
+            mNextSensingTimeoutAlarmTime = 0;
+            mAlarmManager.cancel(mSensingTimeoutAlarmListener);
+        }
+    }
+
     void scheduleAlarmLocked(long delay, boolean idleUntil) {
         if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
         if (mMotionSensor == null) {
@@ -2182,18 +2273,18 @@
 
     void scheduleLightAlarmLocked(long delay) {
         if (DEBUG) Slog.d(TAG, "scheduleLightAlarmLocked(" + delay + ")");
-        if (mMotionSensor == null) {
-            // If there is no motion sensor on this device, then we won't schedule
-            // alarms, because we can't determine if the device is not moving.  This effectively
-            // turns off normal execution of device idling, although it is still possible to
-            // manually poke it by pretending like the alarm is going off.
-            return;
-        }
         mNextLightAlarmTime = SystemClock.elapsedRealtime() + delay;
         mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                 mNextLightAlarmTime, "DeviceIdleController.light", mLightAlarmListener, mHandler);
     }
 
+    void scheduleSensingTimeoutAlarmLocked(long delay) {
+        if (DEBUG) Slog.d(TAG, "scheduleSensingAlarmLocked(" + delay + ")");
+        mNextSensingTimeoutAlarmTime = SystemClock.elapsedRealtime() + delay;
+        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNextSensingTimeoutAlarmTime,
+            "DeviceIdleController.sensing", mSensingTimeoutAlarmListener, mHandler);
+    }
+
     private static int[] buildAppIdArray(ArrayMap<String, Integer> systemApps,
             ArrayMap<String, Integer> userApps, SparseBooleanArray outAppIds) {
         outAppIds.clear();
@@ -2396,9 +2487,14 @@
         pw.println("    Print this help text.");
         pw.println("  step [light|deep]");
         pw.println("    Immediately step to next state, without waiting for alarm.");
-        pw.println("  force-idle");
+        pw.println("  force-idle [light|deep]");
         pw.println("    Force directly into idle mode, regardless of other device state.");
-        pw.println("    Use \"step\" to get out.");
+        pw.println("  force-inactive");
+        pw.println("    Force to be inactive, ready to freely step idle states.");
+        pw.println("  unforce");
+        pw.println("    Resume normal functioning after force-idle or force-inactive.");
+        pw.println("  get [light|deep|force|screen|charging|network]");
+        pw.println("    Retrieve the current given state.");
         pw.println("  disable [light|deep|all]");
         pw.println("    Completely disable device idle mode.");
         pw.println("  enable [light|deep|all]");
@@ -2438,12 +2534,10 @@
                 String arg = shell.getNextArg();
                 try {
                     if (arg == null || "deep".equals(arg)) {
-                        exitForceIdleLocked();
                         stepIdleStateLocked("s:shell");
                         pw.print("Stepped to deep: ");
                         pw.println(stateToString(mState));
                     } else if ("light".equals(arg)) {
-                        exitForceIdleLocked();
                         stepLightIdleStateLocked("s:shell");
                         pw.print("Stepped to light: "); pw.println(lightStateToString(mLightState));
                     } else {
@@ -2458,29 +2552,104 @@
                     null);
             synchronized (this) {
                 long token = Binder.clearCallingIdentity();
+                String arg = shell.getNextArg();
                 try {
-                    if (!mDeepEnabled) {
-                        pw.println("Unable to go idle; not enabled");
-                        return -1;
-                    }
-                    mForceIdle = true;
-                    becomeInactiveIfAppropriateLocked();
-                    int curState = mState;
-                    while (curState != STATE_IDLE) {
-                        stepIdleStateLocked("s:shell");
-                        if (curState == mState) {
-                            pw.print("Unable to go idle; stopped at ");
-                            pw.println(stateToString(mState));
-                            exitForceIdleLocked();
+                    if (arg == null || "deep".equals(arg)) {
+                        if (!mDeepEnabled) {
+                            pw.println("Unable to go deep idle; not enabled");
                             return -1;
                         }
-                        curState = mState;
+                        mForceIdle = true;
+                        becomeInactiveIfAppropriateLocked();
+                        int curState = mState;
+                        while (curState != STATE_IDLE) {
+                            stepIdleStateLocked("s:shell");
+                            if (curState == mState) {
+                                pw.print("Unable to go deep idle; stopped at ");
+                                pw.println(stateToString(mState));
+                                exitForceIdleLocked();
+                                return -1;
+                            }
+                            curState = mState;
+                        }
+                        pw.println("Now forced in to deep idle mode");
+                    } else if ("light".equals(arg)) {
+                        mForceIdle = true;
+                        becomeInactiveIfAppropriateLocked();
+                        int curLightState = mLightState;
+                        while (curLightState != LIGHT_STATE_IDLE) {
+                            stepIdleStateLocked("s:shell");
+                            if (curLightState == mLightState) {
+                                pw.print("Unable to go light idle; stopped at ");
+                                pw.println(lightStateToString(mLightState));
+                                exitForceIdleLocked();
+                                return -1;
+                            }
+                            curLightState = mLightState;
+                        }
+                        pw.println("Now forced in to light idle mode");
+                    } else {
+                        pw.println("Unknown idle mode: " + arg);
                     }
-                    pw.println("Now forced in to idle mode");
                 } finally {
                     Binder.restoreCallingIdentity(token);
                 }
             }
+        } else if ("force-inactive".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mForceIdle = true;
+                    becomeInactiveIfAppropriateLocked();
+                    pw.print("Light state: ");
+                    pw.print(lightStateToString(mLightState));
+                    pw.print(", deep state: ");
+                    pw.println(stateToString(mState));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        } else if ("unforce".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                long token = Binder.clearCallingIdentity();
+                try {
+                    exitForceIdleLocked();
+                    pw.print("Light state: ");
+                    pw.print(lightStateToString(mLightState));
+                    pw.print(", deep state: ");
+                    pw.println(stateToString(mState));
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        } else if ("get".equals(cmd)) {
+            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+                    null);
+            synchronized (this) {
+                String arg = shell.getNextArg();
+                if (arg != null) {
+                    long token = Binder.clearCallingIdentity();
+                    try {
+                        switch (arg) {
+                            case "light": pw.println(lightStateToString(mLightState)); break;
+                            case "deep": pw.println(stateToString(mState)); break;
+                            case "force": pw.println(mForceIdle); break;
+                            case "screen": pw.println(mScreenOn); break;
+                            case "charging": pw.println(mCharging); break;
+                            case "network": pw.println(mNetworkConnected); break;
+                            default: pw.println("Unknown get option: " + arg); break;
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                } else {
+                    pw.println("Argument required");
+                }
+            }
         } else if ("disable".equals(cmd)) {
             getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
                     null);
@@ -2795,6 +2964,7 @@
             pw.print("  mMotionSensor="); pw.println(mMotionSensor);
             pw.print("  mCurDisplay="); pw.println(mCurDisplay);
             pw.print("  mScreenOn="); pw.println(mScreenOn);
+            pw.print("  mNetworkConnected="); pw.println(mNetworkConnected);
             pw.print("  mCharging="); pw.println(mCharging);
             pw.print("  mMotionActive="); pw.println(mMotionListener.active);
             pw.print("  mNotMoving="); pw.println(mNotMoving);
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index aa0a805..9ef0259 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Trace;
 
 /**
  * Shared singleton foreground thread for the system.  This is a thread for
@@ -36,6 +37,7 @@
         if (sInstance == null) {
             sInstance = new DisplayThread();
             sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/FgThread.java b/services/core/java/com/android/server/FgThread.java
index 03765db..5f85cba 100644
--- a/services/core/java/com/android/server/FgThread.java
+++ b/services/core/java/com/android/server/FgThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Trace;
 
 /**
  * Shared singleton foreground thread for the system.  This is a thread for regular
@@ -38,6 +39,7 @@
         if (sInstance == null) {
             sInstance = new FgThread();
             sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index d6575e8..aa98648 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -34,6 +34,7 @@
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.MutableBoolean;
 import android.util.Slog;
 import android.view.KeyEvent;
 
@@ -251,7 +252,8 @@
         return isCameraLaunchEnabled(resources) || isCameraDoubleTapPowerEnabled(resources);
     }
 
-    public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive) {
+    public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
+            MutableBoolean outLaunched) {
         boolean launched = false;
         boolean intercept = false;
         long doubleTapInterval;
@@ -276,6 +278,7 @@
             }
         }
         MetricsLogger.histogram(mContext, "power_double_tap_interval", (int) doubleTapInterval);
+        outLaunched.value = launched;
         return intercept && launched;
     }
 
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 811e34e..ac7872a 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -15,6 +15,8 @@
 
 package com.android.server;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController;
 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
@@ -37,6 +39,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -133,6 +136,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -178,6 +182,12 @@
     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
     private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
 
+    @Retention(SOURCE)
+    @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
+    private @interface  HardKeyboardBehavior {
+        int WIRELESS_AFFORDANCE = 0;
+        int WIRED_AFFORDANCE = 1;
+    }
 
     final Context mContext;
     final Resources mRes;
@@ -462,6 +472,8 @@
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
     private final IPackageManager mIPackageManager;
     private final String mSlotIme;
+    @HardKeyboardBehavior
+    private final int mHardKeyboardBehavior;
 
     class SettingsObserver extends ContentObserver {
         int mUserId;
@@ -854,6 +866,8 @@
         mHasFeature = context.getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_INPUT_METHODS);
         mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);
+        mHardKeyboardBehavior = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_externalHardKeyboardBehavior);
 
         Bundle extras = new Bundle();
         extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
@@ -1712,11 +1726,13 @@
         if (isScreenLocked()) return false;
         if ((visibility & InputMethodService.IME_ACTIVE) == 0) return false;
         if (mWindowManagerInternal.isHardKeyboardAvailable()) {
-            // When physical keyboard is attached, we show the ime switcher (or notification if
-            // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
-            // exists in the IME switcher dialog.  Might be OK to remove this condition once
-            // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
-            return true;
+            if (mHardKeyboardBehavior == HardKeyboardBehavior.WIRELESS_AFFORDANCE) {
+                // When physical keyboard is attached, we show the ime switcher (or notification if
+                // NavBar is not available) because SHOW_IME_WITH_HARD_KEYBOARD settings currently
+                // exists in the IME switcher dialog.  Might be OK to remove this condition once
+                // SHOW_IME_WITH_HARD_KEYBOARD settings finds a good place to live.
+                return true;
+            }
         } else if ((visibility & InputMethodService.IME_VISIBLE) == 0) {
             return false;
         }
diff --git a/services/core/java/com/android/server/IoThread.java b/services/core/java/com/android/server/IoThread.java
index 0f29857..ad4c194 100644
--- a/services/core/java/com/android/server/IoThread.java
+++ b/services/core/java/com/android/server/IoThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Trace;
 
 /**
  * Shared singleton I/O thread for the system.  This is a thread for non-background
@@ -35,6 +36,7 @@
         if (sInstance == null) {
             sInstance = new IoThread();
             sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index ed16af51..4ac75ca 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -56,6 +56,9 @@
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.text.TextUtils;
@@ -68,15 +71,33 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.LockSettingsStorage.CredentialHash;
 
+import libcore.util.HexEncoding;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.nio.charset.StandardCharsets;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
 /**
  * Keeps the lock pattern/password data and related settings for each user.
  * Used by LockPatternUtils. Needs to be a service because Settings app also needs
@@ -90,6 +111,12 @@
     private static final int FBE_ENCRYPTED_NOTIFICATION = 0;
     private static final boolean DEBUG = false;
 
+    private static final String PROFILE_KEY_NAME_ENCRYPT = "profile_key_name_encrypt_";
+    private static final String PROFILE_KEY_NAME_DECRYPT = "profile_key_name_decrypt_";
+    private static final int PROFILE_KEY_IV_SIZE = 12;
+    private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
+    private final Object mSeparateChallengeLock = new Object();
+
     private final Context mContext;
     private final LockSettingsStorage mStorage;
     private final LockSettingsStrongAuth mStrongAuth;
@@ -125,6 +152,7 @@
 
         @Override
         public void onStart() {
+            AndroidKeyStoreProvider.install();
             mLockSettingsService = new LockSettingsService(getContext());
             publishBinderService("lock_settings", mLockSettingsService);
         }
@@ -149,6 +177,46 @@
         }
     }
 
+    /**
+     * Tie managed profile to primary profile if it is in unified mode and not tied before.
+     *
+     * @param managedUserId Managed profile user Id
+     * @param managedUserPassword Managed profile original password (when it has separated lock).
+     *            NULL when it does not have a separated lock before.
+     */
+    public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) {
+        if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId);
+        // Only for managed profile
+        if (!UserManager.get(mContext).getUserInfo(managedUserId).isManagedProfile()) {
+            return;
+        }
+        // Do not tie managed profile when work challenge is enabled
+        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+            return;
+        }
+        // Do not tie managed profile to parent when it's done already
+        if (mStorage.hasChildProfileLock(managedUserId)) {
+            return;
+        }
+        // Do not tie it to parent when parent does not have a screen lock
+        final int parentId = mUserManager.getProfileParent(managedUserId).id;
+        if (!mStorage.hasPassword(parentId) && !mStorage.hasPattern(parentId)) {
+            if (DEBUG) Slog.v(TAG, "Parent does not have a screen lock");
+            return;
+        }
+        if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!");
+        byte[] randomLockSeed = new byte[] {};
+        try {
+            randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40);
+            String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed));
+            setLockPasswordInternal(newPassword, managedUserPassword, managedUserId);
+            tieProfileLockToParent(managedUserId, newPassword);
+        } catch (NoSuchAlgorithmException | RemoteException e) {
+            Slog.e(TAG, "Fail to tie managed profile", e);
+            // Nothing client can do to fix this issue, so we do not throw exception out
+        }
+    }
+
     public LockSettingsService(Context context) {
         mContext = context;
         mStrongAuth = new LockSettingsStrongAuth(context);
@@ -254,7 +322,7 @@
                         com.android.internal.R.color.system_notification_accent_color))
                 .setContentTitle(title)
                 .setContentText(message)
-                .setContentInfo(detail)
+                .setSubText(detail)
                 .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setContentIntent(intent)
                 .build();
@@ -271,6 +339,7 @@
     }
 
     public void onUnlockUser(int userId) {
+        tieManagedProfileLockIfNecessary(userId, null);
         hideEncryptionNotification(new UserHandle(userId));
 
         // Now we have unlocked the parent user we should show notifications
@@ -294,8 +363,7 @@
                 // Notify keystore that a new user was added.
                 final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
                 final KeyStore ks = KeyStore.getInstance();
-                final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
-                final UserInfo parentInfo = um.getProfileParent(userHandle);
+                final UserInfo parentInfo = mUserManager.getProfileParent(userHandle);
                 final int parentHandle = parentInfo != null ? parentInfo.id : -1;
                 ks.onUserAdded(userHandle, parentHandle);
             } else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
@@ -343,9 +411,8 @@
 
             // These Settings changed after multi-user was enabled, hence need to be moved per user.
             if (getString("migrated_user_specific", null, 0) == null) {
-                final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
                 final ContentResolver cr = mContext.getContentResolver();
-                List<UserInfo> users = um.getUsers();
+                List<UserInfo> users = mUserManager.getUsers();
                 for (int user = 0; user < users.size(); user++) {
                     // Migrate owner info
                     final int userId = users.get(user).id;
@@ -380,8 +447,7 @@
 
             // Migrates biometric weak such that the fallback mechanism becomes the primary.
             if (getString("migrated_biometric_weak", null, 0) == null) {
-                final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
-                List<UserInfo> users = um.getUsers();
+                List<UserInfo> users = mUserManager.getUsers();
                 for (int i = 0; i < users.size(); i++) {
                     int userId = users.get(i).id;
                     long type = getLong(LockPatternUtils.PASSWORD_TYPE_KEY,
@@ -407,9 +473,7 @@
             // user was present on the system, so if we're upgrading to M and there is more than one
             // user we disable the flag to remain consistent.
             if (getString("migrated_lockscreen_disabled", null, 0) == null) {
-                final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
-
-                final List<UserInfo> users = um.getUsers();
+                final List<UserInfo> users = mUserManager.getUsers();
                 final int userCount = users.size();
                 int switchableUsers = 0;
                 for (int i = 0; i < userCount; i++) {
@@ -469,6 +533,27 @@
     }
 
     @Override
+    public boolean getSeparateProfileChallengeEnabled(int userId) throws RemoteException {
+        synchronized (mSeparateChallengeLock) {
+            return getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId);
+        }
+    }
+
+    @Override
+    public void setSeparateProfileChallengeEnabled(int userId, boolean enabled,
+            String managedUserPassword) throws RemoteException {
+        synchronized (mSeparateChallengeLock) {
+            setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId);
+            if (enabled) {
+                mStorage.removeChildProfileLock(userId);
+                removeKeystoreProfileKey(userId);
+            } else {
+                tieManagedProfileLockIfNecessary(userId, managedUserPassword);
+            }
+        }
+    }
+
+    @Override
     public void setBoolean(String key, boolean value, int userId) throws RemoteException {
         checkWritePermission(userId);
         setStringUnchecked(key, userId, value ? "1" : "0");
@@ -536,61 +621,65 @@
     @Override
     public boolean havePassword(int userId) throws RemoteException {
         // Do we need a permissions check here?
-
         return mStorage.hasPassword(userId);
     }
 
     @Override
     public boolean havePattern(int userId) throws RemoteException {
         // Do we need a permissions check here?
-
         return mStorage.hasPattern(userId);
     }
 
     private void setKeystorePassword(String password, int userHandle) {
-        final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
         final KeyStore ks = KeyStore.getInstance();
-
-        if (um.getUserInfo(userHandle).isManagedProfile()) {
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
-                ks.onUserPasswordChanged(userHandle, password);
-            } else {
-                throw new RuntimeException("Can't set keystore password on a profile that "
-                        + "doesn't have a profile challenge.");
-            }
-        } else {
-            final List<UserInfo> profiles = um.getProfiles(userHandle);
-            for (UserInfo pi : profiles) {
-                // Change password on the given user and all its profiles that don't have
-                // their own profile challenge enabled.
-                if (pi.id == userHandle || (pi.isManagedProfile()
-                        && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id))) {
-                    ks.onUserPasswordChanged(pi.id, password);
-                }
-            }
-        }
+        ks.onUserPasswordChanged(userHandle, password);
     }
 
     private void unlockKeystore(String password, int userHandle) {
-        final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
+        if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle);
         final KeyStore ks = KeyStore.getInstance();
+        ks.unlock(userHandle, password);
+    }
 
-        if (um.getUserInfo(userHandle).isManagedProfile()) {
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userHandle)) {
-                ks.unlock(userHandle, password);
+    private String getDecryptedPasswordForTiedProfile(int userId)
+            throws KeyStoreException, UnrecoverableKeyException,
+            NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException,
+            InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException,
+            CertificateException, IOException {
+        if (DEBUG) Slog.v(TAG, "Unlock keystore for child profile");
+        byte[] storedData = mStorage.readChildProfileLock(userId);
+        if (storedData == null) {
+            throw new FileNotFoundException("Child profile lock file not found");
+        }
+        byte[] iv = Arrays.copyOfRange(storedData, 0, PROFILE_KEY_IV_SIZE);
+        byte[] encryptedPassword = Arrays.copyOfRange(storedData, PROFILE_KEY_IV_SIZE,
+                storedData.length);
+        byte[] decryptionResult;
+        java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+        keyStore.load(null);
+        SecretKey decryptionKey = (SecretKey) keyStore.getKey(
+                PROFILE_KEY_NAME_DECRYPT + userId, null);
+
+        Cipher cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
+                + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE);
+
+        cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv));
+        decryptionResult = cipher.doFinal(encryptedPassword);
+        return new String(decryptionResult, StandardCharsets.UTF_8);
+    }
+
+    private void unlockChildProfile(int profileHandle) throws RemoteException {
+        try {
+            doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false,
+                    0 /* no challenge */, profileHandle);
+        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
+                | NoSuchAlgorithmException | NoSuchPaddingException
+                | InvalidAlgorithmParameterException | IllegalBlockSizeException
+                | BadPaddingException | CertificateException | IOException e) {
+            if (e instanceof FileNotFoundException) {
+                Slog.i(TAG, "Child profile key not found");
             } else {
-                throw new RuntimeException("Can't unlock a profile explicitly if it "
-                        + "doesn't have a profile challenge.");
-            }
-        } else {
-            final List<UserInfo> profiles = um.getProfiles(userHandle);
-            for (UserInfo pi : profiles) {
-                // Unlock the given user and all its profiles that don't have
-                // their own profile challenge enabled.
-                if (pi.id == userHandle || (pi.isManagedProfile()
-                        && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id))) {
-                    ks.unlock(pi.id, password);
-                }
+                Slog.e(TAG, "Failed to decrypt child profile key", e);
             }
         }
     }
@@ -627,6 +716,21 @@
         } catch (InterruptedException e) {
             Thread.currentThread().interrupt();
         }
+        try {
+            if (!mUserManager.getUserInfo(userId).isManagedProfile()) {
+                final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+                for (UserInfo pi : profiles) {
+                    // Unlock managed profile with unified lock
+                    if (pi.isManagedProfile()
+                            && !mLockPatternUtils.isSeparateProfileChallengeEnabled(pi.id)
+                            && mStorage.hasChildProfileLock(pi.id)) {
+                        unlockChildProfile(pi.id);
+                    }
+                }
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Failed to unlock child profile", e);
+        }
     }
 
     private byte[] getCurrentHandle(int userId) {
@@ -661,10 +765,57 @@
         return currentHandle;
     }
 
+    private void onUserLockChanged(int userId) throws RemoteException {
+        if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+            return;
+        }
+        final boolean isSecure = mStorage.hasPassword(userId) || mStorage.hasPattern(userId);
+        final List<UserInfo> profiles = mUserManager.getProfiles(userId);
+        final int size = profiles.size();
+        for (int i = 0; i < size; i++) {
+            final UserInfo profile = profiles.get(i);
+            if (profile.isManagedProfile()) {
+                final int managedUserId = profile.id;
+                if (mLockPatternUtils.isSeparateProfileChallengeEnabled(managedUserId)) {
+                    continue;
+                }
+                if (isSecure) {
+                    tieManagedProfileLockIfNecessary(managedUserId, null);
+                } else {
+                    getGateKeeperService().clearSecureUserId(managedUserId);
+                    mStorage.writePatternHash(null, managedUserId);
+                    setKeystorePassword(null, managedUserId);
+                    clearUserKeyProtection(managedUserId);
+                    mStorage.removeChildProfileLock(managedUserId);
+                    removeKeystoreProfileKey(managedUserId);
+                }
+            }
+        }
+    }
 
+    private boolean isManagedProfileWithUnifiedLock(int userId) {
+        return mUserManager.getUserInfo(userId).isManagedProfile()
+                && !mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+    }
+
+    private boolean isManagedProfileWithSeparatedLock(int userId) {
+        return mUserManager.getUserInfo(userId).isManagedProfile()
+                && mLockPatternUtils.isSeparateProfileChallengeEnabled(userId);
+    }
+
+    // This method should be called by LockPatternUtil only, all internal methods in this class
+    // should call setLockPatternInternal.
     @Override
     public void setLockPattern(String pattern, String savedCredential, int userId)
             throws RemoteException {
+        synchronized (mSeparateChallengeLock) {
+            setLockPatternInternal(pattern, savedCredential, userId);
+            setSeparateProfileChallengeEnabled(userId, true, null);
+        }
+    }
+
+    public void setLockPatternInternal(String pattern, String savedCredential, int userId)
+            throws RemoteException {
         byte[] currentHandle = getCurrentHandle(userId);
 
         if (pattern == null) {
@@ -672,55 +823,157 @@
             mStorage.writePatternHash(null, userId);
             setKeystorePassword(null, userId);
             clearUserKeyProtection(userId);
+            onUserLockChanged(userId);
             return;
         }
 
-        if (currentHandle == null) {
-            if (savedCredential != null) {
-                Slog.w(TAG, "Saved credential provided, but none stored");
+        if (isManagedProfileWithUnifiedLock(userId)) {
+            // get credential from keystore when managed profile has unified lock
+            try {
+                savedCredential = getDecryptedPasswordForTiedProfile(userId);
+            } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
+                    | NoSuchAlgorithmException | NoSuchPaddingException
+                    | InvalidAlgorithmParameterException | IllegalBlockSizeException
+                    | BadPaddingException | CertificateException | IOException e) {
+                if (e instanceof FileNotFoundException) {
+                    Slog.i(TAG, "Child profile key not found");
+                } else {
+                    Slog.e(TAG, "Failed to decrypt child profile key", e);
+                }
             }
-            savedCredential = null;
+        } else {
+            if (currentHandle == null) {
+                if (savedCredential != null) {
+                    Slog.w(TAG, "Saved credential provided, but none stored");
+                }
+                savedCredential = null;
+            }
         }
 
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId);
         if (enrolledHandle != null) {
             mStorage.writePatternHash(enrolledHandle, userId);
             setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId));
+            onUserLockChanged(userId);
         } else {
             throw new RemoteException("Failed to enroll pattern");
         }
     }
 
-
+    // This method should be called by LockPatternUtil only, all internal methods in this class
+    // should call setLockPasswordInternal.
     @Override
     public void setLockPassword(String password, String savedCredential, int userId)
             throws RemoteException {
-        byte[] currentHandle = getCurrentHandle(userId);
+        synchronized (mSeparateChallengeLock) {
+            setLockPasswordInternal(password, savedCredential, userId);
+            setSeparateProfileChallengeEnabled(userId, true, null);
+        }
+    }
 
+    public void setLockPasswordInternal(String password, String savedCredential, int userId)
+            throws RemoteException {
+        byte[] currentHandle = getCurrentHandle(userId);
         if (password == null) {
             getGateKeeperService().clearSecureUserId(userId);
             mStorage.writePasswordHash(null, userId);
             setKeystorePassword(null, userId);
             clearUserKeyProtection(userId);
+            onUserLockChanged(userId);
             return;
         }
 
-        if (currentHandle == null) {
-            if (savedCredential != null) {
-                Slog.w(TAG, "Saved credential provided, but none stored");
+        if (isManagedProfileWithUnifiedLock(userId)) {
+            // get credential from keystore when managed profile has unified lock
+            try {
+                savedCredential = getDecryptedPasswordForTiedProfile(userId);
+            } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
+                    | NoSuchAlgorithmException | NoSuchPaddingException
+                    | InvalidAlgorithmParameterException | IllegalBlockSizeException
+                    | BadPaddingException | CertificateException | IOException e) {
+                if (e instanceof FileNotFoundException) {
+                    Slog.i(TAG, "Child profile key not found");
+                } else {
+                    Slog.e(TAG, "Failed to decrypt child profile key", e);
+                }
             }
-            savedCredential = null;
+        } else {
+            if (currentHandle == null) {
+                if (savedCredential != null) {
+                    Slog.w(TAG, "Saved credential provided, but none stored");
+                }
+                savedCredential = null;
+            }
         }
 
         byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId);
         if (enrolledHandle != null) {
             mStorage.writePasswordHash(enrolledHandle, userId);
             setUserKeyProtection(userId, password, verifyPassword(password, 0, userId));
+            onUserLockChanged(userId);
         } else {
             throw new RemoteException("Failed to enroll password");
         }
     }
 
+    private void tieProfileLockToParent(int userId, String password) {
+        if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId);
+        byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8);
+        byte[] encryptionResult;
+        byte[] iv;
+        try {
+            KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES);
+            keyGenerator.init(new SecureRandom());
+            SecretKey secretKey = keyGenerator.generateKey();
+
+            java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            keyStore.setEntry(
+                    PROFILE_KEY_NAME_ENCRYPT + userId,
+                    new java.security.KeyStore.SecretKeyEntry(secretKey),
+                    new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT)
+                            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                            .build());
+            keyStore.setEntry(
+                    PROFILE_KEY_NAME_DECRYPT + userId,
+                    new java.security.KeyStore.SecretKeyEntry(secretKey),
+                    new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT)
+                            .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                            .setUserAuthenticationRequired(true)
+                            .setUserAuthenticationValidityDurationSeconds(30)
+                            .build());
+
+            // Key imported, obtain a reference to it.
+            SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
+                    PROFILE_KEY_NAME_ENCRYPT + userId, null);
+            // The original key can now be discarded.
+
+            Cipher cipher = Cipher.getInstance(
+                    KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/"
+                            + KeyProperties.ENCRYPTION_PADDING_NONE);
+            cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey);
+            encryptionResult = cipher.doFinal(randomLockSeed);
+            iv = cipher.getIV();
+        } catch (CertificateException | UnrecoverableKeyException
+                | IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
+                | NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
+            throw new RuntimeException("Failed to encrypt key", e);
+        }
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        try {
+            if (iv.length != PROFILE_KEY_IV_SIZE) {
+                throw new RuntimeException("Invalid iv length: " + iv.length);
+            }
+            outputStream.write(iv);
+            outputStream.write(encryptionResult);
+        } catch (IOException e) {
+            throw new RuntimeException("Failed to concatenate byte arrays", e);
+        }
+        mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
+    }
+
     private byte[] enrollCredential(byte[] enrolledHandle,
             String enrolledCredential, String toEnroll, int userId)
             throws RemoteException {
@@ -820,7 +1073,7 @@
                    @Override
                    public void setCredential(String pattern, String oldPattern, int userId)
                            throws RemoteException {
-                       setLockPattern(pattern, oldPattern, userId);
+                        setLockPatternInternal(pattern, oldPattern, userId);
                    }
 
                    @Override
@@ -838,7 +1091,7 @@
 
        if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK
                && shouldReEnrollBaseZero) {
-           setLockPattern(pattern, patternToVerify, userId);
+            setLockPatternInternal(pattern, patternToVerify, userId);
        }
 
        return response;
@@ -857,6 +1110,37 @@
         return doVerifyPassword(password, true, challenge, userId);
     }
 
+    @Override
+    public VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern,
+            long challenge, int userId) throws RemoteException {
+        checkPasswordReadPermission(userId);
+        if (!isManagedProfileWithUnifiedLock(userId)) {
+            throw new RemoteException("User id must be managed profile with unified lock");
+        }
+        final int parentProfileId = mUserManager.getProfileParent(userId).id;
+        // Unlock parent by using parent's challenge
+        final VerifyCredentialResponse parentResponse = isPattern
+                ? doVerifyPattern(password, true, challenge, parentProfileId)
+                : doVerifyPassword(password, true, challenge, parentProfileId);
+        if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) {
+            // Failed, just return parent's response
+            return parentResponse;
+        }
+
+        try {
+            // Unlock work profile, and work profile with unified lock must use password only
+            return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true,
+                    challenge,
+                    userId);
+        } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException
+                | NoSuchAlgorithmException | NoSuchPaddingException
+                | InvalidAlgorithmParameterException | IllegalBlockSizeException
+                | BadPaddingException | CertificateException | IOException e) {
+            Slog.e(TAG, "Failed to decrypt child profile key", e);
+            throw new RemoteException("Unable to get tied profile token");
+        }
+    }
+
     private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge,
             long challenge, int userId) throws RemoteException {
        checkPasswordReadPermission(userId);
@@ -866,7 +1150,7 @@
                    @Override
                    public void setCredential(String password, String oldPassword, int userId)
                            throws RemoteException {
-                       setLockPassword(password, oldPassword, userId);
+                        setLockPasswordInternal(password, oldPassword, userId);
                    }
 
                    @Override
@@ -947,8 +1231,7 @@
                 " with token length " + response.getPayload().length);
             unlockUser(userId, response.getPayload(), secretFromCredential(credential));
 
-            UserInfo info = UserManager.get(mContext).getUserInfo(userId);
-            if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+            if (isManagedProfileWithSeparatedLock(userId)) {
                 TrustManager trustManager =
                         (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
                 trustManager.setDeviceLockedForUser(userId, false);
@@ -1027,6 +1310,23 @@
         } catch (RemoteException ex) {
             Slog.w(TAG, "unable to clear GK secure user id");
         }
+        if (mUserManager.getUserInfo(userId).isManagedProfile()) {
+            removeKeystoreProfileKey(userId);
+        }
+    }
+
+    private void removeKeystoreProfileKey(int targetUserId) {
+        if (DEBUG) Slog.v(TAG, "Remove keystore profile key for user: " + targetUserId);
+        try {
+            java.security.KeyStore keyStore = java.security.KeyStore.getInstance("AndroidKeyStore");
+            keyStore.load(null);
+            keyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + targetUserId);
+            keyStore.deleteEntry(PROFILE_KEY_NAME_DECRYPT + targetUserId);
+        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
+                | IOException e) {
+            // We have tried our best to remove all keys
+            Slog.e(TAG, "Unable to remove keystore profile key for user:" + targetUserId, e);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index 816c791..d136f1a 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -17,7 +17,6 @@
 package com.android.server;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.widget.LockPatternUtils;
 
 import android.content.ContentValues;
 import android.content.Context;
@@ -30,6 +29,7 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import java.io.File;
 import java.io.IOException;
@@ -44,6 +44,7 @@
 
     private static final String TAG = "LockSettingsStorage";
     private static final String TABLE = "locksettings";
+    private static final boolean DEBUG = false;
 
     private static final String COLUMN_KEY = "name";
     private static final String COLUMN_USERID = "user";
@@ -62,6 +63,7 @@
     private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
     private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
     private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
+    private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
 
     private static final Object DEFAULT = new Object();
 
@@ -70,8 +72,7 @@
     private final Cache mCache = new Cache();
     private final Object mFileWriteLock = new Object();
 
-    private int mStoredCredentialType;
-    private LockPatternUtils mLockPatternUtils;
+    private SparseArray<Integer> mStoredCredentialType;
 
     class CredentialHash {
         static final int TYPE_NONE = -1;
@@ -101,7 +102,7 @@
     public LockSettingsStorage(Context context, Callback callback) {
         mContext = context;
         mOpenHelper = new DatabaseHelper(context, callback);
-        mLockPatternUtils = new LockPatternUtils(context);
+        mStoredCredentialType = new SparseArray<Integer>();
     }
 
     public void writeKeyValue(String key, String value, int userId) {
@@ -182,32 +183,34 @@
     }
 
     public int getStoredCredentialType(int userId) {
-        if (mStoredCredentialType != 0) {
-            return mStoredCredentialType;
+        final Integer cachedStoredCredentialType = mStoredCredentialType.get(userId);
+        if (cachedStoredCredentialType != null) {
+            return cachedStoredCredentialType.intValue();
         }
 
+        int storedCredentialType;
         CredentialHash pattern = readPatternHash(userId);
         if (pattern == null) {
             if (readPasswordHash(userId) != null) {
-                mStoredCredentialType = CredentialHash.TYPE_PASSWORD;
+                storedCredentialType = CredentialHash.TYPE_PASSWORD;
             } else {
-                mStoredCredentialType = CredentialHash.TYPE_NONE;
+                storedCredentialType = CredentialHash.TYPE_NONE;
             }
         } else {
             CredentialHash password = readPasswordHash(userId);
             if (password != null) {
                 // Both will never be GateKeeper
                 if (password.version == CredentialHash.VERSION_GATEKEEPER) {
-                    mStoredCredentialType = CredentialHash.TYPE_PASSWORD;
+                    storedCredentialType = CredentialHash.TYPE_PASSWORD;
                 } else {
-                    mStoredCredentialType = CredentialHash.TYPE_PATTERN;
+                    storedCredentialType = CredentialHash.TYPE_PATTERN;
                 }
             } else {
-                mStoredCredentialType = CredentialHash.TYPE_PATTERN;
+                storedCredentialType = CredentialHash.TYPE_PATTERN;
             }
         }
-
-        return mStoredCredentialType;
+        mStoredCredentialType.put(userId, storedCredentialType);
+        return storedCredentialType;
     }
 
 
@@ -244,6 +247,27 @@
         return null;
     }
 
+    public void removeChildProfileLock(int userId) {
+        if (DEBUG)
+            Slog.e(TAG, "Remove child profile lock for user: " + userId);
+        try {
+            deleteFile(getChildProfileLockFile(userId));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void writeChildProfileLock(int userId, byte[] lock) {
+        writeFile(getChildProfileLockFile(userId), lock);
+    }
+
+    public byte[] readChildProfileLock(int userId) {
+        return readFile(getChildProfileLockFile(userId));
+    }
+
+    public boolean hasChildProfileLock(int userId) {
+        return hasFile(getChildProfileLockFile(userId));
+    }
 
     public boolean hasPassword(int userId) {
         return hasFile(getLockPasswordFilename(userId)) ||
@@ -321,16 +345,19 @@
     }
 
     private void deleteFile(String name) {
-        File f = new File(name);
-        if (f != null) {
-            f.delete();
+        if (DEBUG) Slog.e(TAG, "Delete file " + name);
+        synchronized (mFileWriteLock) {
+            File file = new File(name);
+            if (file.exists()) {
+                file.delete();
+                mCache.putFile(name, null);
+            }
         }
     }
 
     public void writePatternHash(byte[] hash, int userId) {
-        mStoredCredentialType = hash == null
-            ? CredentialHash.TYPE_NONE
-            : CredentialHash.TYPE_PATTERN;
+        mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
+                : CredentialHash.TYPE_PATTERN);
         writeFile(getLockPatternFilename(userId), hash);
         clearPasswordHash(userId);
     }
@@ -340,9 +367,8 @@
     }
 
     public void writePasswordHash(byte[] hash, int userId) {
-        mStoredCredentialType = hash == null
-            ? CredentialHash.TYPE_NONE
-            : CredentialHash.TYPE_PASSWORD;
+        mStoredCredentialType.put(userId, hash == null ? CredentialHash.TYPE_NONE
+                : CredentialHash.TYPE_PASSWORD);
         writeFile(getLockPasswordFilename(userId), hash);
         clearPatternHash(userId);
     }
@@ -375,8 +401,11 @@
         return getLockCredentialFilePathForUser(userId, BASE_ZERO_LOCK_PATTERN_FILE);
     }
 
+    private String getChildProfileLockFile(int userId) {
+        return getLockCredentialFilePathForUser(userId, CHILD_PROFILE_LOCK_FILE);
+    }
+
     private String getLockCredentialFilePathForUser(int userId, String basename) {
-        userId = getUserParentOrSelfId(userId);
         String dataSystemDirectory =
                 android.os.Environment.getDataDirectory().getAbsolutePath() +
                         SYSTEM_DIRECTORY;
@@ -388,23 +417,6 @@
         }
     }
 
-    private int getUserParentOrSelfId(int userId) {
-        // Device supports per user encryption, so lock is applied to the given user.
-        if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
-            return userId;
-        }
-        // Device uses Block Based Encryption, and the parent user's lock is used for the whole
-        // device.
-        if (userId != 0) {
-            final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
-            final UserInfo pi = um.getProfileParent(userId);
-            if (pi != null) {
-                return pi.id;
-            }
-        }
-        return userId;
-    }
-
     public void removeUser(int userId) {
         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
 
@@ -427,6 +439,9 @@
                     mCache.putFile(name, null);
                 }
             }
+        } else {
+            // Manged profile
+            removeChildProfileLock(userId);
         }
 
         try {
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 440d8b7..fd9a94d 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1984,6 +1984,28 @@
                 mHandler.obtainMessage(H_RESET).sendToTarget();
             }
         }
+
+        if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON
+                | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) {
+            final String value;
+            if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) {
+                value = "force_on";
+            } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) {
+                value = "force_off";
+            } else {
+                value = "";
+            }
+
+            final long token = Binder.clearCallingIdentity();
+            try {
+                SystemProperties.set(StorageManager.PROP_SDCARDFS, value);
+
+                // Reset storage to kick new setting into place
+                mHandler.obtainMessage(H_RESET).sendToTarget();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/UiThread.java b/services/core/java/com/android/server/UiThread.java
index 0beb77f..c06afc2 100644
--- a/services/core/java/com/android/server/UiThread.java
+++ b/services/core/java/com/android/server/UiThread.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.os.Handler;
+import android.os.Trace;
 
 /**
  * Shared singleton thread for showing UI.  This is a foreground thread, and in
@@ -35,6 +36,7 @@
         if (sInstance == null) {
             sInstance = new UiThread();
             sInstance.start();
+            sInstance.getLooper().setTraceTag(Trace.TRACE_TAG_ACTIVITY_MANAGER);
             sHandler = new Handler(sInstance.getLooper());
         }
     }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 8a0a62a..810270d 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -2884,7 +2884,6 @@
         }
         if (response == null) throw new IllegalArgumentException("response is null");
         if (account == null) throw new IllegalArgumentException("account is null");
-        if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
         int userId = UserHandle.getCallingUserId();
         long identityToken = clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f6f3295..0f48d21 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1467,6 +1467,7 @@
     static final int NOTIFY_PINNED_ACTIVITY_RESTART_ATTEMPT_LISTENERS_MSG = 65;
     static final int NOTIFY_PINNED_STACK_ANIMATION_ENDED_LISTENERS_MSG = 66;
     static final int NOTIFY_FORCED_RESIZABLE_MSG = 67;
+    static final int NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG = 68;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2056,6 +2057,21 @@
                 }
                 break;
             }
+                case NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG: {
+                    synchronized (ActivityManagerService.this) {
+                        for (int i = mTaskStackListeners.beginBroadcast() - 1; i >= 0; i--) {
+                            try {
+                                // Make a one-way callback to the listener
+                                mTaskStackListeners.getBroadcastItem(i)
+                                        .onActivityDismissingDockedStack();
+                            } catch (RemoteException e){
+                                // Handled by the RemoteCallbackList
+                            }
+                        }
+                        mTaskStackListeners.finishBroadcast();
+                    }
+                    break;
+                }
             case NOTIFY_CLEARTEXT_NETWORK_MSG: {
                 final int uid = msg.arg1;
                 final byte[] firstPacket = (byte[]) msg.obj;
@@ -10590,14 +10606,14 @@
     private boolean requestTargetProviderPermissionsReviewIfNeededLocked(ProviderInfo cpi,
             ProcessRecord r, final int userId) {
         if (getPackageManagerInternalLocked().isPermissionsReviewRequired(
-                cpi.packageName, r.userId)) {
+                cpi.packageName, userId)) {
 
-            final boolean callerForeground = r != null ? r.setSchedGroup
-                    != ProcessList.SCHED_GROUP_BACKGROUND : true;
+            final boolean callerForeground = r == null || r.setSchedGroup
+                    != ProcessList.SCHED_GROUP_BACKGROUND;
 
             // Show a permission review UI only for starting from a foreground app
             if (!callerForeground) {
-                Slog.w(TAG, "u" + r.userId + " Instantiating a provider in package"
+                Slog.w(TAG, "u" + userId + " Instantiating a provider in package"
                         + cpi.packageName + " requires a permissions review");
                 return false;
             }
@@ -10608,7 +10624,7 @@
             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, cpi.packageName);
 
             if (DEBUG_PERMISSIONS_REVIEW) {
-                Slog.i(TAG, "u" + r.userId + " Launching permission review "
+                Slog.i(TAG, "u" + userId + " Launching permission review "
                         + "for package " + cpi.packageName);
             }
 
@@ -13227,10 +13243,11 @@
                     // Merge several logcat streams, and take the last N lines
                     InputStreamReader input = null;
                     try {
-                        java.lang.Process logcat = new ProcessBuilder("/system/bin/logcat",
-                                "-v", "time", "-b", "events", "-b", "system", "-b", "main",
-                                "-b", "crash",
-                                "-t", String.valueOf(lines)).redirectErrorStream(true).start();
+                        java.lang.Process logcat = new ProcessBuilder(
+                                "/system/bin/timeout", "-k", "15s", "10s",
+                                "/system/bin/logcat", "-v", "time", "-b", "events", "-b", "system",
+                                "-b", "main", "-b", "crash", "-t", String.valueOf(lines))
+                                        .redirectErrorStream(true).start();
 
                         try { logcat.getOutputStream().close(); } catch (IOException e) {}
                         try { logcat.getErrorStream().close(); } catch (IOException e) {}
@@ -15640,8 +15657,8 @@
                     pw.println(totalPss - cachedPss);
                 }
             }
-            long lostRAM = memInfo.getTotalSizeKb()
-                    - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+            long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
+                    - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
                     - memInfo.getKernelUsedSizeKb() - memInfo.getZramTotalSizeKb();
             if (!isCompact) {
                 pw.print(" Used RAM: "); pw.print(stringifyKBSize(totalPss - cachedPss
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4ec1f61..2e9947a 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -28,47 +28,78 @@
 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-
 import static android.content.res.Configuration.SCREENLAYOUT_UNDEFINED;
-import static com.android.server.am.ActivityManagerDebugConfig.*;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_APP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKSCREEN;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SAVED_STATE;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SCREENSHOTS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TRANSITION;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_ADD_REMOVE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_APP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONFIGURATION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CONTAINERS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_PAUSE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RELEASE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_RESULTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SAVED_STATE;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SCREENSHOTS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STATES;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_SWITCH;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TASKS;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_TRANSITION;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAVING;
+import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.LOCK_SCREEN_SHOWN;
-import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_REMOVED;
 import static com.android.server.am.ActivityRecord.STARTING_WINDOW_SHOWN;
 import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
 import static com.android.server.am.ActivityStackSupervisor.MOVING;
 import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
-
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.util.ArraySet;
-
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.content.ReferrerIntent;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.server.Watchdog;
-import com.android.server.am.ActivityManagerService.ItemMatcher;
-import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
-import com.android.server.wm.AppTransition;
-import com.android.server.wm.TaskGroup;
-import com.android.server.wm.WindowManagerService;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_CLOSE;
+import static com.android.server.wm.AppTransition.TRANSIT_ACTIVITY_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_NONE;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_CLOSE;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_OPEN_BEHIND;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_BACK;
+import static com.android.server.wm.AppTransition.TRANSIT_TASK_TO_FRONT;
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityController;
 import android.app.ResultInfo;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -83,10 +114,20 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
+import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Slog;
 import android.view.Display;
 
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.content.ReferrerIntent;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService.ItemMatcher;
+import com.android.server.am.ActivityStackSupervisor.ActivityContainer;
+import com.android.server.wm.TaskGroup;
+import com.android.server.wm.WindowManagerService;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -1650,6 +1691,13 @@
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 final ActivityRecord r = activities.get(activityNdx);
                 if (r.finishing) {
+                    // Normally the screenshot will be taken in makeInvisible(). When an activity
+                    // is finishing, we no longer change its visibility, but we still need to take
+                    // the screenshots if startPausingLocked decided it should be taken.
+                    if (r.mUpdateTaskThumbnailWhenHidden) {
+                        r.updateThumbnailLocked(screenshotActivitiesLocked(r), null);
+                        r.mUpdateTaskThumbnailWhenHidden = false;
+                    }
                     continue;
                 }
                 final boolean isTop = r == top;
@@ -2225,11 +2273,11 @@
                         "Prepare close transition: prev=" + prev);
                 if (mNoAnimActivities.contains(prev)) {
                     anim = false;
-                    mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
+                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
                     mWindowManager.prepareAppTransition(prev.task == next.task
-                            ? AppTransition.TRANSIT_ACTIVITY_CLOSE
-                            : AppTransition.TRANSIT_TASK_CLOSE, false);
+                            ? TRANSIT_ACTIVITY_CLOSE
+                            : TRANSIT_TASK_CLOSE, false);
                 }
                 mWindowManager.setAppVisibility(prev.appToken, false);
             } else {
@@ -2237,22 +2285,22 @@
                         "Prepare open transition: prev=" + prev);
                 if (mNoAnimActivities.contains(next)) {
                     anim = false;
-                    mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
+                    mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
                 } else {
                     mWindowManager.prepareAppTransition(prev.task == next.task
-                            ? AppTransition.TRANSIT_ACTIVITY_OPEN
+                            ? TRANSIT_ACTIVITY_OPEN
                             : next.mLaunchTaskBehind
-                                    ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
-                                    : AppTransition.TRANSIT_TASK_OPEN, false);
+                                    ? TRANSIT_TASK_OPEN_BEHIND
+                                    : TRANSIT_TASK_OPEN, false);
                 }
             }
         } else {
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare open transition: no previous");
             if (mNoAnimActivities.contains(next)) {
                 anim = false;
-                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
             } else {
-                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_ACTIVITY_OPEN, false);
+                mWindowManager.prepareAppTransition(TRANSIT_ACTIVITY_OPEN, false);
             }
         }
 
@@ -2595,14 +2643,14 @@
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, keepCurTransition);
+                mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);
                 mNoAnimActivities.add(r);
             } else {
                 mWindowManager.prepareAppTransition(newTask
                         ? r.mLaunchTaskBehind
-                                ? AppTransition.TRANSIT_TASK_OPEN_BEHIND
-                                : AppTransition.TRANSIT_TASK_OPEN
-                        : AppTransition.TRANSIT_ACTIVITY_OPEN, keepCurTransition);
+                                ? TRANSIT_TASK_OPEN_BEHIND
+                                : TRANSIT_TASK_OPEN
+                        : TRANSIT_ACTIVITY_OPEN, keepCurTransition);
                 mNoAnimActivities.remove(r);
             }
             addConfigOverride(r, task);
@@ -3354,13 +3402,13 @@
 
         finishActivityResultsLocked(r, resultCode, resultData);
 
+        final boolean endTask = index <= 0;
+        final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
         if (mResumedActivity == r) {
-            boolean endTask = index <= 0;
+
             if (DEBUG_VISIBILITY || DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare close transition: finishing " + r);
-            mWindowManager.prepareAppTransition(endTask
-                    ? AppTransition.TRANSIT_TASK_CLOSE
-                    : AppTransition.TRANSIT_ACTIVITY_CLOSE, false);
+            mWindowManager.prepareAppTransition(transit, false);
 
             // Tell window manager to prepare for this one to be removed.
             mWindowManager.setAppVisibility(r.appToken, false);
@@ -3380,7 +3428,9 @@
             // it is done pausing; else we can just directly finish it here.
             if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + r);
             if (r.visible) {
+                mWindowManager.prepareAppTransition(transit, false);
                 mWindowManager.setAppVisibility(r.appToken, false);
+                mWindowManager.executeAppTransition();
             }
             return finishCurrentActivityLocked(r, FINISH_AFTER_PAUSE, oomAdj) == null;
         } else {
@@ -4122,7 +4172,7 @@
             if (noAnimation) {
                 ActivityOptions.abort(options);
             } else {
-                updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
+                updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
             }
             return;
         }
@@ -4152,13 +4202,13 @@
 
         if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
         if (noAnimation) {
-            mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
+            mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
             if (r != null) {
                 mNoAnimActivities.add(r);
             }
             ActivityOptions.abort(options);
         } else {
-            updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
+            updateTransitLocked(TRANSIT_TASK_TO_FRONT, options);
         }
 
         mStackSupervisor.resumeFocusedStackTopActivityLocked();
@@ -4261,7 +4311,7 @@
             }
         }
 
-        mWindowManager.prepareAppTransition(AppTransition.TRANSIT_TASK_TO_BACK, false);
+        mWindowManager.prepareAppTransition(TRANSIT_TASK_TO_BACK, false);
         mWindowManager.moveTaskToBottom(taskId);
 
         if (VALIDATE_TOKENS) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index f659bde..8395083 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -152,6 +152,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ANIMATE;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityManagerService.NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG;
 import static com.android.server.am.ActivityManagerService.NOTIFY_FORCED_RESIZABLE_MSG;
 import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
@@ -1810,7 +1811,7 @@
         if (DEBUG_STACK) Slog.d(TAG_STACK,
                 "findTaskToMoveToFront: moved to front of stack=" + task.stack);
 
-        showNonResizeableDockToastIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
+        handleNonResizableTaskIfNeeded(task, INVALID_STACK_ID, task.stack.mStackId);
     }
 
     boolean canUseActivityOptionsLaunchBounds(ActivityOptions options, int launchStackId) {
@@ -2043,8 +2044,10 @@
             resizeStackUncheckedLocked(stack, dockedBounds, tempDockedTaskBounds,
                     tempDockedTaskInsetBounds);
 
-            if (stack.mFullscreen) {
-                // The dock stack went fullscreen which is kinda like dismissing it.
+            // TODO: Checking for isAttached might not be needed as if the user passes in null
+            // dockedBounds then they want the docked stack to be dismissed.
+            if (stack.mFullscreen || (dockedBounds == null && !stack.isAttached())) {
+                // The dock stack either was dismissed or went fullscreen, which is kinda the same.
                 // In this case we make all other static stacks fullscreen and move all
                 // docked stack tasks to the fullscreen stack.
                 for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
@@ -2069,18 +2072,13 @@
                 // static stacks need to be adjusted so they don't overlap with the docked stack.
                 // We get the bounds to use from window manager which has been adjusted for any
                 // screen controls and is also the same for all stacks.
-                if (dockedBounds != null) {
-                    mWindowManager.getStackDockedModeBounds(
-                            HOME_STACK_ID, tempRect, true /* ignoreVisibility */);
-                }
+                mWindowManager.getStackDockedModeBounds(
+                        HOME_STACK_ID, tempRect, true /* ignoreVisibility */);
                 for (int i = FIRST_STATIC_STACK_ID; i <= LAST_STATIC_STACK_ID; i++) {
-                    if (StackId.isResizeableByDockedStack(i)) {
-                        ActivityStack otherStack = getStack(i);
-                        if (otherStack != null) {
-                            resizeStackLocked(i, dockedBounds != null ? tempRect : null,
-                                    tempOtherTaskBounds, tempOtherTaskInsetBounds, preserveWindows,
-                                    true /* allowResizeInDockedMode */);
-                        }
+                    if (StackId.isResizeableByDockedStack(i) && getStack(i) != null) {
+                        resizeStackLocked(i, tempRect, tempOtherTaskBounds,
+                                tempOtherTaskInsetBounds, preserveWindows,
+                                true /* allowResizeInDockedMode */);
                     }
                 }
             }
@@ -2395,7 +2393,7 @@
             resumeFocusedStackTopActivityLocked();
         }
 
-        showNonResizeableDockToastIfNeeded(task, preferredLaunchStackId, stackId);
+        handleNonResizableTaskIfNeeded(task, preferredLaunchStackId, stackId);
 
         return (preferredLaunchStackId == stackId);
     }
@@ -3366,15 +3364,19 @@
         }
     }
 
-    void showNonResizeableDockToastIfNeeded(
+    void handleNonResizableTaskIfNeeded(
             TaskRecord task, int preferredStackId, int actualStackId) {
-        if (!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID) {
+        if ((!isStackDockedInEffect(actualStackId) && preferredStackId != DOCKED_STACK_ID)
+                || task.isHomeTask()) {
             return;
         }
 
         if (!task.canGoInDockedStack()) {
             // Display a warning toast that we tried to put a non-dockable task in the docked stack.
-            mWindowManager.scheduleShowNonResizeableDockToast(task.taskId);
+            mService.mHandler.sendEmptyMessage(NOTIFY_ACTIVITY_DISMISSING_DOCKED_STACK_MSG);
+
+            // Dismiss docked stack.
+            mService.moveTasksToFullscreenStack(DOCKED_STACK_ID, false);
         } else if (task.mResizeMode == RESIZE_MODE_FORCE_RESIZEABLE) {
             String packageName = task.getTopActivity() != null
                     ? task.getTopActivity().appInfo.packageName : null;
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 76dfd01..785dd47 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -119,7 +119,13 @@
         if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
             return false;
         }
-        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId);
+        IIntentSender target = mService.getIntentSenderLocked(
+                INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0,
+                new Intent[] {mIntent}, new String[] {mResolvedType},
+                FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null);
+
+        mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId,
+                new IntentSender(target));
         mCallingPid = mRealCallingPid;
         mCallingUid = mRealCallingUid;
         mResolvedType = null;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3bbc452..6fba8c8 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -986,8 +986,12 @@
             }
             top.deliverNewIntentLocked(
                     mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage);
-            mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task,
-                    preferredLaunchStackId, topStack.mStackId);
+
+            // Don't use mStartActivity.task to show the toast. We're not starting a new activity
+            // but reusing 'top'. Fields in mStartActivity may not be fully initialized.
+            mSupervisor.handleNonResizableTaskIfNeeded(
+                    top.task, preferredLaunchStackId, topStack.mStackId);
+
             return START_DELIVERED_TO_TOP;
         }
 
@@ -1074,7 +1078,7 @@
         }
         mSupervisor.updateUserStackLocked(mStartActivity.userId, mTargetStack);
 
-        mSupervisor.showNonResizeableDockToastIfNeeded(
+        mSupervisor.handleNonResizableTaskIfNeeded(
                 mStartActivity.task, preferredLaunchStackId, mTargetStack.mStackId);
 
         return START_SUCCESS;
@@ -1382,6 +1386,9 @@
             mTargetStack.moveToFront("intentActivityFound");
         }
 
+        mSupervisor.handleNonResizableTaskIfNeeded(intentActivity.task, INVALID_STACK_ID,
+                mTargetStack.mStackId);
+
         // If the caller has requested that the target task be reset, then do so.
         if ((mLaunchFlags & FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
             return mTargetStack.resetTaskIfNeededLocked(intentActivity, mStartActivity);
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 6cd7561..68bd2fd 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -724,9 +724,7 @@
             final boolean crashSilenced = mAppsNotReportingCrashes != null &&
                     mAppsNotReportingCrashes.contains(proc.info.packageName);
             if (mService.canShowErrorDialogs() && !crashSilenced) {
-                Dialog d = new AppErrorDialog(mContext, mService, data);
-                d.show();
-                proc.crashDialog = d;
+                proc.crashDialog = new AppErrorDialog(mContext, mService, data);
             } else {
                 // The device is asleep, so just pretend that the user
                 // saw a crash dialog and hit "force quit".
@@ -735,6 +733,10 @@
                 }
             }
         }
+        // If we've created a crash dialog, show it without the lock held
+        if(data.proc.crashDialog != null) {
+            data.proc.crashDialog.show();
+        }
     }
 
     void stopReportingCrashesLocked(ProcessRecord proc) {
@@ -924,6 +926,7 @@
     }
 
     void handleShowAnrUi(Message msg) {
+        Dialog d = null;
         synchronized (mService) {
             HashMap<String, Object> data = (HashMap<String, Object>) msg.obj;
             ProcessRecord proc = (ProcessRecord)data.get("app");
@@ -944,10 +947,9 @@
                     null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
 
             if (mService.canShowErrorDialogs()) {
-                Dialog d = new AppNotRespondingDialog(mService,
+                d = new AppNotRespondingDialog(mService,
                         mContext, proc, (ActivityRecord)data.get("activity"),
                         msg.arg1 != 0);
-                d.show();
                 proc.anrDialog = d;
             } else {
                 MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_ANR,
@@ -956,6 +958,10 @@
                 mService.killAppAtUsersRequest(proc, null);
             }
         }
+        // If we've created a crash dialog, show it without the lock held
+        if (d != null) {
+            d.show();
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index aef454e..bea26c7 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -237,7 +237,22 @@
                         AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
             }
 
-            maybeUnlockUser(userId);
+            // We need to delay unlocking managed profiles until the parent user
+            // is also unlocked.
+            if (getUserManager().isManagedProfile(userId)) {
+                final UserInfo parent = getUserManager().getProfileParent(userId);
+                if (parent != null
+                        && isUserRunningLocked(parent.id, ActivityManager.FLAG_AND_UNLOCKED)) {
+                    Slog.d(TAG, "User " + userId + " (parent " + parent.id
+                            + "): attempting unlock because parent is unlocked");
+                    maybeUnlockUser(userId);
+                } else {
+                    Slog.d(TAG, "User " + userId + " (parent " + parent.id
+                            + "): delaying unlock because parent is locked");
+                }
+            } else {
+                maybeUnlockUser(userId);
+            }
         }
     }
 
@@ -260,6 +275,9 @@
                 mUserManager.onBeforeUnlockUser(userId);
                 progress.setProgress(20);
 
+                // Dispatch unlocked to system services
+                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
+
                 // Send PRE_BOOT broadcasts if fingerprint changed
                 final UserInfo info = getUserInfo(userId);
                 if (!Objects.equals(info.lastLoggedInFingerprint, Build.FINGERPRINT)) {
@@ -302,9 +320,6 @@
                 // Remember that we logged in
                 mUserManager.onUserLoggedIn(userId);
 
-                // Dispatch unlocked to system services
-                mHandler.sendMessage(mHandler.obtainMessage(SYSTEM_USER_UNLOCK_MSG, userId, 0));
-
                 // Dispatch unlocked to external apps
                 final Intent unlockedIntent = new Intent(Intent.ACTION_USER_UNLOCKED);
                 unlockedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
@@ -903,6 +918,18 @@
         synchronized (mService) {
             final UserState uss = mStartedUsers.get(userId);
             finishUserUnlocking(uss, progress);
+
+            // We just unlocked a user, so let's now attempt to unlock any
+            // managed profiles under that user.
+            for (int i = 0; i < mStartedUsers.size(); i++) {
+                final int testUserId = mStartedUsers.keyAt(i);
+                final UserInfo parent = getUserManager().getProfileParent(testUserId);
+                if (parent != null && parent.id == userId && testUserId != userId) {
+                    Slog.d(TAG, "User " + testUserId + " (parent " + parent.id
+                            + "): attempting unlock because parent was just unlocked");
+                    maybeUnlockUser(testUserId);
+                }
+            }
         }
 
         return true;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f471af6..58db985 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3954,25 +3954,16 @@
 
         public void applyAllVolumes() {
             synchronized (VolumeStreamState.class) {
-                // apply default volume first: by convention this will reset all
-                // devices volumes in audio policy manager to the supplied value
+                // apply device specific volumes first
                 int index;
-                if (mIsMuted) {
-                    index = 0;
-                } else {
-                    index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
-                }
-                AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
-                // then apply device specific volumes
                 for (int i = 0; i < mIndexMap.size(); i++) {
-                    int device = mIndexMap.keyAt(i);
+                    final int device = mIndexMap.keyAt(i);
                     if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                         if (mIsMuted) {
                             index = 0;
                         } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                                 mAvrcpAbsVolSupported)
-                                    || ((device & mFullVolumeDevices) != 0))
-                        {
+                                    || ((device & mFullVolumeDevices) != 0)) {
                             index = (mIndexMax + 5)/10;
                         } else {
                             index = (mIndexMap.valueAt(i) + 5)/10;
@@ -3980,6 +3971,15 @@
                         AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
                     }
                 }
+                // apply default volume last: by convention , default device volume will be used
+                // by audio policy manager if no explicit volume is present for a given device type
+                if (mIsMuted) {
+                    index = 0;
+                } else {
+                    index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
+                }
+                AudioSystem.setStreamVolumeIndex(
+                        mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
             }
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 3b0b79a..a111bf9 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -264,7 +264,12 @@
             return true;
         }
 
-        // Check if the caller is authorized.
+        // Stop an existing always-on VPN from being dethroned by other apps.
+        if (getAlwaysOnPackage() != null) {
+            return false;
+        }
+
+        // Check that the caller is authorized.
         enforceControlPermission();
 
         prepareInternal(newPackage);
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index e9d9628..6e7ea99 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -73,7 +73,8 @@
  * {@hide}
  */
 public final class ContentService extends IContentService.Stub {
-    private static final String TAG = "ContentService";
+    static final String TAG = "ContentService";
+    static final boolean DEBUG = false;
 
     public static class Lifecycle extends SystemService {
         private ContentService mContentService;
@@ -339,12 +340,10 @@
      */
     @Override
     public void notifyChange(Uri uri, IContentObserver observer,
-                             boolean observerWantsSelfNotifications, boolean syncToNetwork,
+                             boolean observerWantsSelfNotifications, int flags,
                              int userHandle) {
-        if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "Notifying update of " + uri + " for user " + userHandle
-                    + " from observer " + observer + ", syncToNetwork " + syncToNetwork);
-        }
+        if (DEBUG) Slog.d(TAG, "Notifying update of " + uri + " for user " + userHandle
+                + " from observer " + observer + ", flags " + Integer.toHexString(flags));
 
         final int uid = Binder.getCallingUid();
         final int pid = Binder.getCallingPid();
@@ -373,16 +372,15 @@
             ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
             synchronized (mRootNode) {
                 mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
-                        userHandle, calls);
+                        flags, userHandle, calls);
             }
             final int numCalls = calls.size();
             for (int i=0; i<numCalls; i++) {
                 ObserverCall oc = calls.get(i);
                 try {
                     oc.mObserver.onChange(oc.mSelfChange, uri, userHandle);
-                    if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                        Log.v(TAG, "Notified " + oc.mObserver + " of " + "update at " + uri);
-                    }
+                    if (DEBUG) Slog.d(TAG, "Notified " + oc.mObserver + " of " + "update at "
+                            + uri);
                 } catch (RemoteException ex) {
                     synchronized (mRootNode) {
                         Log.w(TAG, "Found dead observer, removing");
@@ -401,7 +399,7 @@
                     }
                 }
             }
-            if (syncToNetwork) {
+            if ((flags&ContentResolver.NOTIFY_SYNC_TO_NETWORK) != 0) {
                 SyncManager syncManager = getSyncManager();
                 if (syncManager != null) {
                     syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
@@ -420,7 +418,8 @@
 
     public void notifyChange(Uri uri, IContentObserver observer,
                              boolean observerWantsSelfNotifications, boolean syncToNetwork) {
-        notifyChange(uri, observer, observerWantsSelfNotifications, syncToNetwork,
+        notifyChange(uri, observer, observerWantsSelfNotifications,
+                syncToNetwork ? ContentResolver.NOTIFY_SYNC_TO_NETWORK : 0,
                 UserHandle.getCallingUserId());
     }
 
@@ -1064,14 +1063,14 @@
             for (int i = 0; i < packageCache.size();) {
                 final Pair<String, Uri> key = packageCache.keyAt(i);
                 if (key.second != null && key.second.toString().startsWith(uri.toString())) {
-                    Slog.d(TAG, "Invalidating cache for key " + key);
+                    if (DEBUG) Slog.d(TAG, "Invalidating cache for key " + key);
                     packageCache.removeAt(i);
                 } else {
                     i++;
                 }
             }
         } else {
-            Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
+            if (DEBUG) Slog.d(TAG, "Invalidating cache for package " + providerPackageName);
             packageCache.clear();
         }
     }
@@ -1310,8 +1309,8 @@
         }
 
         private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
-                                              boolean observerWantsSelfNotifications, int targetUserHandle,
-                                              ArrayList<ObserverCall> calls) {
+                                              boolean observerWantsSelfNotifications, int flags,
+                                              int targetUserHandle, ArrayList<ObserverCall> calls) {
             int N = mObservers.size();
             IBinder observerBinder = observer == null ? null : observer.asBinder();
             for (int i = 0; i < N; i++) {
@@ -1329,9 +1328,29 @@
                         || entry.userHandle == UserHandle.USER_ALL
                         || targetUserHandle == entry.userHandle) {
                     // Make sure the observer is interested in the notification
-                    if (leaf || (!leaf && entry.notifyForDescendants)) {
-                        calls.add(new ObserverCall(this, entry.observer, selfChange));
+                    if (leaf) {
+                        // If we are at the leaf: we always report, unless the sender has asked
+                        // to skip observers that are notifying for descendants (since they will
+                        // be sending another more specific URI for them).
+                        if ((flags&ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS) != 0
+                                && entry.notifyForDescendants) {
+                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
+                                    + ": skip notify for descendants");
+                            continue;
+                        }
+                    } else {
+                        // If we are not at the leaf: we report if the observer says it wants
+                        // to be notified for all descendants.
+                        if (!entry.notifyForDescendants) {
+                            if (DEBUG) Slog.d(TAG, "Skipping " + entry.observer
+                                    + ": not monitor descendants");
+                            continue;
+                        }
                     }
+                    if (DEBUG) Slog.d(TAG, "Reporting to " + entry.observer + ": leaf=" + leaf
+                            + " flags=" + Integer.toHexString(flags)
+                            + " desc=" + entry.notifyForDescendants);
+                    calls.add(new ObserverCall(this, entry.observer, selfChange));
                 }
             }
         }
@@ -1340,19 +1359,22 @@
          * targetUserHandle is either a hard user handle or is USER_ALL
          */
         public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
-                                           boolean observerWantsSelfNotifications, int targetUserHandle,
-                                           ArrayList<ObserverCall> calls) {
+                                           boolean observerWantsSelfNotifications, int flags,
+                                           int targetUserHandle, ArrayList<ObserverCall> calls) {
             String segment = null;
             int segmentCount = countUriSegments(uri);
             if (index >= segmentCount) {
                 // This is the leaf node, notify all observers
+                if (DEBUG) Slog.d(TAG, "Collecting leaf observers @ #" + index + ", node " + mName);
                 collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
-                        targetUserHandle, calls);
+                        flags, targetUserHandle, calls);
             } else if (index < segmentCount){
                 segment = getUriSegment(uri, index);
+                if (DEBUG) Slog.d(TAG, "Collecting non-leaf observers @ #" + index + " / "
+                        + segment);
                 // Notify any observers at this level who are interested in descendants
                 collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
-                        targetUserHandle, calls);
+                        flags, targetUserHandle, calls);
             }
 
             int N = mChildren.size();
@@ -1360,8 +1382,8 @@
                 ObserverNode node = mChildren.get(i);
                 if (segment == null || node.mName.equals(segment)) {
                     // We found the child,
-                    node.collectObserversLocked(uri, index + 1,
-                            observer, observerWantsSelfNotifications, targetUserHandle, calls);
+                    node.collectObserversLocked(uri, index + 1, observer,
+                            observerWantsSelfNotifications, flags, targetUserHandle, calls);
                     if (segment != null) {
                         break;
                     }
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e5342ce..db41a54 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -1402,12 +1402,24 @@
         }
     }
 
+    private void restoreLostPeriodicSyncsIfNeeded(int userId) {
+        List<SyncOperation> periodicSyncs = new ArrayList<SyncOperation>();
+        for (SyncOperation sync : getAllPendingSyncs()) {
+            if (sync.isPeriodic && sync.target.userId == userId) {
+                periodicSyncs.add(sync);
+            }
+        }
+        mSyncStorageEngine.restorePeriodicSyncsIfNeededForUser(userId, periodicSyncs);
+    }
+
     private void onUserUnlocked(int userId) {
         // Make sure that accounts we're about to use are valid.
         AccountManagerService.getSingleton().validateAccounts(userId);
 
         mSyncAdapters.invalidateCache(userId);
 
+        restoreLostPeriodicSyncsIfNeeded(userId);
+
         EndPoint target = new EndPoint(null, null, userId);
         updateRunningAccounts(target);
 
@@ -2578,9 +2590,11 @@
                 }
             }
 
+            // Cancel all jobs from non-existent accounts.
+            AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
             List<SyncOperation> ops = getAllPendingSyncs();
             for (SyncOperation op: ops) {
-                if (!containsAccountAndUser(accounts, op.target.account, op.target.userId)) {
+                if (!containsAccountAndUser(allAccounts, op.target.account, op.target.userId)) {
                     getJobScheduler().cancel(op.jobId);
                 }
             }
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index bc3fc6a..fb23265 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -826,6 +826,35 @@
         return true;
     }
 
+    /**
+     * STOPSHIP This is a temporary workaround and should be removed before shipping: b/28052438
+     */
+    void restorePeriodicSyncsIfNeededForUser(int userHandle, List<SyncOperation> periodicSyncs) {
+        if (mPeriodicSyncAddedListener == null) {
+            return;
+        }
+        synchronized (mAuthorities) {
+            for (int i = 0; i < mAuthorities.size(); i++) {
+                AuthorityInfo authority = mAuthorities.valueAt(i);
+                if (authority.target.userId == userHandle && authority.enabled) {
+                    boolean periodicSyncAlreadyExists = false;
+                    for (SyncOperation sync : periodicSyncs) {
+                        if (authority.target.matchesSpec(sync.target)) {
+                            periodicSyncAlreadyExists = true;
+                            break;
+                        }
+                    }
+                    // The periodic sync must have been lost due to previous bug.
+                    if (!periodicSyncAlreadyExists) {
+                        mPeriodicSyncAddedListener.onPeriodicSyncAdded(authority.target,
+                                new Bundle(), DEFAULT_POLL_FREQUENCY_SECONDS,
+                                calculateDefaultFlexTime(DEFAULT_POLL_FREQUENCY_SECONDS));
+                    }
+                }
+            }
+        }
+    }
+
     public void setMasterSyncAutomatically(boolean flag, int userId) {
         synchronized (mAuthorities) {
             Boolean auto = mMasterSyncAutomatically.get(userId);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 7b134ca..3d8bf51 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -107,6 +107,7 @@
     private static final int FINGERPRINT_ACQUIRED_GOOD = 0;
     private final String mKeyguardPackage;
     private int mCurrentUserId = UserHandle.USER_CURRENT;
+    private int mUserIdForRemove = UserHandle.USER_NULL;
 
     Handler mHandler = new Handler() {
         @Override
@@ -205,10 +206,12 @@
     protected void handleRemoved(long deviceId, int fingerId, int groupId) {
         final ClientMonitor client = mRemoveClient;
         if (fingerId != 0) {
-            removeTemplateForUser(mRemoveClient, fingerId);
+            removeTemplateForUser(mUserIdForRemove, fingerId);
+        } else {
+            mUserIdForRemove = UserHandle.USER_NULL;
         }
         if (client != null && client.sendRemoved(fingerId, groupId)) {
-            removeClient(mRemoveClient);
+            removeClient(client);
         }
     }
 
@@ -325,8 +328,8 @@
         return false;
     }
 
-    private void removeTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
-        mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, clientMonitor.userId);
+    private void removeTemplateForUser(int userId, int fingerId) {
+        mFingerprintUtils.removeFingerprintIdForUser(mContext, fingerId, userId);
     }
 
     private void addTemplateForUser(ClientMonitor clientMonitor, int fingerId) {
@@ -488,6 +491,7 @@
 
         stopPendingOperations(true);
         mRemoveClient = new ClientMonitor(token, receiver, userId, restricted, token.toString());
+        mUserIdForRemove = mCurrentUserId;
         // The fingerprint template ids will be removed when we get confirmation from the HAL
         try {
             final int result = daemon.remove(fingerId, userId);
@@ -943,10 +947,6 @@
                 if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
                 return;
             }
-
-            // Group ID is arbitrarily set to parent profile user ID. It just represents
-            // the default fingerprints for the user.
-            final int effectiveGroupId = getEffectiveUserId(groupId);
             final int realUserId = Binder.getCallingUid();
 
             final boolean restricted = isRestricted();
@@ -954,7 +954,7 @@
                 @Override
                 public void run() {
                     MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
-                    startAuthentication(token, opId, realUserId, effectiveGroupId, receiver,
+                    startAuthentication(token, opId, realUserId, groupId, receiver,
                             flags, restricted, opPackageName);
                 }
             });
@@ -989,14 +989,10 @@
                 final IFingerprintServiceReceiver receiver) {
             checkPermission(MANAGE_FINGERPRINT); // TODO: Maybe have another permission
             final boolean restricted = isRestricted();
-
-            // Group ID is arbitrarily set to parent profile user ID. It just represents
-            // the default fingerprints for the user.
-            final int effectiveGroupId = getEffectiveUserId(groupId);
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    startRemove(token, fingerId, effectiveGroupId, receiver, restricted);
+                    startRemove(token, fingerId, groupId, receiver, restricted);
                 }
             });
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e73beaa..c7c765bb 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -200,6 +200,7 @@
     private static native int nativeInjectInputEvent(long ptr, InputEvent event, int displayId,
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis,
             int policyFlags);
+    private static native void nativeToggleCapsLock(long ptr, int deviceId);
     private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
@@ -2279,5 +2280,10 @@
             mHandler.obtainMessage(MSG_INPUT_METHOD_SUBTYPE_CHANGED, userId, 0, someArgs)
                     .sendToTarget();
         }
+
+        @Override
+        public void toggleCapsLock(int deviceId) {
+            nativeToggleCapsLock(mPtr, deviceId);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index fa8620f..b235002 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -304,7 +304,7 @@
 
             toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
             if (toCancel != null) {
-                cancelJobImpl(toCancel);
+                cancelJobImpl(toCancel, jobStatus);
             }
             startTrackingJob(jobStatus, toCancel);
         }
@@ -331,7 +331,7 @@
         }
         for (int i=0; i<jobsForUser.size(); i++) {
             JobStatus toRemove = jobsForUser.get(i);
-            cancelJobImpl(toRemove);
+            cancelJobImpl(toRemove, null);
         }
     }
 
@@ -360,7 +360,7 @@
                 } catch (RemoteException e) {
                 }
             }
-            cancelJobImpl(toRemove);
+            cancelJobImpl(toRemove, null);
         }
     }
 
@@ -377,13 +377,13 @@
             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
         }
         if (toCancel != null) {
-            cancelJobImpl(toCancel);
+            cancelJobImpl(toCancel, null);
         }
     }
 
-    private void cancelJobImpl(JobStatus cancelled) {
+    private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
-        stopTrackingJob(cancelled, true /* writeBack */);
+        stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
         synchronized (mLock) {
             // Remove from pending queue.
             mPendingJobs.remove(cancelled);
@@ -549,7 +549,7 @@
                 for (int i = 0; i < mControllers.size(); i++) {
                     StateController controller = mControllers.get(i);
                     if (update) {
-                        controller.maybeStopTrackingJobLocked(jobStatus, true);
+                        controller.maybeStopTrackingJobLocked(jobStatus, null, true);
                     }
                     controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
                 }
@@ -561,14 +561,15 @@
      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
      * object removed.
      */
-    private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
+    private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
+            boolean writeBack) {
         synchronized (mLock) {
             // Remove from store as well as controllers.
             final boolean removed = mJobs.remove(jobStatus, writeBack);
             if (removed && mReadyToRock) {
                 for (int i=0; i<mControllers.size(); i++) {
                     StateController controller = mControllers.get(i);
-                    controller.maybeStopTrackingJobLocked(jobStatus, false);
+                    controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
                 }
             }
             return removed;
@@ -696,7 +697,7 @@
         }
         // Do not write back immediately if this is a periodic job. The job may get lost if system
         // shuts down before it is added back.
-        if (!stopTrackingJob(jobStatus, !jobStatus.getJob().isPeriodic())) {
+        if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
             if (DEBUG) {
                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
             }
@@ -780,7 +781,7 @@
                     }
                     break;
                 case MSG_STOP_JOB:
-                    cancelJobImpl((JobStatus)message.obj);
+                    cancelJobImpl((JobStatus)message.obj, null);
                     break;
             }
             maybeRunPendingJobsH();
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 2114fc3..d8490d4 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -77,7 +77,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
         mTrackedTasks.remove(jobStatus);
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index ac9f425..0772364 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -87,7 +87,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
         if (taskStatus.hasChargingConstraint()) {
             mTrackedTasks.remove(taskStatus);
         }
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 6ef425a..be9d800 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -77,8 +77,10 @@
         if (cs != null) {
             if (cs.getActiveNetworkInfo() != null) {
                 mNetworkConnected = cs.getActiveNetworkInfo().isConnected();
+                mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
+            } else {
+                mNetworkConnected = mNetworkUnmetered = false;
             }
-            mNetworkUnmetered = mNetworkConnected && !cs.isActiveNetworkMetered();
         }
     }
 
@@ -92,7 +94,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
         if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
             mTrackedJobs.remove(jobStatus);
         }
diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
index c5cf30f..b2f1958 100644
--- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java
@@ -84,17 +84,8 @@
             boolean havePendingUris = false;
             // If there is a previous job associated with the new job, propagate over
             // any pending content URI trigger reports.
-            if (lastJob != null && lastJob.contentObserverJobInstance != null
-                    && lastJob.contentObserverJobInstance
-                    != taskStatus.contentObserverJobInstance
-                    && lastJob.contentObserverJobInstance.mChangedAuthorities != null) {
+            if (taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
                 havePendingUris = true;
-                taskStatus.contentObserverJobInstance.mChangedAuthorities
-                        = lastJob.contentObserverJobInstance.mChangedAuthorities;
-                taskStatus.contentObserverJobInstance.mChangedUris
-                        = lastJob.contentObserverJobInstance.mChangedUris;
-                lastJob.contentObserverJobInstance.mChangedAuthorities = null;
-                lastJob.contentObserverJobInstance.mChangedUris = null;
             }
             // If we have previously reported changed authorities/uris, then we failed
             // to complete the job with them so will re-record them to report again.
@@ -138,15 +129,34 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
+            boolean forUpdate) {
         if (taskStatus.hasContentTriggerConstraint()) {
-            if (!forUpdate) {
-                // We won't do this reset if being called for an update, because
-                // we know it will be immediately followed by maybeStartTrackingJobLocked...
-                // and we don't want to lose any content changes in-between.
-                if (taskStatus.contentObserverJobInstance != null) {
-                    taskStatus.contentObserverJobInstance.detach();
-                    taskStatus.contentObserverJobInstance = null;
+            if (taskStatus.contentObserverJobInstance != null) {
+                if (incomingJob != null && taskStatus.contentObserverJobInstance != null
+                        && taskStatus.contentObserverJobInstance.mChangedAuthorities != null) {
+                    // We are stopping this job, but it is going to be replaced by this given
+                    // incoming job.  We want to propagate our state over to it, so we don't
+                    // lose any content changes that had happend since the last one started.
+                    // If there is a previous job associated with the new job, propagate over
+                    // any pending content URI trigger reports.
+                    if (incomingJob.contentObserverJobInstance == null) {
+                        incomingJob.contentObserverJobInstance = new JobInstance(incomingJob);
+                    }
+                    incomingJob.contentObserverJobInstance.mChangedAuthorities
+                            = taskStatus.contentObserverJobInstance.mChangedAuthorities;
+                    incomingJob.contentObserverJobInstance.mChangedUris
+                            = taskStatus.contentObserverJobInstance.mChangedUris;
+                    taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
+                    taskStatus.contentObserverJobInstance.mChangedUris = null;
+                } else {
+                    // We won't do this reset if being called for an update, because
+                    // we know it will be immediately followed by maybeStartTrackingJobLocked...
+                    // and we don't want to lose any content changes in-between.
+                    if (taskStatus.contentObserverJobInstance != null) {
+                        taskStatus.contentObserverJobInstance.detach();
+                        taskStatus.contentObserverJobInstance = null;
+                    }
                 }
             }
             mTrackedTasks.remove(taskStatus);
diff --git a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
index d2ef6a2..64887e8 100644
--- a/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/DeviceIdleJobsController.java
@@ -166,7 +166,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
         mTrackedTasks.remove(jobStatus);
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index d9eb45c..50aa882 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -75,7 +75,7 @@
     }
 
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
         mTrackedTasks.remove(taskStatus);
     }
 
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index ac7f4c3..0139039 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -56,7 +56,8 @@
     /**
      * Remove task - this will happen if the task is cancelled, completed, etc.
      */
-    public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate);
+    public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+            boolean forUpdate);
     /**
      * Called when a new job is being created to reschedule an old failed job.
      */
diff --git a/services/core/java/com/android/server/job/controllers/TimeController.java b/services/core/java/com/android/server/job/controllers/TimeController.java
index 3543249..ab6768e 100644
--- a/services/core/java/com/android/server/job/controllers/TimeController.java
+++ b/services/core/java/com/android/server/job/controllers/TimeController.java
@@ -74,7 +74,7 @@
     @Override
     public void maybeStartTrackingJobLocked(JobStatus job, JobStatus lastJob) {
         if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
-            maybeStopTrackingJobLocked(job, false);
+            maybeStopTrackingJobLocked(job, null, false);
             boolean isInsert = false;
             ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
             while (it.hasPrevious()) {
@@ -101,7 +101,7 @@
      * Really an == comparison should be enough, but why play with fate? We'll do <=.
      */
     @Override
-    public void maybeStopTrackingJobLocked(JobStatus job, boolean forUpdate) {
+    public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob, boolean forUpdate) {
         if (mTrackedJobs.remove(job)) {
             checkExpiredDelaysAndResetAlarm();
             checkExpiredDeadlinesAndResetAlarm();
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index 257c7da..5953dde 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -18,12 +18,17 @@
 
 import com.android.server.SystemService;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.VrManagerService;
 import com.android.server.vr.VrStateListener;
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Message;
+import android.os.RemoteException;
 import android.os.Trace;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.util.Slog;
 
 public class LightsService extends SystemService {
@@ -164,13 +169,19 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
-            getLocalService(VrManagerInternal.class).registerListener(mVrStateListener);
+            IVrManager vrManager =
+                    (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
+            try {
+                vrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            }
         }
     }
 
-    private final VrStateListener mVrStateListener = new VrStateListener() {
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
         @Override
-        public void onVrStateChanged(boolean enabled) {
+        public void onVrStateChanged(boolean enabled) throws RemoteException {
             LightImpl l = mLights[LightsManager.LIGHT_ID_BACKLIGHT];
             if (enabled) {
                 if (DEBUG) Slog.v(TAG, "VR mode enabled, setting brightness to low persistence");
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index e08fad4..6b916be 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -39,7 +39,7 @@
 import android.location.IGnssStatusListener;
 import android.location.IGnssStatusProvider;
 import android.location.GnssMeasurementsEvent;
-import android.location.GnssNavigationMessageEvent;
+import android.location.GnssNavigationMessage;
 import android.location.IGpsGeofenceHardware;
 import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
@@ -1662,7 +1662,7 @@
     /**
      * called from native code - GPS navigation message callback
      */
-    private void reportNavigationMessage(GnssNavigationMessageEvent event) {
+    private void reportNavigationMessage(GnssNavigationMessage event) {
         mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
     }
 
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 734a8d4..caf1d6c 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -64,15 +64,15 @@
         int status;
         switch (result) {
             case RESULT_SUCCESS:
-                status = GnssMeasurementsEvent.STATUS_READY;
+                status = GnssMeasurementsEvent.Callback.STATUS_READY;
                 break;
             case RESULT_NOT_AVAILABLE:
             case RESULT_NOT_SUPPORTED:
             case RESULT_INTERNAL_ERROR:
-                status = GnssMeasurementsEvent.STATUS_NOT_SUPPORTED;
+                status = GnssMeasurementsEvent.Callback.STATUS_NOT_SUPPORTED;
                 break;
             case RESULT_GPS_LOCATION_DISABLED:
-                status = GnssMeasurementsEvent.STATUS_GNSS_LOCATION_DISABLED;
+                status = GnssMeasurementsEvent.Callback.STATUS_LOCATION_DISABLED;
                 break;
             case RESULT_UNKNOWN:
                 return null;
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index fdef31f..8d21928 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -16,7 +16,7 @@
 
 package com.android.server.location;
 
-import android.location.GnssNavigationMessageEvent;
+import android.location.GnssNavigationMessage;
 import android.location.IGnssNavigationMessageListener;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -37,7 +37,7 @@
         super(handler, TAG);
     }
 
-    public void onNavigationMessageAvailable(final GnssNavigationMessageEvent event) {
+    public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
         ListenerOperation<IGnssNavigationMessageListener> operation =
                 new ListenerOperation<IGnssNavigationMessageListener>() {
                     @Override
@@ -65,16 +65,15 @@
         int status;
         switch (result) {
             case RESULT_SUCCESS:
-                status = GnssNavigationMessageEvent.STATUS_READY;
+                status = GnssNavigationMessage.Callback.STATUS_READY;
                 break;
             case RESULT_NOT_AVAILABLE:
             case RESULT_NOT_SUPPORTED:
             case RESULT_INTERNAL_ERROR:
-                status = GnssNavigationMessageEvent.STATUS_NOT_SUPPORTED;
+                status = GnssNavigationMessage.Callback.STATUS_NOT_SUPPORTED;
                 break;
             case RESULT_GPS_LOCATION_DISABLED:
-                status = GnssNavigationMessageEvent
-                        .STATUS_GNSS_LOCATION_DISABLED;
+                status = GnssNavigationMessage.Callback.STATUS_LOCATION_DISABLED;
                 break;
             case RESULT_UNKNOWN:
                 return null;
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index e3c540a..a4d2cd2 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -962,6 +962,7 @@
                         mKeyEventReceiver.aquireWakeLockLocked();
                     }
                     Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+                    mediaButtonIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     try {
                         if (user.mLastMediaButtonReceiver != null) {
@@ -986,6 +987,7 @@
                     }
                     // Fallback to legacy behavior
                     Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+                    keyIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                     keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
                     if (needWakeLock) {
                         keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index 68dc715..959a823 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -23,6 +23,8 @@
 import java.io.IOException;
 import java.util.HashSet;
 
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+
 /**
  * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
  * active on that interface.
@@ -34,6 +36,7 @@
     private static final int VERSION_INIT = 1;
     private static final int VERSION_ADD_ROAMING = 2;
     private static final int VERSION_ADD_NETWORK_ID = 3;
+    private static final int VERSION_ADD_METERED = 4;
 
     public NetworkIdentitySet() {
     }
@@ -61,12 +64,22 @@
                 roaming = false;
             }
 
-            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming));
+            final boolean metered;
+            if (version >= VERSION_ADD_METERED) {
+                metered = in.readBoolean();
+            } else {
+                // If this is the old data and the type is mobile, treat it as metered. (Note that
+                // if this is a mobile network, TYPE_MOBILE is the only possible type that could be
+                // used.)
+                metered = (type == TYPE_MOBILE);
+            }
+
+            add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered));
         }
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION_ADD_NETWORK_ID);
+        out.writeInt(VERSION_ADD_METERED);
         out.writeInt(size());
         for (NetworkIdentity ident : this) {
             out.writeInt(ident.getType());
@@ -74,6 +87,7 @@
             writeOptionalString(out, ident.getSubscriberId());
             writeOptionalString(out, ident.getNetworkId());
             out.writeBoolean(ident.getRoaming());
+            out.writeBoolean(ident.getMetered());
         }
     }
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 612bae2..c248608 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -940,7 +940,7 @@
             for (int subId : subIds) {
                 final String subscriberId = tele.getSubscriberId(subId);
                 final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false);
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
                 if (template.matches(probeIdent)) {
                     return true;
                 }
@@ -1333,7 +1333,7 @@
     private void ensureActiveMobilePolicyLocked(String subscriberId) {
         // Poke around to see if we already have a policy
         final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false);
+                TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true);
         for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
             final NetworkTemplate template = mNetworkPolicy.keyAt(i);
             if (template.matches(probeIdent)) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 660f790..f20d0a1 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -30,8 +30,11 @@
 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_UNAUTOBUNDLED;
 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.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
+import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_OFF;
 import static android.service.notification.NotificationListenerService.SUPPRESSED_EFFECT_SCREEN_ON;
 import static android.service.notification.NotificationListenerService.TRIM_FULL;
@@ -39,8 +42,6 @@
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_DEFAULT;
 import static android.service.notification.NotificationListenerService.Ranking.IMPORTANCE_NONE;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
-import static org.xmlpull.v1.XmlPullParser.END_TAG;
-import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.Manifest;
 import android.annotation.Nullable;
@@ -97,6 +98,7 @@
 import android.os.UserManager;
 import android.os.Vibrator;
 import android.provider.Settings;
+import android.service.notification.Adjustment;
 import android.service.notification.Condition;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
@@ -114,6 +116,7 @@
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.Xml;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -136,7 +139,6 @@
 
 import libcore.io.IoUtils;
 
-import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.xmlpull.v1.XmlPullParser;
@@ -158,7 +160,6 @@
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -248,8 +249,9 @@
     private String mSoundNotificationKey;
     private String mVibrateNotificationKey;
 
-    private final ArraySet<ManagedServiceInfo> mListenersDisablingEffects = new ArraySet<>();
-    private ComponentName mEffectsSuppressor;
+    private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects =
+            new SparseArray<ArraySet<ManagedServiceInfo>>();
+    private List<ComponentName> mEffectsSuppressors = new ArrayList<ComponentName>();
     private int mListenerHints;  // right now, all hints are global
     private int mInterruptionFilter = NotificationListenerService.INTERRUPTION_FILTER_UNKNOWN;
 
@@ -263,6 +265,7 @@
             new ArrayList<NotificationRecord>();
     final ArrayMap<String, NotificationRecord> mNotificationsByKey =
             new ArrayMap<String, NotificationRecord>();
+    final ArrayMap<String, String> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
     final PolicyAccess mPolicyAccess = new PolicyAccess();
@@ -283,11 +286,6 @@
     private static final String TAG_NOTIFICATION_POLICY = "notification-policy";
     private static final String ATTR_VERSION = "version";
 
-    // Obsolete:  converted if present, but not resaved to disk.
-    private static final String TAG_BLOCKED_PKGS = "blocked-packages";
-    private static final String TAG_PACKAGE = "package";
-    private static final String ATTR_NAME = "name";
-
     private RankingHelper mRankingHelper;
 
     private final UserProfiles mUserProfiles = new UserProfiles();
@@ -1118,23 +1116,112 @@
     }
 
     private void updateListenerHintsLocked() {
-        final int hints = mListenersDisablingEffects.isEmpty() ? 0 : HINT_HOST_DISABLE_EFFECTS;
+        final int hints = calculateHints();
         if (hints == mListenerHints) return;
-        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mListenersDisablingEffects.size());
+        ZenLog.traceListenerHintsChanged(mListenerHints, hints, mEffectsSuppressors.size());
         mListenerHints = hints;
         scheduleListenerHintsChanged(hints);
     }
 
     private void updateEffectsSuppressorLocked() {
-        final ComponentName suppressor = !mListenersDisablingEffects.isEmpty()
-                ? mListenersDisablingEffects.valueAt(0).component : null;
-        if (Objects.equals(suppressor, mEffectsSuppressor)) return;
-        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressor, suppressor);
-        mEffectsSuppressor = suppressor;
-        mZenModeHelper.setEffectsSuppressed(suppressor != null);
+        final long updatedSuppressedEffects = calculateSuppressedEffects();
+        if (updatedSuppressedEffects == mZenModeHelper.getSuppressedEffects()) return;
+        final List<ComponentName> suppressors = getSuppressors();
+        ZenLog.traceEffectsSuppressorChanged(mEffectsSuppressors, suppressors, updatedSuppressedEffects);
+        mEffectsSuppressors = suppressors;
+        mZenModeHelper.setSuppressedEffects(updatedSuppressedEffects);
         sendRegisteredOnlyBroadcast(NotificationManager.ACTION_EFFECTS_SUPPRESSOR_CHANGED);
     }
 
+    private ArrayList<ComponentName> getSuppressors() {
+        ArrayList<ComponentName> names = new ArrayList<ComponentName>();
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+
+            for (ManagedServiceInfo info : serviceInfoList) {
+                names.add(info.component);
+            }
+        }
+
+        return names;
+    }
+
+    private boolean removeDisabledHints(ManagedServiceInfo info) {
+        return removeDisabledHints(info, 0);
+    }
+
+    private boolean removeDisabledHints(ManagedServiceInfo info, int hints) {
+        boolean removed = false;
+
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            final int hint = mListenersDisablingEffects.keyAt(i);
+            final ArraySet<ManagedServiceInfo> listeners =
+                    mListenersDisablingEffects.valueAt(i);
+
+            if (hints == 0 || (hint & hints) == hint) {
+                removed = removed || listeners.remove(info);
+            }
+        }
+
+        return removed;
+    }
+
+    private void addDisabledHints(ManagedServiceInfo info, int hints) {
+        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_EFFECTS);
+        }
+
+        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
+        }
+
+        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+            addDisabledHint(info, HINT_HOST_DISABLE_CALL_EFFECTS);
+        }
+    }
+
+    private void addDisabledHint(ManagedServiceInfo info, int hint) {
+        if (mListenersDisablingEffects.indexOfKey(hint) < 0) {
+            mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>());
+        }
+
+        ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint);
+        hintListeners.add(info);
+    }
+
+    private int calculateHints() {
+        int hints = 0;
+        for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) {
+            int hint = mListenersDisablingEffects.keyAt(i);
+            ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i);
+
+            if (!serviceInfoList.isEmpty()) {
+                hints |= hint;
+            }
+        }
+
+        return hints;
+    }
+
+    private long calculateSuppressedEffects() {
+        int hints = calculateHints();
+        long suppressedEffects = 0;
+
+        if ((hints & HINT_HOST_DISABLE_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_ALL;
+        }
+
+        if ((hints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_NOTIFICATIONS;
+        }
+
+        if ((hints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
+            suppressedEffects |= ZenModeHelper.SUPPRESSED_EFFECT_CALLS;
+        }
+
+        return suppressedEffects;
+    }
+
     private void updateInterruptionFilterLocked() {
         int interruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
         if (interruptionFilter == mInterruptionFilter) return;
@@ -1259,10 +1346,13 @@
             checkCallerIsSystemOrSameApp(pkg);
             userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
                     Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg);
-            // Don't allow client applications to cancel foreground service notis.
+            // Don't allow client applications to cancel foreground service notis or autobundled
+            // summaries.
             cancelNotification(Binder.getCallingUid(), Binder.getCallingPid(), pkg, tag, id, 0,
-                    Binder.getCallingUid() == Process.SYSTEM_UID
-                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId,
+                    (Binder.getCallingUid() == Process.SYSTEM_UID
+                            ? 0 : Notification.FLAG_FOREGROUND_SERVICE)
+                            | (Binder.getCallingUid() == Process.SYSTEM_UID
+                            ? 0 : Notification.FLAG_AUTOGROUP_SUMMARY), false, userId,
                     REASON_APP_CANCEL, null);
         }
 
@@ -1404,7 +1494,9 @@
                 final int N = mNotificationList.size();
                 for (int i = 0; i < N; i++) {
                     final StatusBarNotification sbn = mNotificationList.get(i).sbn;
-                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
+                    if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId
+                            && (sbn.getNotification().flags
+                            & Notification.FLAG_AUTOGROUP_SUMMARY) != 0) {
                         // We could pass back a cloneLight() but clients might get confused and
                         // try to send this thing back to notify() again, which would not work
                         // very well.
@@ -1519,7 +1611,8 @@
             checkCallerIsSystemOrSameApp(component.getPackageName());
             long identity = Binder.clearCallingIdentity();
             try {
-                ManagedServices manager = mRankerServices.isComponentEnabledForCurrentProfiles(component)
+                ManagedServices manager =
+                        mRankerServices.isComponentEnabledForCurrentProfiles(component)
                         ? mRankerServices
                         : mListeners;
                 manager.setComponentState(component, true);
@@ -1651,11 +1744,14 @@
             try {
                 synchronized (mNotificationList) {
                     final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
-                    final boolean disableEffects = (hints & HINT_HOST_DISABLE_EFFECTS) != 0;
+                    final int disableEffectsMask = HINT_HOST_DISABLE_EFFECTS
+                            | HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
+                            | HINT_HOST_DISABLE_CALL_EFFECTS;
+                    final boolean disableEffects = (hints & disableEffectsMask) != 0;
                     if (disableEffects) {
-                        mListenersDisablingEffects.add(info);
+                        addDisabledHints(info, hints);
                     } else {
-                        mListenersDisablingEffects.remove(info);
+                        removeDisabledHints(info, hints);
                     }
                     updateListenerHintsLocked();
                     updateEffectsSuppressorLocked();
@@ -1913,7 +2009,7 @@
         @Override
         public ComponentName getEffectsSuppressor() {
             enforceSystemOrSystemUIOrVolume("INotificationManager.getEffectsSuppressor");
-            return mEffectsSuppressor;
+            return mEffectsSuppressors.get(0);
         }
 
         @Override
@@ -2035,25 +2131,123 @@
         }
 
         @Override
-        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);
-            }
+        public void applyAdjustmentFromRankerService(INotificationListener token,
+                Adjustment adjustment) throws RemoteException {
             final long identity = Binder.clearCallingIdentity();
             try {
                 synchronized (mNotificationList) {
                     mRankerServices.checkServiceTokenLocked(token);
-                    NotificationRecord n = mNotificationsByKey.get(key);
-                    n.setImportance(importance, explanation);
-                    mRankingHandler.requestSort();
+                    applyAdjustmentLocked(adjustment);
                 }
+                maybeAddAutobundleSummary(adjustment);
+                mRankingHandler.requestSort();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @Override
+        public void applyAdjustmentsFromRankerService(INotificationListener token,
+                List<Adjustment> adjustments) throws RemoteException {
+
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationList) {
+                    mRankerServices.checkServiceTokenLocked(token);
+                    for (Adjustment adjustment : adjustments) {
+                        applyAdjustmentLocked(adjustment);
+                    }
+                }
+                for (Adjustment adjustment : adjustments) {
+                    maybeAddAutobundleSummary(adjustment);
+                }
+                mRankingHandler.requestSort();
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
         }
     };
 
+    private void applyAdjustmentLocked(Adjustment adjustment) {
+        maybeClearAutobundleSummaryLocked(adjustment);
+        NotificationRecord n = mNotificationsByKey.get(adjustment.getKey());
+        if (n == null) {
+            return;
+        }
+        if (adjustment.getImportance() != IMPORTANCE_NONE) {
+            n.setImportance(adjustment.getImportance(), adjustment.getExplanation());
+        }
+        if (adjustment.getSignals() != null) {
+            Bundle.setDefusable(adjustment.getSignals(), true);
+            n.sbn.setOverrideGroupKey(adjustment.getSignals().getString(
+                    Adjustment.GROUP_KEY_OVERRIDE_KEY, null));
+        }
+    }
+
+    // Clears the 'fake' auto-bunding summary.
+    private void maybeClearAutobundleSummaryLocked(Adjustment adjustment) {
+        if (adjustment.getSignals() != null
+                && adjustment.getSignals().containsKey(Adjustment.NEEDS_AUTOGROUPING_KEY)
+                && !adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
+            if (mAutobundledSummaries.containsKey(adjustment.getPackage())) {
+                // Clear summary.
+                final NotificationRecord removed = mNotificationsByKey.get(
+                        mAutobundledSummaries.remove(adjustment.getPackage()));
+                if (removed != null) {
+                    mNotificationList.remove(removed);
+                    cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED);
+                }
+            }
+        }
+    }
+
+    // Posts a 'fake' summary for a package that has exceeded the solo-notification limit.
+    private void maybeAddAutobundleSummary(Adjustment adjustment) {
+        if (adjustment.getSignals() != null
+                && adjustment.getSignals().getBoolean(Adjustment.NEEDS_AUTOGROUPING_KEY, false)) {
+            final String newAutoBundleKey =
+                    adjustment.getSignals().getString(Adjustment.GROUP_KEY_OVERRIDE_KEY, null);
+            int userId = -1;
+            NotificationRecord summaryRecord = null;
+            synchronized (mNotificationList) {
+                if (!mAutobundledSummaries.containsKey(adjustment.getPackage())
+                        && newAutoBundleKey != null) {
+                    // Add summary
+                    final StatusBarNotification adjustedSbn
+                            = mNotificationsByKey.get(adjustment.getKey()).sbn;
+
+                    final ApplicationInfo appInfo =
+                            adjustedSbn.getNotification().extras.getParcelable(
+                                    Notification.EXTRA_BUILDER_APPLICATION_INFO);
+                    final Bundle extras = new Bundle();
+                    extras.putParcelable(Notification.EXTRA_BUILDER_APPLICATION_INFO, appInfo);
+                    final Notification summaryNotification =
+                            new Notification.Builder(getContext()).setSmallIcon(
+                                    adjustedSbn.getNotification().getSmallIcon())
+                                    .setGroupSummary(true)
+                                    .setGroup(newAutoBundleKey)
+                                    .setFlag(Notification.FLAG_AUTOGROUP_SUMMARY, true)
+                                    .setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+                                    .build();
+                    summaryNotification.extras.putAll(extras);
+                    final StatusBarNotification summarySbn =
+                            new StatusBarNotification(adjustedSbn.getPackageName(),
+                                    adjustedSbn.getOpPkg(),
+                                    Integer.MAX_VALUE, Adjustment.GROUP_KEY_OVERRIDE_KEY,
+                                    adjustedSbn.getUid(), adjustedSbn.getInitialPid(),
+                                    summaryNotification, adjustedSbn.getUser(), newAutoBundleKey,
+                                    System.currentTimeMillis());
+                    summaryRecord = new NotificationRecord(getContext(), summarySbn);
+                    mAutobundledSummaries.put(adjustment.getPackage(), summarySbn.getKey());
+                    userId = adjustedSbn.getUser().getIdentifier();
+                }
+            }
+            if (summaryRecord != null) {
+                mHandler.post(new EnqueueNotificationRunnable(userId, summaryRecord));
+            }
+        }
+    }
+
     private String disableNotificationEffects(NotificationRecord record) {
         if (mDisableNotificationEffects) {
             return "booleanState";
@@ -2175,9 +2369,19 @@
                 pw.print("    mListenersDisablingEffects: (");
                 N = mListenersDisablingEffects.size();
                 for (int i = 0; i < N; i++) {
-                    final ManagedServiceInfo listener = mListenersDisablingEffects.valueAt(i);
-                    if (i > 0) pw.print(',');
-                    pw.print(listener.component);
+                    final int hint = mListenersDisablingEffects.keyAt(i);
+                    if (i > 0) pw.print(';');
+                    pw.print("hint[" + hint + "]:");
+
+                    final ArraySet<ManagedServiceInfo> listeners =
+                            mListenersDisablingEffects.valueAt(i);
+                    final int listenerSize = listeners.size();
+
+                    for (int j = 0; j < listenerSize; j++) {
+                        if (i > 0) pw.print(',');
+                        final ManagedServiceInfo listener = listeners.valueAt(i);
+                        pw.print(listener.component);
+                    }
                 }
                 pw.println(')');
                 pw.println("\n  mRankerServicePackageName: " + mRankerServicePackageName);
@@ -2253,6 +2457,17 @@
                 callingUid, incomingUserId, true, false, "enqueueNotification", pkg);
         final UserHandle user = new UserHandle(userId);
 
+        // Fix the notification as best we can.
+        try {
+            Notification.addFieldsFromContext(getContext().createApplicationContext(
+                    getContext().getPackageManager().getApplicationInfoAsUser(
+                            pkg, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId),
+                    Context.CONTEXT_RESTRICTED), notification);
+        } catch (NameNotFoundException e) {
+            Slog.e(TAG, "Cannot create a context for sending app", e);
+            return;
+        }
+
         // Limit the number of notifications that any given package except the android
         // package or a registered listener can enqueue.  Prevents DOS attacks and deals with leaks.
         if (!isSystemNotification && !isNotificationFromListener) {
@@ -2492,7 +2707,7 @@
             StatusBarNotification sbn = r.sbn;
             String group = sbn.getGroupKey();
             boolean isSummary = sbn.getNotification().isGroupSummary();
-            boolean isChild = sbn.getNotification().isGroupChild();
+            boolean isChild = !isSummary && sbn.isGroup();
 
             NotificationRecord summary = mSummaryByGroupKey.get(group);
             if (isChild && summary != null) {
@@ -2857,11 +3072,13 @@
         synchronized (mNotificationList) {
             final int N = mNotificationList.size();
             ArrayList<String> orderBefore = new ArrayList<String>(N);
+            ArrayList<String> groupOverrideBefore = new ArrayList<>(N);
             int[] visibilities = new int[N];
-            int [] importances = new int[N];
+            int[] importances = new int[N];
             for (int i = 0; i < N; i++) {
                 final NotificationRecord r = mNotificationList.get(i);
                 orderBefore.add(r.getKey());
+                groupOverrideBefore.add(r.sbn.getGroupKey());
                 visibilities[i] = r.getPackageVisibilityOverride();
                 importances[i] = r.getImportance();
                 mRankingHelper.extractSignals(r);
@@ -2871,7 +3088,8 @@
                 final NotificationRecord r = mNotificationList.get(i);
                 if (!orderBefore.get(i).equals(r.getKey())
                         || visibilities[i] != r.getPackageVisibilityOverride()
-                        || importances[i] != r.getImportance()) {
+                        || importances[i] != r.getImportance()
+                        || !groupOverrideBefore.get(i).equals(r.sbn.getGroupKey())) {
                     scheduleSendRankingUpdate();
                     return;
                 }
@@ -2897,9 +3115,10 @@
     }
 
     private void scheduleSendRankingUpdate() {
-        mHandler.removeMessages(MESSAGE_SEND_RANKING_UPDATE);
-        Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
-        mHandler.sendMessage(m);
+        if (!mHandler.hasMessages(MESSAGE_SEND_RANKING_UPDATE)) {
+            Message m = Message.obtain(mHandler, MESSAGE_SEND_RANKING_UPDATE);
+            mHandler.sendMessage(m);
+        }
     }
 
     private void handleSendRankingUpdate() {
@@ -3069,6 +3288,7 @@
         mLights.remove(canceledKey);
 
         // Record usage stats
+        // TODO: add unbundling stats?
         switch (reason) {
             case REASON_DELEGATE_CANCEL:
             case REASON_DELEGATE_CANCEL_ALL:
@@ -3088,6 +3308,9 @@
         if (groupSummary != null && groupSummary.getKey().equals(r.getKey())) {
             mSummaryByGroupKey.remove(groupKey);
         }
+        if (r.sbn.getKey().equals(mAutobundledSummaries.get(r.sbn.getPackageName()))) {
+            mAutobundledSummaries.remove(r.sbn.getPackageName());
+        }
 
         // Save it for users of getHistoricalNotifications()
         mArchive.record(r.sbn);
@@ -3286,7 +3509,7 @@
         for (int i = N - 1; i >= 0; i--) {
             NotificationRecord childR = mNotificationList.get(i);
             StatusBarNotification childSbn = childR.sbn;
-            if (childR.getNotification().isGroupChild() &&
+            if ((childSbn.isGroup() && !childSbn.getNotification().isGroupSummary()) &&
                     childR.getGroupKey().equals(r.getGroupKey())) {
                 EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(),
                         childSbn.getTag(), userId, 0, 0, reason, listenerName);
@@ -3437,6 +3660,7 @@
         ArrayList<String> keys = new ArrayList<String>(N);
         ArrayList<String> interceptedKeys = new ArrayList<String>(N);
         ArrayList<Integer> importance = new ArrayList<>(N);
+        Bundle overrideGroupKeys = new Bundle();
         Bundle visibilityOverrides = new Bundle();
         Bundle suppressedVisualEffects = new Bundle();
         Bundle explanation = new Bundle();
@@ -3460,6 +3684,7 @@
                     != NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE) {
                 visibilityOverrides.putInt(key, record.getPackageVisibilityOverride());
             }
+            overrideGroupKeys.putString(key, record.sbn.getOverrideGroupKey());
         }
         final int M = keys.size();
         String[] keysAr = keys.toArray(new String[M]);
@@ -3469,7 +3694,7 @@
             importanceAr[i] = importance.get(i);
         }
         return new NotificationRankingUpdate(keysAr, interceptedKeysAr, visibilityOverrides,
-                suppressedVisualEffects, importanceAr, explanation);
+                suppressedVisualEffects, importanceAr, explanation, overrideGroupKeys);
     }
 
     private boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
@@ -3689,7 +3914,7 @@
 
         @Override
         protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
-            if (mListenersDisablingEffects.remove(removed)) {
+            if (removeDisabledHints(removed)) {
                 updateListenerHintsLocked();
                 updateEffectsSuppressorLocked();
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index fd893fa..a89a422 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -24,18 +24,15 @@
 
 import android.app.Notification;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
-import android.os.Build;
 import android.os.UserHandle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.EventLogTags;
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c45071b..207bdba 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -31,6 +31,7 @@
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.List;
 
 public class ZenLog {
     private static final String TAG = "ZenLog";
@@ -126,10 +127,11 @@
         append(TYPE_DISABLE_EFFECTS, record.getKey() + "," + reason);
     }
 
-    public static void traceEffectsSuppressorChanged(ComponentName oldSuppressor,
-            ComponentName newSuppressor) {
-        append(TYPE_SUPPRESSOR_CHANGED, componentToString(oldSuppressor) + "->"
-            + componentToString(newSuppressor));
+    public static void traceEffectsSuppressorChanged(List<ComponentName> oldSuppressors,
+            List<ComponentName> newSuppressors, long suppressedEffects) {
+        append(TYPE_SUPPRESSOR_CHANGED, "suppressed effects:" + suppressedEffects + ","
+                + componentListToString(oldSuppressors) + "->"
+                + componentListToString(newSuppressors));
     }
 
     public static void traceListenerHintsChanged(int oldHints, int newHints, int listenerCount) {
@@ -193,6 +195,19 @@
         return component != null ? component.toShortString() : null;
     }
 
+    private static String componentListToString(List<ComponentName> components) {
+        StringBuilder stringBuilder = new StringBuilder();
+
+        for (int i = 0; i < components.size(); ++i) {
+            if (i > 0) {
+                stringBuilder.append(", ");
+            }
+            stringBuilder.append(componentToString(components.get(i)));
+        }
+
+        return stringBuilder.toString();
+    }
+
     private static void append(int type, String msg) {
         synchronized(MSGS) {
             TIMES[sNext] = System.currentTimeMillis();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5c5c8f8..eb49e9f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -102,7 +102,12 @@
     private ZenModeConfig mConfig;
     private AudioManagerInternal mAudioManager;
     private PackageManager mPm;
-    private boolean mEffectsSuppressed;
+    private long mSuppressedEffects;
+
+    public static final long SUPPRESSED_EFFECT_NOTIFICATIONS = 1;
+    public static final long SUPPRESSED_EFFECT_CALLS = 1 << 1;
+    public static final long SUPPRESSED_EFFECT_ALL = SUPPRESSED_EFFECT_CALLS
+            | SUPPRESSED_EFFECT_NOTIFICATIONS;
 
     public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
         mContext = context;
@@ -228,12 +233,16 @@
         }
     }
 
-    public void setEffectsSuppressed(boolean effectsSuppressed) {
-        if (mEffectsSuppressed == effectsSuppressed) return;
-        mEffectsSuppressed = effectsSuppressed;
+    public void setSuppressedEffects(long suppressedEffects) {
+        if (mSuppressedEffects == suppressedEffects) return;
+        mSuppressedEffects = suppressedEffects;
         applyRestrictions();
     }
 
+    public long getSuppressedEffects() {
+        return mSuppressedEffects;
+    }
+
     public int getZenMode() {
         return mZenMode;
     }
@@ -484,7 +493,8 @@
         synchronized (mConfig) {
             dump(pw, prefix, "mConfig", mConfig);
         }
-        pw.print(prefix); pw.print("mEffectsSuppressed="); pw.println(mEffectsSuppressed);
+
+        pw.print(prefix); pw.print("mSuppressedEffects="); pw.println(mSuppressedEffects);
         mFiltering.dump(pw, prefix);
         mConditions.dump(pw, prefix);
     }
@@ -708,9 +718,11 @@
         final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
 
         // notification restrictions
-        final boolean muteNotifications = mEffectsSuppressed;
+        final boolean muteNotifications =
+                (mSuppressedEffects & SUPPRESSED_EFFECT_NOTIFICATIONS) != 0;
         // call restrictions
-        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
+        final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
+                || (mSuppressedEffects & SUPPRESSED_EFFECT_CALLS) != 0;
         // total silence restrictions
         final boolean muteEverything = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 928c19f..8368185 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -862,8 +862,12 @@
                 IntentSender statusReceiver, int userId) {
         final int callingUid = Binder.getCallingUid();
         mPm.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
+        boolean allowSilentUninstall = true;
         if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
             mAppOps.checkPackage(callingUid, callerPackageName);
+            final String installerPackageName = mPm.getInstallerPackageName(packageName);
+            allowSilentUninstall = installerPackageName != null
+                    && installerPackageName.equals(callerPackageName);
         }
 
         // Check whether the caller is device owner, in which case we do it silently.
@@ -874,8 +878,8 @@
 
         final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
                 statusReceiver, packageName, isDeviceOwner, userId);
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
-                == PackageManager.PERMISSION_GRANTED) {
+        if (allowSilentUninstall && mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.DELETE_PACKAGES) == PackageManager.PERMISSION_GRANTED) {
             // Sweet, call straight through!
             mPm.deletePackage(packageName, adapter.getBinder(), userId, flags);
         } else if (isDeviceOwner) {
@@ -901,7 +905,10 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG);
 
         synchronized (mSessions) {
-            mSessions.get(sessionId).setPermissionsResult(accepted);
+            PackageInstallerSession session = mSessions.get(sessionId);
+            if (session != null) {
+                session.setPermissionsResult(accepted);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ef53905..6cdc40f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,6 +81,7 @@
 import java.io.FileDescriptor;
 import java.io.FileFilter;
 import java.io.IOException;
+import java.security.cert.Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -151,6 +152,7 @@
     private String mPackageName;
     private int mVersionCode;
     private Signature[] mSignatures;
+    private Certificate[][] mCertificates;
 
     /**
      * Path to the validated base APK for this session, which may point at an
@@ -633,7 +635,7 @@
 
         mRelinquished = true;
         mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
-                installerPackageName, installerUid, user);
+                installerPackageName, installerUid, user, mCertificates);
     }
 
     /**
@@ -695,6 +697,7 @@
             }
             if (mSignatures == null) {
                 mSignatures = apk.signatures;
+                mCertificates = apk.certificates;
             }
 
             assertApkConsistent(String.valueOf(addedFile), apk);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f85dce7..e62450c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -76,7 +76,6 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.content.pm.PackageParser.PARSE_IS_PRIVILEGED;
 import static android.content.pm.PackageParser.isApkFile;
-import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.PACKAGE_INFO_GID;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
@@ -110,13 +109,13 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.IntentFilter.AuthorityEntry;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
 import android.content.ServiceConnection;
@@ -149,7 +148,6 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ActivityIntentInfo;
-import android.content.pm.PackageParser.IntentInfo;
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageStats;
@@ -277,6 +275,7 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
@@ -1064,8 +1063,9 @@
             | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
 
     final @Nullable String mRequiredVerifierPackage;
-    final @Nullable String mRequiredInstallerPackage;
+    final @NonNull String mRequiredInstallerPackage;
     final @Nullable String mSetupWizardPackage;
+    final @NonNull String mServicesSystemSharedLibraryPackageName;
 
     private final PackageUsage mPackageUsage = new PackageUsage();
 
@@ -2583,11 +2583,16 @@
                 mIntentFilterVerifierComponent = getIntentFilterVerifierComponentNameLPr();
                 mIntentFilterVerifier = new IntentVerifierProxy(mContext,
                         mIntentFilterVerifierComponent);
+                mServicesSystemSharedLibraryPackageName = getRequiredSharedLibraryLPr(
+                        PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES);
+                getRequiredSharedLibraryLPr(
+                        PackageManager.SYSTEM_SHARED_LIBRARY_SHARED);
             } else {
                 mRequiredVerifierPackage = null;
                 mRequiredInstallerPackage = null;
                 mIntentFilterVerifierComponent = null;
                 mIntentFilterVerifier = null;
+                mServicesSystemSharedLibraryPackageName = null;
             }
 
             mInstallerService = new PackageInstallerService(context, this);
@@ -2667,6 +2672,16 @@
         }
     }
 
+    private @NonNull String getRequiredSharedLibraryLPr(String libraryName) {
+        synchronized (mPackages) {
+            SharedLibraryEntry libraryEntry = mSharedLibraries.get(libraryName);
+            if (libraryEntry == null) {
+                throw new IllegalStateException("Missing required shared library:" + libraryName);
+            }
+            return libraryEntry.apk;
+        }
+    }
+
     private @NonNull String getRequiredInstallerLPr() {
         final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
         intent.addCategory(Intent.CATEGORY_DEFAULT);
@@ -2676,6 +2691,10 @@
                 MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                 UserHandle.USER_SYSTEM);
         if (matches.size() == 1) {
+            ResolveInfo resolveInfo = matches.get(0);
+            if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
+                throw new RuntimeException("The installer must be a privileged app");
+            }
             return matches.get(0).getComponentInfo().packageName;
         } else {
             throw new RuntimeException("There must be exactly one installer; found " + matches);
@@ -3036,7 +3055,7 @@
             }
             if (p == null) {
                 p = mPackages.get(packageName);
-                if (matchFactoryOnly && !isSystemApp(p)) {
+                if (matchFactoryOnly && p != null && !isSystemApp(p)) {
                     return null;
                 }
             }
@@ -3558,15 +3577,10 @@
     }
 
     @Override
-    public @Nullable String getServicesSystemSharedLibraryPackageName() {
+    public @NonNull String getServicesSystemSharedLibraryPackageName() {
         synchronized (mPackages) {
-            SharedLibraryEntry libraryEntry = mSharedLibraries.get(
-                    PackageManager.SYSTEM_SHARED_LIBRARY_SERVICES);
-            if (libraryEntry != null) {
-                return libraryEntry.apk;
-            }
+            return mServicesSystemSharedLibraryPackageName;
         }
-        return null;
     }
 
     @Override
@@ -7005,25 +7019,46 @@
             pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
         }
 
+        UsageStatsManager usageMgr =
+                (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
+
         int curr = 0;
         int total = pkgs.size();
         for (PackageParser.Package pkg : pkgs) {
             curr++;
 
+            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
+                }
+                continue;
+            }
+
+            if (!causeFirstBoot && usageMgr.isAppInactive(pkg.packageName)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of of idle app " + pkg.packageName);
+                }
+                continue;
+            }
+
             if (DEBUG_DEXOPT) {
                 Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName);
             }
 
-            if (PackageDexOptimizer.canOptimizePackage(pkg)) {
-                // If the cache was pruned, any compiled odex files will likely be out of date
-                // and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
-                // Instead, force the extraction in this case.
-                performDexOpt(pkg.packageName,
-                        null /* instructionSet */,
-                        false /* checkProfiles */,
-                        causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
-                        false /* force */);
+            if (!isFirstBoot()) {
+                try {
+                    ActivityManagerNative.getDefault().showBootMessage(
+                            mContext.getResources().getString(R.string.android_upgrading_apk,
+                                    curr, total), true);
+                } catch (RemoteException e) {
+                }
             }
+
+            performDexOpt(pkg.packageName,
+                    null /* instructionSet */,
+                    false /* checkProfiles */,
+                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
+                    false /* force */);
         }
     }
 
@@ -8570,7 +8605,7 @@
                 if (abi32 >= 0) {
                     final String abi = Build.SUPPORTED_32_BIT_ABIS[abi32];
                     if (abi64 >= 0) {
-                        if (cpuAbiOverride == null && pkg.use32bitAbi) {
+                        if (pkg.use32bitAbi) {
                             pkg.applicationInfo.secondaryCpuAbi = pkg.applicationInfo.primaryCpuAbi;
                             pkg.applicationInfo.primaryCpuAbi = abi;
                         } else {
@@ -10955,7 +10990,8 @@
                 null /*originatingUri*/, null /*referrer*/, -1 /*originatingUid*/, callingUid);
         final InstallParams params = new InstallParams(origin, null /*moveInfo*/, observer,
                 installFlags, installerPackageName, null /*volumeUuid*/, verificationInfo, user,
-                null /*packageAbiOverride*/, null /*grantedPermissions*/);
+                null /*packageAbiOverride*/, null /*grantedPermissions*/,
+                null /*certificates*/);
         params.setTraceMethod("installAsUser").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -10969,7 +11005,8 @@
 
     void installStage(String packageName, File stagedDir, String stagedCid,
             IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
-            String installerPackageName, int installerUid, UserHandle user) {
+            String installerPackageName, int installerUid, UserHandle user,
+            Certificate[][] certificates) {
         if (DEBUG_EPHEMERAL) {
             if ((sessionParams.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
                 Slog.d(TAG, "Ephemeral install of " + packageName);
@@ -10990,7 +11027,7 @@
         final InstallParams params = new InstallParams(origin, null, observer,
                 sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                 verificationInfo, user, sessionParams.abiOverride,
-                sessionParams.grantedRuntimePermissions);
+                sessionParams.grantedRuntimePermissions, certificates);
         params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
@@ -11717,6 +11754,9 @@
 
             // Okay!
             targetPackageSetting.installerPackageName = installerPackageName;
+            if (installerPackageName != null) {
+                mSettings.mInstallerPackages.add(installerPackageName);
+            }
             scheduleWriteSettingsLocked();
         }
     }
@@ -12085,11 +12125,12 @@
         final String packageAbiOverride;
         final String[] grantedRuntimePermissions;
         final VerificationInfo verificationInfo;
+        final Certificate[][] certificates;
 
         InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
                 int installFlags, String installerPackageName, String volumeUuid,
                 VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
-                String[] grantedPermissions) {
+                String[] grantedPermissions, Certificate[][] certificates) {
             super(user);
             this.origin = origin;
             this.move = move;
@@ -12100,6 +12141,7 @@
             this.verificationInfo = verificationInfo;
             this.packageAbiOverride = packageAbiOverride;
             this.grantedRuntimePermissions = grantedPermissions;
+            this.certificates = certificates;
         }
 
         @Override
@@ -12568,6 +12610,7 @@
         /** If non-null, drop an async trace when the install completes */
         final String traceMethod;
         final int traceCookie;
+        final Certificate[][] certificates;
 
         // The list of instruction sets supported by this app. This is currently
         // only used during the rmdex() phase to clean up resources. We can get rid of this
@@ -12578,7 +12621,7 @@
                 int installFlags, String installerPackageName, String volumeUuid,
                 UserHandle user, String[] instructionSets,
                 String abiOverride, String[] installGrantPermissions,
-                String traceMethod, int traceCookie) {
+                String traceMethod, int traceCookie, Certificate[][] certificates) {
             this.origin = origin;
             this.move = move;
             this.installFlags = installFlags;
@@ -12591,6 +12634,7 @@
             this.installGrantPermissions = installGrantPermissions;
             this.traceMethod = traceMethod;
             this.traceCookie = traceCookie;
+            this.certificates = certificates;
         }
 
         abstract int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException;
@@ -12683,9 +12727,9 @@
         FileInstallArgs(InstallParams params) {
             super(params.origin, params.move, params.observer, params.installFlags,
                     params.installerPackageName, params.volumeUuid,
-                    params.getUser(), null /* instruction sets */, params.packageAbiOverride,
+                    params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
-                    params.traceMethod, params.traceCookie);
+                    params.traceMethod, params.traceCookie, params.certificates);
             if (isFwdLocked()) {
                 throw new IllegalArgumentException("Forward locking only supported in ASEC");
             }
@@ -12694,7 +12738,7 @@
         /** Existing install */
         FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) {
             super(OriginInfo.fromNothing(), null, null, 0, null, null, null, instructionSets,
-                    null, null, null, 0);
+                    null, null, null, 0, null /*certificates*/);
             this.codeFile = (codePath != null) ? new File(codePath) : null;
             this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null;
         }
@@ -12919,15 +12963,15 @@
                     params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
-                    params.traceMethod, params.traceCookie);
+                    params.traceMethod, params.traceCookie, params.certificates);
         }
 
         /** Existing install */
         AsecInstallArgs(String fullCodePath, String[] instructionSets,
                         boolean isExternal, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
-                    instructionSets, null, null, null, 0);
+              | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+                    instructionSets, null, null, null, 0, null /*certificates*/);
             // Hackily pretend we're still looking at a full code path
             if (!fullCodePath.endsWith(RES_FILE_NAME)) {
                 fullCodePath = new File(fullCodePath, RES_FILE_NAME).getAbsolutePath();
@@ -12943,8 +12987,8 @@
 
         AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) {
             super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0)
-                    | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
-                    instructionSets, null, null, null, 0);
+              | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null,
+                    instructionSets, null, null, null, 0, null /*certificates*/);
             this.cid = cid;
             setMountPath(PackageHelper.getSdDir(cid));
         }
@@ -13213,7 +13257,7 @@
                     params.installerPackageName, params.volumeUuid,
                     params.getUser(), null /* instruction sets */, params.packageAbiOverride,
                     params.grantedRuntimePermissions,
-                    params.traceMethod, params.traceCookie);
+                    params.traceMethod, params.traceCookie, params.certificates);
         }
 
         int copyApk(IMediaContainerService imcs, boolean temp) {
@@ -14251,14 +14295,22 @@
             }
         }
 
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
         try {
-            PackageParser.collectCertificates(pkg, parseFlags);
+            // either use what we've been given or parse directly from the APK
+            if (args.certificates != null) {
+                try {
+                    PackageParser.populateCertificates(pkg, args.certificates);
+                } catch (PackageParserException e) {
+                    // there was something wrong with the certificates we were given;
+                    // try to pull them from the APK
+                    PackageParser.collectCertificates(pkg, parseFlags);
+                }
+            } else {
+                PackageParser.collectCertificates(pkg, parseFlags);
+            }
         } catch (PackageParserException e) {
             res.setError("Failed collect during installPackageLI", e);
             return;
-        } finally {
-            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
 
         // Get rid of all references to package scan path via parser.
@@ -14846,7 +14898,7 @@
      *  This method is an internal method that could be get invoked either
      *  to delete an installed package or to clean up a failed installation.
      *  After deleting an installed package, a broadcast is sent to notify any
-     *  listeners that the package has been installed. For cleaning up a failed
+     *  listeners that the package has been removed. For cleaning up a failed
      *  installation, the broadcast is not necessary since the package's
      *  installation wouldn't have sent the initial broadcast either
      *  The key steps in deleting a package are
@@ -16997,7 +17049,9 @@
         intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
 
         final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,
-                MATCH_SYSTEM_ONLY | MATCH_DISABLED_COMPONENTS, UserHandle.myUserId());
+                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
+                        | MATCH_DISABLED_COMPONENTS,
+                UserHandle.myUserId());
         if (matches.size() == 1) {
             return matches.get(0).getComponentInfo().packageName;
         } else {
@@ -19138,7 +19192,7 @@
         final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
         final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
                 installerPackageName, volumeUuid, null /*verificationInfo*/, user,
-                packageAbiOverride, null);
+                packageAbiOverride, null /*grantedPermissions*/, null /*certificates*/);
         params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
         msg.obj = params;
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3c3c576..83cd978 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -247,6 +247,9 @@
     /** Map from package name to settings */
     final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
 
+    /** List of packages that installed other packages */
+    final ArraySet<String> mInstallerPackages = new ArraySet<>();
+
     /** Map from package name to appId */
     private final ArrayMap<String, Integer> mKernelMapping = new ArrayMap<>();
 
@@ -360,7 +363,7 @@
     // Packages that have been uninstalled and still need their external
     // storage data deleted.
     final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
-    
+
     // Packages that have been renamed since they were first installed.
     // Keys are the new names of the packages, values are the original
     // names.  The packages appear everwhere else under their original
@@ -506,6 +509,9 @@
         PackageSetting p = mPackages.get(pkgName);
         if (p != null) {
             p.setInstallerPackageName(installerPkgName);
+            if (installerPkgName != null) {
+                mInstallerPackages.add(installerPkgName);
+            }
         }
     }
 
@@ -572,7 +578,7 @@
         }
         PackageSetting ret = addPackageLPw(name, p.realName, p.codePath, p.resourcePath,
                 p.legacyNativeLibraryPathString, p.primaryCpuAbiString,
-                p.secondaryCpuAbiString, p.secondaryCpuAbiString,
+                p.secondaryCpuAbiString, p.cpuAbiOverrideString,
                 p.appId, p.versionCode, p.pkgFlags, p.pkgPrivateFlags,
                 p.parentPackageName, p.childPackageNames);
         mDisabledSysPackages.remove(name);
@@ -1062,6 +1068,7 @@
         final PackageSetting p = mPackages.get(name);
         if (p != null) {
             mPackages.remove(name);
+            removeInstallerPackageStatus(name);
             if (p.sharedUser != null) {
                 p.sharedUser.removePackage(p);
                 if (p.sharedUser.packages.size() == 0) {
@@ -1077,6 +1084,26 @@
         return -1;
     }
 
+    /**
+     * Checks if {@param packageName} is an installer package and if so, clear the installer
+     * package name of the packages that are installed by this.
+     */
+    private void removeInstallerPackageStatus(String packageName) {
+        // Check if the package to be removed is an installer package.
+        if (!mInstallerPackages.contains(packageName)) {
+            return;
+        }
+        for (int i = 0; i < mPackages.size(); i++) {
+            final PackageSetting ps = mPackages.valueAt(i);
+            final String installerPackageName = ps.getInstallerPackageName();
+            if (installerPackageName != null
+                    && installerPackageName.equals(packageName)) {
+                ps.setInstallerPackageName(null);
+            }
+        }
+        mInstallerPackages.remove(packageName);
+    }
+
     private void replacePackageLPw(String name, PackageSetting newp) {
         final PackageSetting p = mPackages.get(name);
         if (p != null) {
@@ -2742,6 +2769,7 @@
         mPendingPackages.clear();
         mPastSignatures.clear();
         mKeySetRefs.clear();
+        mInstallerPackages.clear();
 
         try {
             if (str == null) {
@@ -3706,6 +3734,10 @@
                 packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
             }
 
+            if (installerPackageName != null) {
+                mInstallerPackages.add(installerPackageName);
+            }
+
             final String installStatusStr = parser.getAttributeValue(null, "installStatus");
             if (installStatusStr != null) {
                 if (installStatusStr.equalsIgnoreCase("false")) {
@@ -3724,7 +3756,7 @@
                 }
 
                 String tagName = parser.getName();
-                // Legacy 
+                // Legacy
                 if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
                     readDisabledComponentsLPw(packageSetting, parser, 0);
                 } else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 06a91fb..60a0d62 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -467,13 +467,16 @@
 
     @Override
     public List<UserInfo> getProfiles(int userId, boolean enabledOnly) {
+        boolean returnFullInfo = true;
         if (userId != UserHandle.getCallingUserId()) {
             checkManageUsersPermission("getting profiles related to user " + userId);
+        } else {
+            returnFullInfo = hasManageUsersPermission();
         }
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mUsersLock) {
-                return getProfilesLU(userId, enabledOnly);
+                return getProfilesLU(userId, enabledOnly, returnFullInfo);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -481,7 +484,7 @@
     }
 
     /** Assume permissions already checked and caller's identity cleared */
-    private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly) {
+    private List<UserInfo> getProfilesLU(int userId, boolean enabledOnly, boolean fullInfo) {
         UserInfo user = getUserInfoLU(userId);
         ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
         if (user == null) {
@@ -503,7 +506,14 @@
             if (profile.partial) {
                 continue;
             }
-            users.add(userWithName(profile));
+            UserInfo userInfo = userWithName(profile);
+            // If full info is not required - clear PII data to prevent 3P apps from reading it
+            if (!fullInfo) {
+                userInfo = new UserInfo(userInfo);
+                userInfo.name = null;
+                userInfo.iconPath = null;
+            }
+            users.add(userInfo);
         }
         return users;
     }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4b355de62..364e9fa6 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -425,6 +425,18 @@
                             }
                         }
                     }
+                    break;
+                case UserManager.DISALLOW_SAFE_BOOT:
+                    // Unlike with the other restrictions, we want to propagate the new value to
+                    // the system settings even if it is false. The other restrictions modify
+                    // settings which could be manually changed by the user from the Settings app
+                    // after the policies enforcing these restrictions have been revoked, so we
+                    // leave re-setting of those settings to the user.
+                    android.provider.Settings.Global.putInt(
+                            context.getContentResolver(),
+                            android.provider.Settings.Global.SAFE_BOOT_DISALLOWED,
+                            newValue ? 1 : 0);
+                    break;
             }
         } finally {
             Binder.restoreCallingIdentity(id);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 14d0457..5ce451f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -69,6 +69,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiPlaybackClient;
 import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.InputManagerInternal;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
@@ -107,6 +108,7 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.MutableBoolean;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.LongSparseArray;
@@ -304,6 +306,7 @@
     WindowManagerInternal mWindowManagerInternal;
     PowerManager mPowerManager;
     ActivityManagerInternal mActivityManagerInternal;
+    InputManagerInternal mInputManagerInternal;
     DreamManagerInternal mDreamManagerInternal;
     PowerManagerInternal mPowerManagerInternal;
     IStatusBarService mStatusBarService;
@@ -396,6 +399,8 @@
     volatile boolean mBeganFromNonInteractive;
     volatile int mPowerKeyPressCounter;
     volatile boolean mEndCallKeyHandled;
+    volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
+    volatile boolean mGoingToSleep;
 
     boolean mRecentsVisible;
     int mRecentAppsHeldModifiers;
@@ -601,6 +606,9 @@
     boolean mConsumeSearchKeyUp;
     boolean mAssistKeyLongPressed;
     boolean mPendingMetaAction;
+    boolean mPendingCapsLockToggle;
+    int mMetaState;
+    int mInitialMetaState;
     boolean mForceShowSystemBars;
 
     // support for activating the lock screen while the screen is on
@@ -684,8 +692,7 @@
     private final LogDecelerateInterpolator mLogDecelerateInterpolator
             = new LogDecelerateInterpolator(100, 0);
 
-    private boolean mForceWindowDrawsStatusBarBackground;
-
+    private final MutableBoolean mTmpBoolean = new MutableBoolean(false);
     private static final int MSG_ENABLE_POINTER_LOCATION = 1;
     private static final int MSG_DISABLE_POINTER_LOCATION = 2;
     private static final int MSG_DISPATCH_MEDIA_KEY_WITH_WAKE_LOCK = 3;
@@ -1032,7 +1039,11 @@
                 GestureLauncherService.class);
         boolean gesturedServiceIntercepted = false;
         if (gestureService != null) {
-            gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive);
+            gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
+                    mTmpBoolean);
+            if (mTmpBoolean.value && mGoingToSleep) {
+                mCameraGestureTriggeredDuringGoingToSleep = true;
+            }
         }
 
         // If the power key has still not yet been handled, then detect short
@@ -1486,6 +1497,7 @@
         mWindowManagerFuncs = windowManagerFuncs;
         mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
+        mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
         mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1731,8 +1743,6 @@
 
         mScreenshotChordEnabled = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableScreenshotChord);
-        mForceWindowDrawsStatusBarBackground = mContext.getResources().getBoolean(
-                R.bool.config_forceWindowDrawsStatusBarBackground);
 
         mGlobalKeyManager = new GlobalKeyManager(mContext);
 
@@ -1817,41 +1827,6 @@
             }
         }
 
-        mStatusBarHeight =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
-
-        // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotationDefault[mPortraitRotation] =
-        mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
-        mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
-        mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height_landscape);
-
-        // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotationDefault[mPortraitRotation] =
-        mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
-        mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
-        mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
-                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
-
-        // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
-        mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
-                res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.navigation_bar_height_car_mode);
-        mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
-        mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
-
-        // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
-        mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
-        mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
-        mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
-                res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.navigation_bar_width_car_mode);
-
         // SystemUI (status bar) layout policy
         int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
         int longSizeDp = longSize * DisplayMetrics.DENSITY_DEFAULT / density;
@@ -1860,6 +1835,7 @@
         mNavigationBarCanMove = width != height && shortSizeDp < 600;
 
         mHasNavigationBar = res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+
         // Allow a system property to override this. Used by the emulator.
         // See also hasNavigationBar().
         String navBarOverride = SystemProperties.get("qemu.hw.mainkeys");
@@ -2237,9 +2213,12 @@
             if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0) {
                 attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
             }
+            final boolean forceWindowDrawsStatusBarBackground =
+                    (attrs.privateFlags & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND)
+                            != 0;
             if ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
-                    || (mForceWindowDrawsStatusBarBackground
-                            && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT)) {
+                    || forceWindowDrawsStatusBarBackground
+                            && attrs.height == MATCH_PARENT && attrs.width == MATCH_PARENT) {
                 attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
             }
         }
@@ -2290,6 +2269,46 @@
         }
     }
 
+    @Override
+    public void onConfigurationChanged() {
+        final Resources res = mContext.getResources();
+
+        mStatusBarHeight =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
+
+        // Height of the navigation bar when presented horizontally at bottom
+        mNavigationBarHeightForRotationDefault[mPortraitRotation] =
+        mNavigationBarHeightForRotationDefault[mUpsideDownRotation] =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height);
+        mNavigationBarHeightForRotationDefault[mLandscapeRotation] =
+        mNavigationBarHeightForRotationDefault[mSeascapeRotation] = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height_landscape);
+
+        // Width of the navigation bar when presented vertically along one side
+        mNavigationBarWidthForRotationDefault[mPortraitRotation] =
+        mNavigationBarWidthForRotationDefault[mUpsideDownRotation] =
+        mNavigationBarWidthForRotationDefault[mLandscapeRotation] =
+        mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
+                res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
+
+        // Height of the navigation bar when presented horizontally at bottom
+        mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
+        mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+                res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_height_car_mode);
+        mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
+        mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
+
+        // Width of the navigation bar when presented vertically along one side
+        mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
+        mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
+        mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
+        mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+                res.getDimensionPixelSize(
+                        com.android.internal.R.dimen.navigation_bar_width_car_mode);
+    }
+
     /** {@inheritDoc} */
     @Override
     public int windowTypeToLayerLw(int type) {
@@ -2954,6 +2973,10 @@
         if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
             mPendingMetaAction = false;
         }
+        // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
+        if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
+            mPendingCapsLockToggle = false;
+        }
 
         // First we always handle the home key here, so applications
         // can never break it, although if keyguard is on, we do let
@@ -3202,6 +3225,38 @@
             }
         }
 
+        // Toggle Caps Lock on META-ALT.
+        boolean actionTriggered = false;
+        if (KeyEvent.isModifierKey(keyCode)) {
+            if (!mPendingCapsLockToggle) {
+                // Start tracking meta state for combo.
+                mInitialMetaState = mMetaState;
+                mPendingCapsLockToggle = true;
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                int altOnMask = mMetaState & KeyEvent.META_ALT_MASK;
+                int metaOnMask = mMetaState & KeyEvent.META_META_MASK;
+
+                // Check for Caps Lock toggle
+                if ((metaOnMask != 0) && (altOnMask != 0)) {
+                    // Check if nothing else is pressed
+                    if (mInitialMetaState == (mMetaState ^ (altOnMask | metaOnMask))) {
+                        // Handle Caps Lock Toggle
+                        mInputManagerInternal.toggleCapsLock(event.getDeviceId());
+                        actionTriggered = true;
+                    }
+                }
+
+                // Always stop tracking when key goes up.
+                mPendingCapsLockToggle = false;
+            }
+        }
+        // Store current meta state to be able to evaluate it later.
+        mMetaState = metaState;
+
+        if (actionTriggered) {
+            return -1;
+        }
+
         if (KeyEvent.isMetaKey(keyCode)) {
             if (down) {
                 mPendingMetaAction = true;
@@ -4218,6 +4273,7 @@
         }
 
         final int fl = PolicyControl.getWindowFlags(win, attrs);
+        final int pfl = attrs.privateFlags;
         final int sim = attrs.softInputMode;
         final int sysUiFl = PolicyControl.getSystemUiVisibility(win, null);
 
@@ -4313,7 +4369,7 @@
                         && (fl & WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) == 0
                         && (fl & WindowManager.LayoutParams.
                                 FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
-                        && !mForceWindowDrawsStatusBarBackground) {
+                        && (pfl & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) == 0) {
                     // Ensure policy decor includes status bar
                     dcf.top = mStableTop;
                 }
@@ -5948,6 +6004,8 @@
     @Override
     public void startedGoingToSleep(int why) {
         if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")");
+        mCameraGestureTriggeredDuringGoingToSleep = false;
+        mGoingToSleep = true;
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedGoingToSleep(why);
         }
@@ -5960,6 +6018,8 @@
         if (DEBUG_WAKEUP) Slog.i(TAG, "Finished going to sleep... (why=" + why + ")");
         MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
 
+        mGoingToSleep = false;
+
         // We must get this work done here because the power manager will drop
         // the wake lock and let the system suspend once this function returns.
         synchronized (mLock) {
@@ -5969,8 +6029,10 @@
             updateLockScreenTimeout();
         }
         if (mKeyguardDelegate != null) {
-            mKeyguardDelegate.onFinishedGoingToSleep(why);
+            mKeyguardDelegate.onFinishedGoingToSleep(why,
+                    mCameraGestureTriggeredDuringGoingToSleep);
         }
+        mCameraGestureTriggeredDuringGoingToSleep = false;
     }
 
     // Called on the PowerManager's Notifier thread.
@@ -7181,6 +7243,15 @@
         return vis;
     }
 
+    private boolean drawsSystemBarBackground(WindowState win) {
+        return win == null || (win.getAttrs().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
+    }
+
+    private boolean forcesDrawStatusBarBackground(WindowState win) {
+        return win == null || (win.getAttrs().privateFlags
+                & PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND) != 0;
+    }
+
     private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
         final boolean dockedStackVisible = mWindowManagerInternal.isStackVisible(DOCKED_STACK_ID);
         final boolean freeformStackVisible =
@@ -7194,11 +7265,22 @@
         final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;
 
         // apply translucent bar vis flags
-        WindowState transWin = isStatusBarKeyguard() && !mHideLockScreen
+        WindowState fullscreenTransWin = isStatusBarKeyguard() && !mHideLockScreen
                 ? mStatusBar
                 : mTopFullscreenOpaqueWindowState;
-        vis = mStatusBarController.applyTranslucentFlagLw(transWin, vis, oldVis);
-        vis = mNavigationBarController.applyTranslucentFlagLw(transWin, vis, oldVis);
+        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
+        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
+                mTopDockedOpaqueWindowState, 0, 0);
+
+        final boolean fullscreenDrawsStatusBarBackground =
+                (drawsSystemBarBackground(mTopFullscreenOpaqueWindowState)
+                        && (vis & View.STATUS_BAR_TRANSLUCENT) == 0)
+                || forcesDrawStatusBarBackground(mTopFullscreenOpaqueWindowState);
+        final boolean dockedDrawsStatusBarBackground =
+                (drawsSystemBarBackground(mTopDockedOpaqueWindowState)
+                        && (dockedVis & View.STATUS_BAR_TRANSLUCENT) == 0)
+                || forcesDrawStatusBarBackground(mTopDockedOpaqueWindowState);
 
         // prevent status bar interaction from clearing certain flags
         int type = win.getAttrs().type;
@@ -7215,18 +7297,16 @@
             vis = (vis & ~flags) | (oldVis & flags);
         }
 
-        if ((!areTranslucentBarsAllowed() && transWin != mStatusBar)
+        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
+            vis |= View.STATUS_BAR_TRANSPARENT;
+            vis &= ~View.STATUS_BAR_TRANSLUCENT;
+        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
                 || forceOpaqueStatusBar) {
             vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
         }
 
         vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);
 
-        if (mForceWindowDrawsStatusBarBackground) {
-            vis |= View.STATUS_BAR_TRANSPARENT;
-            vis &= ~View.STATUS_BAR_TRANSLUCENT;
-        }
-
         // update status bar
         boolean immersiveSticky =
                 (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 8d296d5..52e5880 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -294,9 +294,9 @@
         mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
     }
 
-    public void onFinishedGoingToSleep(int why) {
+    public void onFinishedGoingToSleep(int why, boolean cameraGestureTriggered) {
         if (mKeyguardService != null) {
-            mKeyguardService.onFinishedGoingToSleep(why);
+            mKeyguardService.onFinishedGoingToSleep(why, cameraGestureTriggered);
         }
         mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
     }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 429b188..dacdec0 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -117,9 +117,9 @@
     }
 
     @Override
-    public void onFinishedGoingToSleep(int reason) {
+    public void onFinishedGoingToSleep(int reason, boolean cameraGestureTriggered) {
         try {
-            mService.onFinishedGoingToSleep(reason);
+            mService.onFinishedGoingToSleep(reason, cameraGestureTriggered);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 7570960..8cd536d 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -53,6 +53,8 @@
 import android.provider.Settings.Secure;
 import android.provider.Settings.SettingNotFoundException;
 import android.service.dreams.DreamManagerInternal;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -72,6 +74,7 @@
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.vr.VrManagerInternal;
+import com.android.server.vr.VrManagerService;
 import com.android.server.vr.VrStateListener;
 import libcore.util.Objects;
 
@@ -658,7 +661,13 @@
             resolver.registerContentObserver(Settings.Secure.getUriFor(
                     Secure.BRIGHTNESS_USE_TWILIGHT),
                     false, mSettingsObserver, UserHandle.USER_ALL);
-            getLocalService(VrManagerInternal.class).registerListener(mVrStateListener);
+            IVrManager vrManager =
+                    (IVrManager) getBinderService(VrManagerService.VR_MANAGER_BINDER_SERVICE);
+            try {
+                vrManager.registerListener(mVrStateCallbacks);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to register VR mode state listener: " + e);
+            }
             // Go.
             readConfigurationLocked();
             updateSettingsLocked();
@@ -3007,7 +3016,7 @@
         }
     }
 
-    private final VrStateListener mVrStateListener = new VrStateListener() {
+    private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
         @Override
         public void onVrStateChanged(boolean enabled) {
             powerHintInternal(POWER_HINT_VR_MODE, enabled ? 1 : 0);
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index bcafddc..5b9d139 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -93,6 +93,7 @@
 
     // Indicates whether we are rebooting into safe mode
     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
+    public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
 
     // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this
     public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode";
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 858f7c7..9c2c6bf 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -375,7 +375,7 @@
                 } else {
                     mTrustAgentService.onConfigure(Collections.EMPTY_LIST, null);
                 }
-                final long maxTimeToLock = dpm.getMaximumTimeToLock(null);
+                final long maxTimeToLock = dpm.getMaximumTimeToLockForUserAndProfiles(mUserId);
                 if (maxTimeToLock != mMaximumTimeToLock) {
                     // If the timeout changes, cancel the alarm and send a timeout event to have
                     // the agent re-evaluate trust.
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 93bb9d7..1bbb9f5 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -31,13 +31,6 @@
     public static final int NO_ERROR = 0;
 
     /**
-     * Return current VR mode state.
-     *
-     * @return {@code true} if VR mode is enabled.
-     */
-    public abstract boolean isInVrMode();
-
-    /**
      * Return {@code true} if the given package is the currently bound VrListenerService for the
      * given user.
      *
@@ -59,22 +52,6 @@
     public abstract void setVrMode(boolean enabled, @NonNull ComponentName packageName,
             int userId, @NonNull ComponentName calling);
 
-    /**
-     * Add a listener for VR mode state changes.
-     * <p>
-     * This listener will immediately be called with the current VR mode state.
-     * </p>
-     * @param listener the listener instance to add.
-     */
-    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(@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.
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index c572e76..f004b45 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.vr;
 
+import android.Manifest;
 import android.app.AppOpsManager;
 import android.app.NotificationManager;
 import android.annotation.NonNull;
@@ -29,11 +30,15 @@
 import android.os.IBinder;
 import android.os.IInterface;
 import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.NotificationListenerService;
 import android.service.vr.IVrListener;
+import android.service.vr.IVrManager;
+import android.service.vr.IVrStateCallbacks;
 import android.service.vr.VrListenerService;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -46,6 +51,7 @@
 import com.android.server.utils.ManagedApplicationService.BinderChecker;
 
 import java.lang.StringBuilder;
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Objects;
@@ -75,6 +81,8 @@
 
     public static final String TAG = "VrManagerService";
 
+    public static final String VR_MANAGER_BINDER_SERVICE = "vrmanager";
+
     private static native void initializeNative();
     private static native void setVrModeNative(boolean enabled);
 
@@ -84,7 +92,6 @@
 
     // State protected by mLock
     private boolean mVrModeEnabled;
-    private final Set<VrStateListener> mListeners = new ArraySet<>();
     private EnabledComponentsObserver mComponentObserver;
     private ManagedApplicationService mCurrentVrService;
     private Context mContext;
@@ -92,10 +99,37 @@
     private int mCurrentVrModeUser;
     private boolean mWasDefaultGranted;
     private boolean mGuard;
+    private final RemoteCallbackList<IVrStateCallbacks> mRemoteCallbacks =
+            new RemoteCallbackList<>();
     private final ArraySet<String> mPreviousToggledListenerSettings = new ArraySet<>();
     private String mPreviousNotificationPolicyAccessPackage;
     private String mPreviousManageOverlayPackage;
 
+    private static final int MSG_VR_STATE_CHANGE = 0;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_VR_STATE_CHANGE : {
+                    boolean state = (msg.arg1 == 1);
+                    int i = mRemoteCallbacks.beginBroadcast();
+                    while (i > 0) {
+                        i--;
+                        try {
+                            mRemoteCallbacks.getBroadcastItem(i).onVrStateChanged(state);
+                        } catch (RemoteException e) {
+                            // Noop
+                        }
+                    }
+                    mRemoteCallbacks.finishBroadcast();
+                } break;
+                default :
+                    throw new IllegalStateException("Unknown message type: " + msg.what);
+            }
+        }
+    };
+
     private static final BinderChecker sBinderChecker = new BinderChecker() {
         @Override
         public IInterface asInterface(IBinder binder) {
@@ -125,16 +159,47 @@
         }
     }
 
+    private final IVrManager mVrManager = new IVrManager.Stub() {
+
+        @Override
+        public void registerListener(IVrStateCallbacks cb) {
+            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            if (cb == null) {
+                throw new IllegalArgumentException("Callback binder object is null.");
+            }
+
+            VrManagerService.this.addStateCallback(cb);
+        }
+
+        @Override
+        public void unregisterListener(IVrStateCallbacks cb) {
+            enforceCallerPermission(Manifest.permission.ACCESS_VR_MANAGER);
+            if (cb == null) {
+                throw new IllegalArgumentException("Callback binder object is null.");
+            }
+
+            VrManagerService.this.removeStateCallback(cb);
+        }
+
+        @Override
+        public boolean getVrModeState() {
+            return VrManagerService.this.getVrMode();
+        }
+
+    };
+
+    private void enforceCallerPermission(String permission) {
+        if (mContext.checkCallingOrSelfPermission(permission)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Caller does not hold the permission " + permission);
+        }
+    }
+
     /**
      * Implementation of VrManagerInternal.  Callable only from system services.
      */
     private final class LocalService extends VrManagerInternal {
         @Override
-        public boolean isInVrMode() {
-            return VrManagerService.this.getVrMode();
-        }
-
-        @Override
         public void setVrMode(boolean enabled, ComponentName packageName, int userId,
                 ComponentName callingPackage) {
             VrManagerService.this.setVrMode(enabled, packageName, userId, callingPackage);
@@ -146,16 +211,6 @@
         }
 
         @Override
-        public void registerListener(VrStateListener listener) {
-            VrManagerService.this.addListener(listener);
-        }
-
-        @Override
-        public void unregisterListener(VrStateListener listener) {
-            VrManagerService.this.removeListener(listener);
-        }
-
-        @Override
         public int hasVrPackage(ComponentName packageName, int userId) {
             return VrManagerService.this.hasVrPackage(packageName, userId);
         }
@@ -173,6 +228,7 @@
         }
 
         publishLocalService(VrManagerInternal.class, new LocalService());
+        publishBinderService(VR_MANAGER_BINDER_SERVICE, mVrManager.asBinder());
     }
 
     @Override
@@ -434,8 +490,15 @@
     private void revokeNotificationPolicyAccess(String pkg) {
         NotificationManager nm = mContext.getSystemService(NotificationManager.class);
         if (mPreviousNotificationPolicyAccessPackage != null) {
-            nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
-            mPreviousNotificationPolicyAccessPackage = null;
+            if (mPreviousNotificationPolicyAccessPackage.equals(pkg)) {
+                // Remove any DND zen rules possibly created by the package.
+                nm.removeAutomaticZenRules(mPreviousNotificationPolicyAccessPackage);
+                // Remove Notification Policy Access.
+                nm.setNotificationPolicyAccessGranted(mPreviousNotificationPolicyAccessPackage, false);
+                mPreviousNotificationPolicyAccessPackage = null;
+            } else {
+                Slog.e(TAG, "Couldn't remove Notification Policy Access for package: " + pkg);
+            }
         }
     }
 
@@ -551,9 +614,8 @@
      * Note: Must be called while holding {@code mLock}.
      */
     private void onVrModeChangedLocked() {
-        for (VrStateListener l : mListeners) {
-            l.onVrStateChanged(mVrModeEnabled);
-        }
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_VR_STATE_CHANGE,
+                (mVrModeEnabled) ? 1 : 0, 0));
     }
 
     /**
@@ -577,9 +639,9 @@
         }
     }
 
-    private boolean getVrMode() {
+    private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
         synchronized (mLock) {
-            return mVrModeEnabled;
+            return mComponentObserver.isValid(targetPackageName, userId);
         }
     }
 
@@ -593,21 +655,21 @@
         }
     }
 
-    private void addListener(VrStateListener listener) {
-        synchronized (mLock) {
-            mListeners.add(listener);
-        }
+    /*
+     * Implementation of IVrManager calls.
+     */
+
+    private void addStateCallback(IVrStateCallbacks cb) {
+        mRemoteCallbacks.register(cb);
     }
 
-    private void removeListener(VrStateListener listener) {
-        synchronized (mLock) {
-            mListeners.remove(listener);
-        }
+    private void removeStateCallback(IVrStateCallbacks cb) {
+        mRemoteCallbacks.unregister(cb);
     }
 
-    private int hasVrPackage(@NonNull ComponentName targetPackageName, int userId) {
+    private boolean getVrMode() {
         synchronized (mLock) {
-            return mComponentObserver.isValid(targetPackageName, userId);
+            return mVrModeEnabled;
         }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 6052a6e..9486cfd 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -110,9 +110,7 @@
                     Log.e(TAG, "Found an element that is not a webview provider");
                 }
             }
-        } catch(XmlPullParserException e) {
-            throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
-        } catch(IOException e) {
+        } catch (XmlPullParserException | IOException e) {
             throw new MissingWebViewPackageException("Error when parsing WebView meta data " + e);
         } finally {
             if (parser != null) parser.close();
@@ -120,6 +118,11 @@
         return webViewProviders.toArray(new WebViewProviderInfo[webViewProviders.size()]);
     }
 
+    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException {
+        PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+        return pm.getPackageInfo(packageName, PackageManager.MATCH_FACTORY_ONLY).versionCode;
+    }
+
     /**
      * Reads all signatures at the current depth (within the current provider) from the XML parser.
      */
@@ -181,15 +184,18 @@
 
     @Override
     public void uninstallAndDisablePackageForAllUsers(Context context, String packageName) {
-        context.getPackageManager().deletePackage(packageName,
-                new IPackageDeleteObserver.Stub() {
-            public void packageDeleted(String packageName, int returnCode) {
-                // Ignore returnCode since the deletion could fail, e.g. we might be trying
-                // to delete a non-updated system-package (and we should still disable the
-                // package)
-                enablePackageForAllUsers(context, packageName, false);
+        enablePackageForAllUsers(context, packageName, false);
+        try {
+            PackageManager pm = AppGlobals.getInitialApplication().getPackageManager();
+            if (pm.getApplicationInfo(packageName, 0).isUpdatedSystemApp()) {
+                pm.deletePackage(packageName, new IPackageDeleteObserver.Stub() {
+                        public void packageDeleted(String packageName, int returnCode) {
+                            enablePackageForAllUsers(context, packageName, false);
+                        }
+                    }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
             }
-        }, PackageManager.DELETE_SYSTEM_APP | PackageManager.DELETE_ALL_USERS);
+        } catch (NameNotFoundException e) {
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index b5eb0a7..7bde37a 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -32,6 +32,7 @@
 public interface SystemInterface {
     public WebViewProviderInfo[] getWebViewPackages();
     public int onWebViewProviderChanged(PackageInfo packageInfo);
+    public int getFactoryPackageVersion(String packageName) throws NameNotFoundException;
 
     public String getUserChosenWebViewProvider(Context context);
     public void updateUserSetting(Context context, String newProviderName);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 4669676..1f6fb2a 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -20,16 +20,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
 import android.os.Binder;
 import android.os.PatternMatcher;
 import android.os.Process;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
-import android.util.Base64;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
 import android.webkit.WebViewFactory;
@@ -39,9 +35,7 @@
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.List;
 
 /**
  * Private service to wait for the updatable WebView to be ready for use.
@@ -50,26 +44,18 @@
 public class WebViewUpdateService extends SystemService {
 
     private static final String TAG = "WebViewUpdateService";
-    private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
-
-    // Keeps track of the number of running relro creations
-    private int mNumRelroCreationsStarted = 0;
-    private int mNumRelroCreationsFinished = 0;
-    // Implies that we need to rerun relro creation because we are using an out-of-date package
-    private boolean mWebViewPackageDirty = false;
-    private boolean mAnyWebViewInstalled = false;
-
-    private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
-
-    // The WebView package currently in use (or the one we are preparing).
-    private PackageInfo mCurrentWebViewPackage = null;
 
     private BroadcastReceiver mWebViewUpdatedReceiver;
-    private SystemInterface mSystemInterface;
+    private WebViewUpdateServiceImpl mImpl;
+
+    static final int PACKAGE_CHANGED = 0;
+    static final int PACKAGE_ADDED = 1;
+    static final int PACKAGE_ADDED_REPLACED = 2;
+    static final int PACKAGE_REMOVED = 3;
 
     public WebViewUpdateService(Context context) {
         super(context);
-        mSystemInterface = new SystemImpl();
+        mImpl = new WebViewUpdateServiceImpl(context, new SystemImpl());
     }
 
     @Override
@@ -77,76 +63,36 @@
         mWebViewUpdatedReceiver = new BroadcastReceiver() {
                 @Override
                 public void onReceive(Context context, Intent intent) {
-                    // When a package is replaced we will receive two intents, one representing
-                    // the removal of the old package and one representing the addition of the
-                    // new package.
-                    // In the case where we receive an intent to remove the old version of the
-                    // package that is being replaced we early-out here so that we don't run the
-                    // update-logic twice.
-                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)
-                        && intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) {
-                        return;
-                    }
-
-                    // Ensure that we only heed PACKAGE_CHANGED intents if they change an entire
-                    // package, not just a component
-                    if (intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED)) {
-                        if (!entirePackageChanged(intent)) {
-                            return;
-                        }
-                    }
-
-                    if (intent.getAction().equals(Intent.ACTION_USER_ADDED)) {
-                        int userId =
-                            intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
-                        handleNewUser(userId);
-                        return;
-                    }
-
-                    updateFallbackState(context, intent);
-
-                    for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
-                        String webviewPackage = "package:" + provider.packageName;
-
-                        if (webviewPackage.equals(intent.getDataString())) {
-                            boolean updateWebView = false;
-                            boolean removedOrChangedOldPackage = false;
-                            String oldProviderName = null;
-                            PackageInfo newPackage = null;
-                            synchronized(WebViewUpdateService.this) {
-                                try {
-                                    newPackage = findPreferredWebViewPackage();
-                                    if (mCurrentWebViewPackage != null)
-                                        oldProviderName = mCurrentWebViewPackage.packageName;
-                                    // Only trigger update actions if the updated package is the one
-                                    // that will be used, or the one that was in use before the
-                                    // update, or if we haven't seen a valid WebView package before.
-                                    updateWebView =
-                                        provider.packageName.equals(newPackage.packageName)
-                                        || provider.packageName.equals(oldProviderName)
-                                        || mCurrentWebViewPackage == null;
-                                    // We removed the old package if we received an intent to remove
-                                    // or replace the old package.
-                                    removedOrChangedOldPackage =
-                                        provider.packageName.equals(oldProviderName);
-                                    if (updateWebView) {
-                                        onWebViewProviderChanged(newPackage);
-                                    }
-                                } catch (WebViewFactory.MissingWebViewPackageException e) {
-                                    Slog.e(TAG, "Could not find valid WebView package to create " +
-                                            "relro with " + e);
-                                }
+                    switch (intent.getAction()) {
+                        case Intent.ACTION_PACKAGE_REMOVED:
+                            // When a package is replaced we will receive two intents, one
+                            // representing the removal of the old package and one representing the
+                            // addition of the new package.
+                            // In the case where we receive an intent to remove the old version of
+                            // the package that is being replaced we early-out here so that we don't
+                            // run the update-logic twice.
+                            if (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)) return;
+                            mImpl.packageStateChanged(packageNameFromIntent(intent),
+                                    PACKAGE_REMOVED);
+                            break;
+                        case Intent.ACTION_PACKAGE_CHANGED:
+                            // Ensure that we only heed PACKAGE_CHANGED intents if they change an
+                            // entire package, not just a component
+                            if (entirePackageChanged(intent)) {
+                                mImpl.packageStateChanged(packageNameFromIntent(intent),
+                                        PACKAGE_CHANGED);
                             }
-                            if(updateWebView && !removedOrChangedOldPackage
-                                    && oldProviderName != null) {
-                                // If the provider change is the result of adding or replacing a
-                                // package that was not the previous provider then we must kill
-                                // packages dependent on the old package ourselves. The framework
-                                // only kills dependents of packages that are being removed.
-                                mSystemInterface.killPackageDependents(oldProviderName);
-                            }
-                            return;
-                        }
+                            break;
+                        case Intent.ACTION_PACKAGE_ADDED:
+                            mImpl.packageStateChanged(packageNameFromIntent(intent),
+                                    (intent.getExtras().getBoolean(Intent.EXTRA_REPLACING)
+                                     ? PACKAGE_ADDED_REPLACED : PACKAGE_ADDED));
+                            break;
+                        case Intent.ACTION_USER_ADDED:
+                            int userId =
+                                intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                            mImpl.handleNewUser(userId);
+                            break;
                     }
                 }
         };
@@ -156,7 +102,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addDataScheme("package");
         // Make sure we only receive intents for WebView packages from our config file.
-        for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+        for (WebViewProviderInfo provider : mImpl.getWebViewPackages()) {
             filter.addDataSchemeSpecificPart(provider.packageName, PatternMatcher.PATTERN_LITERAL);
         }
         getContext().registerReceiver(mWebViewUpdatedReceiver, filter);
@@ -168,315 +114,12 @@
         publishBinderService("webviewupdate", new BinderService(), true /*allowIsolated*/);
     }
 
-    private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
-        for (WebViewProviderInfo provider : providers) {
-            if (provider.availableByDefault && !provider.isFallback) {
-                try {
-                    PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
-                    if (isEnabledPackage(packageInfo) && isValidProvider(provider, packageInfo)) {
-                        return true;
-                    }
-                } catch (NameNotFoundException e) {
-                    // A non-existent provider is neither valid nor enabled
-                }
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Called when a new user has been added to update the state of its fallback package.
-     */
-    void handleNewUser(int userId) {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
-
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider == null) return;
-
-        mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
-                !existsValidNonFallbackProvider(webviewProviders), userId);
-    }
-
-    /**
-     * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
-     * package that is valid (and available by default) then disable the fallback package,
-     * otherwise, enable the fallback package.
-     */
-    void updateFallbackState(final Context context, final Intent intent) {
-        if (!mSystemInterface.isFallbackLogicEnabled()) return;
-
-        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
-
-        if (intent != null && (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)
-                    || intent.getAction().equals(Intent.ACTION_PACKAGE_CHANGED))) {
-            // A package was changed / updated / downgraded, early out if it is not one of the
-            // webview packages that are available by default.
-            String changedPackage = null;
-            for (WebViewProviderInfo provider : webviewProviders) {
-                String webviewPackage = "package:" + provider.packageName;
-                if (webviewPackage.equals(intent.getDataString())) {
-                    if (provider.availableByDefault) {
-                        changedPackage = provider.packageName;
-                    }
-                    break;
-                }
-            }
-            if (changedPackage == null) return;
-        }
-
-        // If there exists a valid and enabled non-fallback package - disable the fallback
-        // package, otherwise, enable it.
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
-        if (fallbackProvider == null) return;
-        boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
-
-        boolean isFallbackEnabled = false;
-        try {
-            isFallbackEnabled =
-                isEnabledPackage(mSystemInterface.getPackageInfoForProvider(fallbackProvider));
-        } catch (NameNotFoundException e) {
-        }
-
-        if (existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (isFallbackEnabled || intent == null)) {
-            mSystemInterface.uninstallAndDisablePackageForAllUsers(context,
-                    fallbackProvider.packageName);
-        } else if (!existsValidNonFallbackProvider
-                // During an OTA the primary user's WebView state might differ from other users', so
-                // ignore the state of that user during boot.
-                && (!isFallbackEnabled || intent==null)) {
-            // Enable the fallback package for all users.
-            mSystemInterface.enablePackageForAllUsers(context, fallbackProvider.packageName, true);
-        }
-    }
-
-    /**
-     * Returns the only fallback provider, or null if there is none.
-     */
-    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
-        for (WebViewProviderInfo provider : webviewPackages) {
-            if (provider.isFallback) {
-                return provider;
-            }
-        }
-        return null;
-    }
-
-    private boolean isFallbackPackage(String packageName) {
-        if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
-
-        WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
-        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
-        return (fallbackProvider != null
-                && packageName.equals(fallbackProvider.packageName));
-    }
-
-    /**
-     * Perform any WebView loading preparations that must happen at boot from the system server,
-     * after the package manager has started or after an update to the webview is installed.
-     * This must be called in the system server.
-     * Currently, this means spawning the child processes which will create the relro files.
-     */
     public void prepareWebViewInSystemServer() {
-        updateFallbackState(getContext(), null);
-        try {
-            synchronized(this) {
-                mCurrentWebViewPackage = findPreferredWebViewPackage();
-                onWebViewProviderChanged(mCurrentWebViewPackage);
-            }
-        } catch (Throwable t) {
-            // Log and discard errors at this stage as we must not crash the system server.
-            Slog.e(TAG, "error preparing webview provider from system server", t);
-        }
+        mImpl.prepareWebViewInSystemServer();
     }
 
-
-    /**
-     * Change WebView provider and provider setting and kill packages using the old provider.
-     * Return the new provider (in case we are in the middle of creating relro files this new
-     * provider will not be in use directly, but will when the relros are done).
-     */
-    private String changeProviderAndSetting(String newProviderName) {
-        PackageInfo oldPackage = null;
-        PackageInfo newPackage = null;
-        synchronized(this) {
-            oldPackage = mCurrentWebViewPackage;
-            mSystemInterface.updateUserSetting(getContext(), newProviderName);
-
-            try {
-                newPackage = findPreferredWebViewPackage();
-                if (oldPackage != null && newPackage.packageName.equals(oldPackage.packageName)) {
-                    // If we don't perform the user change, revert the settings change.
-                    mSystemInterface.updateUserSetting(getContext(), newPackage.packageName);
-                    return newPackage.packageName;
-                }
-            } catch (WebViewFactory.MissingWebViewPackageException e) {
-                Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView package "
-                        + e);
-                // If we don't perform the user change but don't have an installed WebView package,
-                // we will have changed the setting and it will be used when a package is available.
-                return newProviderName;
-            }
-            onWebViewProviderChanged(newPackage);
-        }
-        // Kill apps using the old provider
-        if (oldPackage != null) {
-            mSystemInterface.killPackageDependents(oldPackage.packageName);
-        }
-        return newPackage.packageName;
-    }
-
-    /**
-     * This is called when we change WebView provider, either when the current provider is updated
-     * or a new provider is chosen / takes precedence.
-     */
-    private void onWebViewProviderChanged(PackageInfo newPackage) {
-        synchronized(this) {
-            mAnyWebViewInstalled = true;
-            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-                mCurrentWebViewPackage = newPackage;
-                mSystemInterface.updateUserSetting(getContext(), newPackage.packageName);
-
-                // The relro creations might 'finish' (not start at all) before
-                // WebViewFactory.onWebViewProviderChanged which means we might not know the number
-                // of started creations before they finish.
-                mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
-                mNumRelroCreationsFinished = 0;
-                mNumRelroCreationsStarted = mSystemInterface.onWebViewProviderChanged(newPackage);
-                // If the relro creations finish before we know the number of started creations we
-                // will have to do any cleanup/notifying here.
-                checkIfRelrosDoneLocked();
-            } else {
-                mWebViewPackageDirty = true;
-            }
-        }
-    }
-
-    private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
-        WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
-        List<ProviderAndPackageInfo> providers = new ArrayList<>();
-        for(int n = 0; n < allProviders.length; n++) {
-            try {
-                PackageInfo packageInfo =
-                    mSystemInterface.getPackageInfoForProvider(allProviders[n]);
-                if (isValidProvider(allProviders[n], packageInfo)) {
-                    providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
-                }
-            } catch (NameNotFoundException e) {
-                // Don't add non-existent packages
-            }
-        }
-        return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
-    }
-
-    /**
-     * Fetch only the currently valid WebView packages.
-     **/
-    private WebViewProviderInfo[] getValidWebViewPackages() {
-        ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
-        WebViewProviderInfo[] providers = new WebViewProviderInfo[providersAndPackageInfos.length];
-        for(int n = 0; n < providersAndPackageInfos.length; n++) {
-            providers[n] = providersAndPackageInfos[n].provider;
-        }
-        return providers;
-    }
-
-    private class ProviderAndPackageInfo {
-        public final WebViewProviderInfo provider;
-        public final PackageInfo packageInfo;
-
-        public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
-            this.provider = provider;
-            this.packageInfo = packageInfo;
-        }
-    }
-
-    /**
-     * Returns either the package info of the WebView provider determined in the following way:
-     * If the user has chosen a provider then use that if it is valid,
-     * otherwise use the first package in the webview priority list that is valid.
-     *
-     * @hide
-     */
-    private PackageInfo findPreferredWebViewPackage() {
-        ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
-
-        String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(getContext());
-
-        // If the user has chosen provider, use that
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.packageName.equals(userChosenProvider)
-                    && isEnabledPackage(providerAndPackage.packageInfo)) {
-                return providerAndPackage.packageInfo;
-            }
-        }
-
-        // User did not choose, or the choice failed; use the most stable provider that is
-        // enabled and available by default (not through user choice).
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            if (providerAndPackage.provider.availableByDefault
-                    && isEnabledPackage(providerAndPackage.packageInfo)) {
-                return providerAndPackage.packageInfo;
-            }
-        }
-
-        // Could not find any enabled package either, use the most stable provider.
-        for (ProviderAndPackageInfo providerAndPackage : providers) {
-            return providerAndPackage.packageInfo;
-        }
-
-        mAnyWebViewInstalled = false;
-        throw new WebViewFactory.MissingWebViewPackageException(
-                "Could not find a loadable WebView package");
-    }
-
-
-    /**
-     * Returns whether this provider is valid for use as a WebView provider.
-     */
-    public boolean isValidProvider(WebViewProviderInfo configInfo,
-            PackageInfo packageInfo) {
-        if (providerHasValidSignature(configInfo, packageInfo) &&
-                WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
-            return true;
-        }
-        return false;
-    }
-
-    private boolean providerHasValidSignature(WebViewProviderInfo provider,
-            PackageInfo packageInfo) {
-        if (mSystemInterface.systemIsDebuggable()) {
-            return true;
-        }
-        Signature[] packageSignatures;
-        // If no signature is declared, instead check whether the package is included in the
-        // system.
-        if (provider.signatures == null || provider.signatures.length == 0) {
-            return packageInfo.applicationInfo.isSystemApp();
-        }
-        packageSignatures = packageInfo.signatures;
-        if (packageSignatures.length != 1)
-            return false;
-
-        final byte[] packageSignature = packageSignatures[0].toByteArray();
-        // Return whether the package signature matches any of the valid signatures
-        for (String signature : provider.signatures) {
-            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
-            if (Arrays.equals(packageSignature, validSignature))
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns whether the given package is enabled.
-     * This state can be changed by the user from Settings->Apps
-     */
-    public boolean isEnabledPackage(PackageInfo packageInfo) {
-        return packageInfo.applicationInfo.enabled;
+    private static String packageNameFromIntent(Intent intent) {
+        return intent.getDataString().substring("package:".length());
     }
 
     /**
@@ -491,32 +134,6 @@
                 intent.getDataString().substring("package:".length()));
     }
 
-    /**
-     * Returns whether WebView is ready and is not going to go through its preparation phase again
-     * directly.
-     */
-    private boolean webViewIsReadyLocked() {
-        return !mWebViewPackageDirty
-            && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
-            // The current package might be replaced though we haven't received an intent declaring
-            // this yet, the following flag makes anyone loading WebView to wait in this case.
-            && mAnyWebViewInstalled;
-    }
-
-    private void checkIfRelrosDoneLocked() {
-        if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
-            if (mWebViewPackageDirty) {
-                mWebViewPackageDirty = false;
-                // If we have changed provider since we started the relro creation we need to
-                // redo the whole process using the new package instead.
-                PackageInfo newPackage = findPreferredWebViewPackage();
-                onWebViewProviderChanged(newPackage);
-            } else {
-                this.notifyAll();
-            }
-        }
-    }
-
     private class BinderService extends IWebViewUpdateService.Stub {
 
         @Override
@@ -544,10 +161,7 @@
 
             long callingId = Binder.clearCallingIdentity();
             try {
-                synchronized (WebViewUpdateService.this) {
-                    mNumRelroCreationsFinished++;
-                    checkIfRelrosDoneLocked();
-                }
+                WebViewUpdateService.this.mImpl.notifyRelroCreationCompleted();
             } finally {
                 Binder.restoreCallingIdentity(callingId);
             }
@@ -567,34 +181,7 @@
                 throw new IllegalStateException("Cannot create a WebView from the SystemServer");
             }
 
-            PackageInfo webViewPackage = null;
-            final long NS_PER_MS = 1000000;
-            final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
-            boolean webViewReady = false;
-            int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
-            synchronized (WebViewUpdateService.this) {
-                webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
-                while (!webViewReady) {
-                    final long timeNowMs = System.nanoTime() / NS_PER_MS;
-                    if (timeNowMs >= timeoutTimeMs) break;
-                    try {
-                        WebViewUpdateService.this.wait(timeoutTimeMs - timeNowMs);
-                    } catch (InterruptedException e) {}
-                    webViewReady = WebViewUpdateService.this.webViewIsReadyLocked();
-                }
-                // Make sure we return the provider that was used to create the relro file
-                webViewPackage = WebViewUpdateService.this.mCurrentWebViewPackage;
-                if (webViewReady) {
-                } else if (!mAnyWebViewInstalled) {
-                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
-                } else {
-                    // Either the current relro creation  isn't done yet, or the new relro creatioin
-                    // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
-                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
-                }
-            }
-            if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
-            return new WebViewProviderResponse(webViewPackage, webViewStatus);
+            return WebViewUpdateService.this.mImpl.waitForAndGetProvider();
         }
 
         /**
@@ -615,7 +202,8 @@
 
             long callingId = Binder.clearCallingIdentity();
             try {
-                return WebViewUpdateService.this.changeProviderAndSetting(newProvider);
+                return WebViewUpdateService.this.mImpl.changeProviderAndSetting(
+                        newProvider);
             } finally {
                 Binder.restoreCallingIdentity(callingId);
             }
@@ -623,26 +211,22 @@
 
         @Override // Binder call
         public WebViewProviderInfo[] getValidWebViewPackages() {
-            return WebViewUpdateService.this.getValidWebViewPackages();
+            return WebViewUpdateService.this.mImpl.getValidWebViewPackages();
         }
 
         @Override // Binder call
         public WebViewProviderInfo[] getAllWebViewPackages() {
-            return WebViewUpdateService.this.mSystemInterface.getWebViewPackages();
+            return WebViewUpdateService.this.mImpl.getWebViewPackages();
         }
 
         @Override // Binder call
         public String getCurrentWebViewPackageName() {
-            synchronized(WebViewUpdateService.this) {
-                if (WebViewUpdateService.this.mCurrentWebViewPackage == null)
-                    return null;
-                return WebViewUpdateService.this.mCurrentWebViewPackage.packageName;
-            }
+            return WebViewUpdateService.this.mImpl.getCurrentWebViewPackageName();
         }
 
         @Override // Binder call
         public boolean isFallbackPackage(String packageName) {
-            return WebViewUpdateService.this.isFallbackPackage(packageName);
+            return WebViewUpdateService.this.mImpl.isFallbackPackage(packageName);
         }
 
         @Override // Binder call
@@ -658,7 +242,7 @@
                 throw new SecurityException(msg);
             }
 
-            WebViewUpdateService.this.mSystemInterface.enableFallbackLogic(enable);
+            WebViewUpdateService.this.mImpl.enableFallbackLogic(enable);
         }
     }
 }
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
new file mode 100644
index 0000000..32b195b
--- /dev/null
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -0,0 +1,588 @@
+/*
+ * 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.webkit;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
+import android.util.Base64;
+import android.util.Slog;
+import android.webkit.WebViewFactory;
+import android.webkit.WebViewProviderInfo;
+import android.webkit.WebViewProviderResponse;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of the WebViewUpdateService.
+ * This class doesn't depend on the android system like the actual Service does and can be used
+ * directly by tests (as long as they implement a SystemInterface).
+ * @hide
+ */
+public class WebViewUpdateServiceImpl {
+    private static final String TAG = WebViewUpdateServiceImpl.class.getSimpleName();
+
+    private SystemInterface mSystemInterface;
+    private WebViewUpdater mWebViewUpdater;
+    private Context mContext;
+
+    public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
+        mContext = context;
+        mSystemInterface = systemInterface;
+        mWebViewUpdater = new WebViewUpdater(mContext, mSystemInterface);
+    }
+
+    void packageStateChanged(String packageName, int changedState) {
+        updateFallbackStateOnPackageChange(packageName, changedState);
+        mWebViewUpdater.packageStateChanged(packageName, changedState);
+    }
+
+    void prepareWebViewInSystemServer() {
+        updateFallbackStateOnBoot();
+        mWebViewUpdater.prepareWebViewInSystemServer();
+    }
+
+    private boolean existsValidNonFallbackProvider(WebViewProviderInfo[] providers) {
+        for (WebViewProviderInfo provider : providers) {
+            if (provider.availableByDefault && !provider.isFallback) {
+                try {
+                    PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(provider);
+                    if (isEnabledPackage(packageInfo)
+                            && mWebViewUpdater.isValidProvider(provider, packageInfo)) {
+                        return true;
+                    }
+                } catch (NameNotFoundException e) {
+                    // A non-existent provider is neither valid nor enabled
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Called when a new user has been added to update the state of its fallback package.
+     */
+    void handleNewUser(int userId) {
+        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
+        if (fallbackProvider == null) return;
+
+        mSystemInterface.enablePackageForUser(fallbackProvider.packageName,
+                !existsValidNonFallbackProvider(webviewProviders), userId);
+    }
+
+    void notifyRelroCreationCompleted() {
+        mWebViewUpdater.notifyRelroCreationCompleted();
+    }
+
+    WebViewProviderResponse waitForAndGetProvider() {
+        return mWebViewUpdater.waitForAndGetProvider();
+    }
+
+    String changeProviderAndSetting(String newProvider) {
+        return mWebViewUpdater.changeProviderAndSetting(newProvider);
+    }
+
+    WebViewProviderInfo[] getValidWebViewPackages() {
+        return mWebViewUpdater.getValidWebViewPackages();
+    }
+
+    WebViewProviderInfo[] getWebViewPackages() {
+        return mSystemInterface.getWebViewPackages();
+    }
+
+    String getCurrentWebViewPackageName() {
+        return mWebViewUpdater.getCurrentWebViewPackageName();
+    }
+
+    void enableFallbackLogic(boolean enable) {
+        mSystemInterface.enableFallbackLogic(enable);
+    }
+
+    private void updateFallbackStateOnBoot() {
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+        updateFallbackState(webviewProviders, true);
+    }
+
+    /**
+     * Handle the enabled-state of our fallback package, i.e. if there exists some non-fallback
+     * package that is valid (and available by default) then disable the fallback package,
+     * otherwise, enable the fallback package.
+     */
+    private void updateFallbackStateOnPackageChange(String changedPackage, int changedState) {
+        if (!mSystemInterface.isFallbackLogicEnabled()) return;
+
+        WebViewProviderInfo[] webviewProviders = mSystemInterface.getWebViewPackages();
+
+        // A package was changed / updated / downgraded, early out if it is not one of the
+        // webview packages that are available by default.
+        boolean changedPackageAvailableByDefault = false;
+        for (WebViewProviderInfo provider : webviewProviders) {
+            if (provider.packageName.equals(changedPackage)) {
+                if (provider.availableByDefault) {
+                    changedPackageAvailableByDefault = true;
+                }
+                break;
+            }
+        }
+        if (!changedPackageAvailableByDefault) return;
+        updateFallbackState(webviewProviders, false);
+    }
+
+    private void updateFallbackState(WebViewProviderInfo[] webviewProviders, boolean isBoot) {
+        // If there exists a valid and enabled non-fallback package - disable the fallback
+        // package, otherwise, enable it.
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewProviders);
+        if (fallbackProvider == null) return;
+        boolean existsValidNonFallbackProvider = existsValidNonFallbackProvider(webviewProviders);
+
+        boolean isFallbackEnabled = false;
+        try {
+            isFallbackEnabled = isEnabledPackage(
+                    mSystemInterface.getPackageInfoForProvider(fallbackProvider));
+        } catch (NameNotFoundException e) {
+        }
+
+        if (existsValidNonFallbackProvider
+                // During an OTA the primary user's WebView state might differ from other users', so
+                // ignore the state of that user during boot.
+                && (isFallbackEnabled || isBoot)) {
+            mSystemInterface.uninstallAndDisablePackageForAllUsers(mContext,
+                    fallbackProvider.packageName);
+        } else if (!existsValidNonFallbackProvider
+                // During an OTA the primary user's WebView state might differ from other users', so
+                // ignore the state of that user during boot.
+                && (!isFallbackEnabled || isBoot)) {
+            // Enable the fallback package for all users.
+            mSystemInterface.enablePackageForAllUsers(mContext,
+                    fallbackProvider.packageName, true);
+        }
+    }
+
+    /**
+     * Returns the only fallback provider in the set of given packages, or null if there is none.
+     */
+    private static WebViewProviderInfo getFallbackProvider(WebViewProviderInfo[] webviewPackages) {
+        for (WebViewProviderInfo provider : webviewPackages) {
+            if (provider.isFallback) {
+                return provider;
+            }
+        }
+        return null;
+    }
+
+    boolean isFallbackPackage(String packageName) {
+        if (packageName == null || !mSystemInterface.isFallbackLogicEnabled()) return false;
+
+        WebViewProviderInfo[] webviewPackages = mSystemInterface.getWebViewPackages();
+        WebViewProviderInfo fallbackProvider = getFallbackProvider(webviewPackages);
+        return (fallbackProvider != null
+                && packageName.equals(fallbackProvider.packageName));
+    }
+
+    /**
+     * Class that decides what WebView implementation to use and prepares that implementation for
+     * use.
+     */
+    private static class WebViewUpdater {
+        private Context mContext;
+        private SystemInterface mSystemInterface;
+        private int mMinimumVersionCode = -1;
+
+        public WebViewUpdater(Context context, SystemInterface systemInterface) {
+            mContext = context;
+            mSystemInterface = systemInterface;
+        }
+
+        private static final int WAIT_TIMEOUT_MS = 4500; // KEY_DISPATCHING_TIMEOUT is 5000.
+
+        // Keeps track of the number of running relro creations
+        private int mNumRelroCreationsStarted = 0;
+        private int mNumRelroCreationsFinished = 0;
+        // Implies that we need to rerun relro creation because we are using an out-of-date package
+        private boolean mWebViewPackageDirty = false;
+        private boolean mAnyWebViewInstalled = false;
+
+        private int NUMBER_OF_RELROS_UNKNOWN = Integer.MAX_VALUE;
+
+        // The WebView package currently in use (or the one we are preparing).
+        private PackageInfo mCurrentWebViewPackage = null;
+
+        private Object mLock = new Object();
+
+        public void packageStateChanged(String packageName, int changedState) {
+            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+                String webviewPackage = provider.packageName;
+
+                if (webviewPackage.equals(packageName)) {
+                    boolean updateWebView = false;
+                    boolean removedOrChangedOldPackage = false;
+                    String oldProviderName = null;
+                    PackageInfo newPackage = null;
+                    synchronized(mLock) {
+                        try {
+                            newPackage = findPreferredWebViewPackage();
+                            if (mCurrentWebViewPackage != null)
+                                oldProviderName = mCurrentWebViewPackage.packageName;
+                            // Only trigger update actions if the updated package is the one
+                            // that will be used, or the one that was in use before the
+                            // update, or if we haven't seen a valid WebView package before.
+                            updateWebView =
+                                provider.packageName.equals(newPackage.packageName)
+                                || provider.packageName.equals(oldProviderName)
+                                || mCurrentWebViewPackage == null;
+                            // We removed the old package if we received an intent to remove
+                            // or replace the old package.
+                            removedOrChangedOldPackage =
+                                provider.packageName.equals(oldProviderName);
+                            if (updateWebView) {
+                                onWebViewProviderChanged(newPackage);
+                            }
+                        } catch (WebViewFactory.MissingWebViewPackageException e) {
+                            Slog.e(TAG, "Could not find valid WebView package to create " +
+                                    "relro with " + e);
+                        }
+                    }
+                    if(updateWebView && !removedOrChangedOldPackage
+                            && oldProviderName != null) {
+                        // If the provider change is the result of adding or replacing a
+                        // package that was not the previous provider then we must kill
+                        // packages dependent on the old package ourselves. The framework
+                        // only kills dependents of packages that are being removed.
+                        mSystemInterface.killPackageDependents(oldProviderName);
+                    }
+                    return;
+                }
+            }
+        }
+
+        public void prepareWebViewInSystemServer() {
+            try {
+                synchronized(mLock) {
+                    mCurrentWebViewPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(mCurrentWebViewPackage);
+                }
+            } catch (Throwable t) {
+                // Log and discard errors at this stage as we must not crash the system server.
+                Slog.e(TAG, "error preparing webview provider from system server", t);
+            }
+        }
+
+        /**
+         * Change WebView provider and provider setting and kill packages using the old provider.
+         * Return the new provider (in case we are in the middle of creating relro files this new
+         * provider will not be in use directly, but will when the relros are done).
+         */
+        public String changeProviderAndSetting(String newProviderName) {
+            PackageInfo oldPackage = null;
+            PackageInfo newPackage = null;
+            synchronized(mLock) {
+                oldPackage = mCurrentWebViewPackage;
+                mSystemInterface.updateUserSetting(mContext, newProviderName);
+
+                try {
+                    newPackage = findPreferredWebViewPackage();
+                    if (oldPackage != null
+                            && newPackage.packageName.equals(oldPackage.packageName)) {
+                        // If we don't perform the user change, revert the settings change.
+                        mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+                        return newPackage.packageName;
+                    }
+                } catch (WebViewFactory.MissingWebViewPackageException e) {
+                    Slog.e(TAG, "Tried to change WebView provider but failed to fetch WebView " +
+                            "package " + e);
+                    // If we don't perform the user change but don't have an installed WebView
+                    // package, we will have changed the setting and it will be used when a package
+                    // is available.
+                    return newProviderName;
+                }
+                onWebViewProviderChanged(newPackage);
+            }
+            // Kill apps using the old provider
+            if (oldPackage != null) {
+                mSystemInterface.killPackageDependents(oldPackage.packageName);
+            }
+            return newPackage.packageName;
+        }
+
+        /**
+         * This is called when we change WebView provider, either when the current provider is
+         * updated or a new provider is chosen / takes precedence.
+         */
+        private void onWebViewProviderChanged(PackageInfo newPackage) {
+            synchronized(mLock) {
+                mAnyWebViewInstalled = true;
+                if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                    mCurrentWebViewPackage = newPackage;
+                    mSystemInterface.updateUserSetting(mContext, newPackage.packageName);
+
+                    // The relro creations might 'finish' (not start at all) before
+                    // WebViewFactory.onWebViewProviderChanged which means we might not know the
+                    // number of started creations before they finish.
+                    mNumRelroCreationsStarted = NUMBER_OF_RELROS_UNKNOWN;
+                    mNumRelroCreationsFinished = 0;
+                    mNumRelroCreationsStarted =
+                        mSystemInterface.onWebViewProviderChanged(newPackage);
+                    // If the relro creations finish before we know the number of started creations
+                    // we will have to do any cleanup/notifying here.
+                    checkIfRelrosDoneLocked();
+                } else {
+                    mWebViewPackageDirty = true;
+                }
+            }
+        }
+
+        private ProviderAndPackageInfo[] getValidWebViewPackagesAndInfos() {
+            WebViewProviderInfo[] allProviders = mSystemInterface.getWebViewPackages();
+            List<ProviderAndPackageInfo> providers = new ArrayList<>();
+            for(int n = 0; n < allProviders.length; n++) {
+                try {
+                    PackageInfo packageInfo =
+                        mSystemInterface.getPackageInfoForProvider(allProviders[n]);
+                    if (isValidProvider(allProviders[n], packageInfo)) {
+                        providers.add(new ProviderAndPackageInfo(allProviders[n], packageInfo));
+                    }
+                } catch (NameNotFoundException e) {
+                    // Don't add non-existent packages
+                }
+            }
+            return providers.toArray(new ProviderAndPackageInfo[providers.size()]);
+        }
+
+        /**
+         * Fetch only the currently valid WebView packages.
+         **/
+        public WebViewProviderInfo[] getValidWebViewPackages() {
+            ProviderAndPackageInfo[] providersAndPackageInfos = getValidWebViewPackagesAndInfos();
+            WebViewProviderInfo[] providers =
+                new WebViewProviderInfo[providersAndPackageInfos.length];
+            for(int n = 0; n < providersAndPackageInfos.length; n++) {
+                providers[n] = providersAndPackageInfos[n].provider;
+            }
+            return providers;
+        }
+
+
+        private class ProviderAndPackageInfo {
+            public final WebViewProviderInfo provider;
+            public final PackageInfo packageInfo;
+
+            public ProviderAndPackageInfo(WebViewProviderInfo provider, PackageInfo packageInfo) {
+                this.provider = provider;
+                this.packageInfo = packageInfo;
+            }
+        }
+
+        /**
+         * Returns either the package info of the WebView provider determined in the following way:
+         * If the user has chosen a provider then use that if it is valid,
+         * otherwise use the first package in the webview priority list that is valid.
+         *
+         */
+        private PackageInfo findPreferredWebViewPackage() {
+            ProviderAndPackageInfo[] providers = getValidWebViewPackagesAndInfos();
+
+            String userChosenProvider = mSystemInterface.getUserChosenWebViewProvider(mContext);
+
+            // If the user has chosen provider, use that
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                if (providerAndPackage.provider.packageName.equals(userChosenProvider)
+                        && isEnabledPackage(providerAndPackage.packageInfo)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+
+            // User did not choose, or the choice failed; use the most stable provider that is
+            // enabled and available by default (not through user choice).
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                if (providerAndPackage.provider.availableByDefault
+                        && isEnabledPackage(providerAndPackage.packageInfo)) {
+                    return providerAndPackage.packageInfo;
+                }
+            }
+
+            // Could not find any enabled package either, use the most stable provider.
+            for (ProviderAndPackageInfo providerAndPackage : providers) {
+                return providerAndPackage.packageInfo;
+            }
+
+            mAnyWebViewInstalled = false;
+            throw new WebViewFactory.MissingWebViewPackageException(
+                    "Could not find a loadable WebView package");
+        }
+
+        public void notifyRelroCreationCompleted() {
+            synchronized (mLock) {
+                mNumRelroCreationsFinished++;
+                checkIfRelrosDoneLocked();
+            }
+        }
+
+        public WebViewProviderResponse waitForAndGetProvider() {
+            PackageInfo webViewPackage = null;
+            final long NS_PER_MS = 1000000;
+            final long timeoutTimeMs = System.nanoTime() / NS_PER_MS + WAIT_TIMEOUT_MS;
+            boolean webViewReady = false;
+            int webViewStatus = WebViewFactory.LIBLOAD_SUCCESS;
+            synchronized (mLock) {
+                webViewReady = webViewIsReadyLocked();
+                while (!webViewReady) {
+                    final long timeNowMs = System.nanoTime() / NS_PER_MS;
+                    if (timeNowMs >= timeoutTimeMs) break;
+                    try {
+                        mLock.wait(timeoutTimeMs - timeNowMs);
+                    } catch (InterruptedException e) {}
+                    webViewReady = webViewIsReadyLocked();
+                }
+                // Make sure we return the provider that was used to create the relro file
+                webViewPackage = mCurrentWebViewPackage;
+                if (webViewReady) {
+                } else if (!mAnyWebViewInstalled) {
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES;
+                } else {
+                    // Either the current relro creation  isn't done yet, or the new relro creatioin
+                    // hasn't kicked off yet (the last relro creation used an out-of-date WebView).
+                    webViewStatus = WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO;
+                }
+            }
+            if (!webViewReady) Slog.w(TAG, "creating relro file timed out");
+            return new WebViewProviderResponse(webViewPackage, webViewStatus);
+        }
+
+        public String getCurrentWebViewPackageName() {
+            synchronized(mLock) {
+                if (mCurrentWebViewPackage == null)
+                    return null;
+                return mCurrentWebViewPackage.packageName;
+            }
+        }
+
+        /**
+         * Returns whether WebView is ready and is not going to go through its preparation phase
+         * again directly.
+         */
+        private boolean webViewIsReadyLocked() {
+            return !mWebViewPackageDirty
+                && (mNumRelroCreationsStarted == mNumRelroCreationsFinished)
+                // The current package might be replaced though we haven't received an intent
+                // declaring this yet, the following flag makes anyone loading WebView to wait in
+                // this case.
+                && mAnyWebViewInstalled;
+        }
+
+        private void checkIfRelrosDoneLocked() {
+            if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
+                if (mWebViewPackageDirty) {
+                    mWebViewPackageDirty = false;
+                    // If we have changed provider since we started the relro creation we need to
+                    // redo the whole process using the new package instead.
+                    PackageInfo newPackage = findPreferredWebViewPackage();
+                    onWebViewProviderChanged(newPackage);
+                } else {
+                    mLock.notifyAll();
+                }
+            }
+        }
+
+        /**
+         * Returns whether this provider is valid for use as a WebView provider.
+         */
+        public boolean isValidProvider(WebViewProviderInfo configInfo,
+                PackageInfo packageInfo) {
+            if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0
+                    && packageInfo.versionCode < getMinimumVersionCode()
+                    && !mSystemInterface.systemIsDebuggable()) {
+                // Non-system package webview providers may be downgraded arbitrarily low, prevent
+                // that by enforcing minimum version code. This check is only enforced for user
+                // builds.
+                return false;
+            }
+            if (providerHasValidSignature(configInfo, packageInfo, mSystemInterface) &&
+                    WebViewFactory.getWebViewLibrary(packageInfo.applicationInfo) != null) {
+                return true;
+            }
+            return false;
+        }
+
+        /**
+         * Gets the minimum version code allowed for a valid provider. It is the minimum versionCode
+         * of all available-by-default and non-fallback WebView provider packages. If there is no
+         * such WebView provider package on the system, then return -1, which means all positive
+         * versionCode WebView packages are accepted.
+         */
+        private int getMinimumVersionCode() {
+            if (mMinimumVersionCode > 0) {
+                return mMinimumVersionCode;
+            }
+
+            for (WebViewProviderInfo provider : mSystemInterface.getWebViewPackages()) {
+                if (provider.availableByDefault && !provider.isFallback) {
+                    try {
+                        int versionCode =
+                            mSystemInterface.getFactoryPackageVersion(provider.packageName);
+                        if (mMinimumVersionCode < 0 || versionCode < mMinimumVersionCode) {
+                            mMinimumVersionCode = versionCode;
+                        }
+                    } catch (NameNotFoundException e) {
+                        // Safe to ignore.
+                    }
+                }
+            }
+
+            return mMinimumVersionCode;
+        }
+    }
+
+    private static boolean providerHasValidSignature(WebViewProviderInfo provider,
+            PackageInfo packageInfo, SystemInterface systemInterface) {
+        if (systemInterface.systemIsDebuggable()) {
+            return true;
+        }
+        Signature[] packageSignatures;
+        // If no signature is declared, instead check whether the package is included in the
+        // system.
+        if (provider.signatures == null || provider.signatures.length == 0) {
+            return packageInfo.applicationInfo.isSystemApp();
+        }
+        packageSignatures = packageInfo.signatures;
+        if (packageSignatures.length != 1)
+            return false;
+
+        final byte[] packageSignature = packageSignatures[0].toByteArray();
+        // Return whether the package signature matches any of the valid signatures
+        for (String signature : provider.signatures) {
+            final byte[] validSignature = Base64.decode(signature, Base64.DEFAULT);
+            if (Arrays.equals(packageSignature, validSignature))
+                return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns whether the given package is enabled.
+     * This state can be changed by the user from Settings->Apps
+     */
+    private static boolean isEnabledPackage(PackageInfo packageInfo) {
+        return packageInfo.applicationInfo.enabled;
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index bae628a..3ef077b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1214,7 +1214,10 @@
             window.type = windowState.mAttrs.type;
             window.layer = windowState.mLayer;
             window.token = windowState.mClient.asBinder();
-            window.title = windowState.mAttrs.getTitle();
+            window.title = windowState.mAttrs.accessibilityTitle;
+            if (window.title == null) {
+                window.title = windowState.mAttrs.getTitle();
+            }
             window.accessibilityIdOfAnchor = windowState.mAttrs.accessibilityIdOfAnchor;
 
             WindowState attachedWindow = windowState.mAttachedWindow;
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d684278..11b01cc 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -881,7 +881,7 @@
      * when a thumbnail is specified with the pending animation override.
      */
     Animation createThumbnailAspectScaleAnimationLocked(Rect appRect, @Nullable Rect contentInsets,
-            Bitmap thumbnailHeader, final int taskId, int orientation) {
+            Bitmap thumbnailHeader, final int taskId, int uiMode, int orientation) {
         Animation a;
         final int thumbWidthI = thumbnailHeader.getWidth();
         final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
@@ -896,7 +896,7 @@
         final float toY;
         final float pivotX;
         final float pivotY;
-        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+        if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
             fromX = mTmpRect.left;
             fromY = mTmpRect.top;
 
@@ -1028,7 +1028,7 @@
      * activity that is leaving, and the activity that is entering.
      */
     Animation createAspectScaledThumbnailEnterExitAnimationLocked(int thumbTransitState,
-            int orientation, int transit, Rect containingFrame, Rect contentInsets,
+            int uiMode, int orientation, int transit, Rect containingFrame, Rect contentInsets,
             @Nullable Rect surfaceInsets, boolean freeform, int taskId) {
         Animation a;
         final int appWidth = containingFrame.width();
@@ -1067,8 +1067,7 @@
                     mTmpFromClipRect.inset(contentInsets);
                     mNextAppTransitionInsets.set(contentInsets);
 
-                    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
-                        // We scale the width and clip to the top/left square
+                    if (isTvUiMode(uiMode) || orientation == Configuration.ORIENTATION_PORTRAIT) {
                         // We scale the width and clip to the top/left square
                         float scale = thumbWidth /
                                 (appWidth - contentInsets.left - contentInsets.right);
@@ -1401,7 +1400,7 @@
      *                      to the recents thumbnail and hence need to account for the surface being
      *                      bigger.
      */
-    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
+    Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, int uiMode,
             int orientation, Rect frame, Rect displayFrame, Rect insets,
             @Nullable Rect surfaceInsets, boolean isVoiceInteraction, boolean freeform,
             int taskId) {
@@ -1481,7 +1480,7 @@
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_UP);
             a = createAspectScaledThumbnailEnterExitAnimationLocked(
-                    getThumbnailTransitionState(enter), orientation, transit, frame,
+                    getThumbnailTransitionState(enter), uiMode, orientation, transit, frame,
                     insets, surfaceInsets, freeform, taskId);
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
@@ -1922,4 +1921,11 @@
         }
         return prepared;
     }
+
+    /**
+     * @return whether the specified {@param uiMode} is the TV mode.
+     */
+    private boolean isTvUiMode(int uiMode) {
+        return (uiMode & Configuration.UI_MODE_TYPE_TELEVISION) > 0;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6f7e64f..6ac71c7 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -76,8 +76,8 @@
 
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
-    private final int mDividerWindowWidth;
-    private final int mDividerInsets;
+    private int mDividerWindowWidth;
+    private int mDividerInsets;
     private boolean mResizing;
     private WindowState mWindow;
     private final Rect mTmpRect = new Rect();
@@ -105,14 +105,23 @@
         mService = service;
         mDisplayContent = displayContent;
         final Context context = service.mContext;
-        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
-                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(),
                 "DockedStackDim");
         mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
                 context, android.R.interpolator.fast_out_slow_in);
+        loadDimens();
+    }
+
+    private void loadDimens() {
+        final Context context = mService.mContext;
+        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+        mDividerInsets = context.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_insets);
+    }
+
+    void onConfigurationChanged() {
+        loadDimens();
     }
 
     boolean isResizing() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eea0ca0..7b16dbe 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,24 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZEABLE;
-import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_CROP_WINDOWS;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
-import static com.android.server.wm.WindowManagerService.H.SHOW_NON_RESIZEABLE_DOCK_TOAST;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
-import static android.view.WindowManager.DOCKED_TOP;
 
 import android.app.ActivityManager.StackId;
 import android.content.pm.ActivityInfo;
@@ -44,7 +37,6 @@
 import android.view.DisplayInfo;
 import android.view.Surface;
 
-import com.android.internal.R;
 import com.android.server.EventLogTags;
 
 import java.io.PrintWriter;
@@ -94,11 +86,6 @@
     // Resize mode of the task. See {@link ActivityInfo#resizeMode}
     private int mResizeMode;
 
-    // Whether we need to show toast about the app being non-resizeable when it becomes visible.
-    // This flag is set when a non-resizeable task is docked (or side-by-side). It's cleared
-    // after we show the toast.
-    private boolean mShowNonResizeableDockToast;
-
     // Whether the task is currently being drag-resized
     private boolean mDragResizing;
     private int mDragResizeMode;
@@ -118,30 +105,6 @@
         return mStack.getDisplayContent();
     }
 
-    void setShowNonResizeableDockToast() {
-        mShowNonResizeableDockToast = true;
-    }
-
-    void scheduleShowNonResizeableDockToastIfNeeded() {
-        if (!mShowNonResizeableDockToast) {
-            return;
-        }
-        final DisplayContent displayContent = mStack.getDisplayContent();
-        // If docked stack is not yet visible, we don't want to show the toast yet,
-        // since we need the visible rect of the docked task to position the toast.
-        if (displayContent == null || displayContent.getDockedStackLocked() == null) {
-            return;
-        }
-
-        mShowNonResizeableDockToast = false;
-
-        if (mResizeMode == RESIZE_MODE_UNRESIZEABLE) {
-            final String text =
-                    mService.mContext.getString(R.string.dock_non_resizeble_failed_to_dock_text);
-            mService.mH.obtainMessage(SHOW_NON_RESIZEABLE_DOCK_TOAST, 0, 0, text).sendToTarget();
-        }
-    }
-
     void addAppToken(int addPos, AppWindowToken wtoken, int resizeMode, boolean homeTask) {
         final int lastPos = mAppTokens.size();
         if (addPos >= lastPos) {
@@ -595,6 +558,12 @@
                 if (win.mHasSurface && !resizingWindows.contains(win)) {
                     if (DEBUG_RESIZE) Slog.d(TAG, "resizeWindows: Resizing " + win);
                     resizingWindows.add(win);
+
+                    // If we are not drag resizing, force recreating of a new surface so updating
+                    // the content and positioning that surface will be in sync.
+                    if (!win.computeDragResizing()) {
+                        win.mResizedWhileNotDragResizing = true;
+                    }
                 }
                 if (win.isGoneForLayoutLw()) {
                     win.mResizedWhileGone = true;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 0225c9b..446b74b 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -37,8 +37,8 @@
 
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.DENSITY_DPI_UNDEFINED;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
@@ -89,6 +89,9 @@
     // Device rotation as of the last time {@link #mBounds} was set.
     int mRotation;
 
+    /** Density as of last time {@link #mBounds} was set. */
+    int mDensity;
+
     /** Support for non-zero {@link android.view.animation.Animation#getBackgroundColor()} */
     DimLayer mAnimationBackgroundSurface;
 
@@ -250,9 +253,11 @@
     private boolean setBounds(Rect bounds) {
         boolean oldFullscreen = mFullscreen;
         int rotation = Surface.ROTATION_0;
+        int density = DENSITY_DPI_UNDEFINED;
         if (mDisplayContent != null) {
             mDisplayContent.getLogicalDisplayRect(mTmpRect);
             rotation = mDisplayContent.getDisplayInfo().rotation;
+            density = mDisplayContent.getDisplayInfo().logicalDensityDpi;
             mFullscreen = bounds == null;
             if (mFullscreen) {
                 bounds = mTmpRect;
@@ -274,6 +279,7 @@
 
         mBounds.set(bounds);
         mRotation = rotation;
+        mDensity = density;
 
         updateAdjustedBounds();
 
@@ -343,20 +349,21 @@
 
         mTmpRect2.set(mBounds);
         final int newRotation = mDisplayContent.getDisplayInfo().rotation;
-        if (mRotation == newRotation) {
+        final int newDensity = mDisplayContent.getDisplayInfo().logicalDensityDpi;
+        if (mRotation == newRotation && mDensity == newDensity) {
             setBounds(mTmpRect2);
         } else {
             mLastUpdateDisplayInfoRotation = newRotation;
-            updateBoundsAfterRotation(true);
+            updateBoundsAfterConfigChange(true);
         }
     }
 
     boolean onConfigurationChanged() {
         mLastConfigChangedRotation = getDisplayInfo().rotation;
-        return updateBoundsAfterRotation(false);
+        return updateBoundsAfterConfigChange(false);
     }
 
-    boolean updateBoundsAfterRotation(boolean scheduleResize) {
+    boolean updateBoundsAfterConfigChange(boolean scheduleResize) {
         if (mLastConfigChangedRotation != mLastUpdateDisplayInfoRotation) {
             // We wait for the rotation values after configuration change and display info. update
             // to be equal before updating the bounds due to rotation change otherwise things might
@@ -365,8 +372,9 @@
         }
 
         final int newRotation = getDisplayInfo().rotation;
+        final int newDensity = getDisplayInfo().logicalDensityDpi;
 
-        if (mRotation == newRotation) {
+        if (mRotation == newRotation && mDensity == newDensity) {
             // Nothing to do here if the rotation didn't change
             return false;
         }
@@ -712,9 +720,9 @@
                 }
             } else {
                 if (splitHorizontally) {
-                    outBounds.left = position - dockDividerWidth;
+                    outBounds.left = position + dockDividerWidth;
                 } else {
-                    outBounds.top = position - dockDividerWidth;
+                    outBounds.top = position + dockDividerWidth;
                 }
             }
             return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eba7818..57f38d1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2017,7 +2017,13 @@
             final WindowStateAnimator winAnimator = win.mWinAnimator;
             winAnimator.mEnterAnimationPending = true;
             winAnimator.mEnteringAnimation = true;
-            prepareWindowReplacementTransition(atoken);
+            // Check if we need to prepare a transition for replacing window first.
+            if (atoken != null && !prepareWindowReplacementTransition(atoken)) {
+                // If not, check if need to set up a dummy transition during display freeze
+                // so that the unfreeze wait for the apps to draw. This might be needed if
+                // the app is relaunching.
+                prepareNoneTransitionForRelaunching(atoken);
+            }
 
             if (displayContent.isDefaultDisplay) {
                 if (mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets,
@@ -2077,10 +2083,10 @@
         return res;
     }
 
-    private void prepareWindowReplacementTransition(AppWindowToken atoken) {
-        if (atoken == null) {
-            return;
-        }
+    /**
+     * Returns true if we're done setting up any transitions.
+     */
+    private boolean prepareWindowReplacementTransition(AppWindowToken atoken) {
         atoken.allDrawn = false;
         WindowState replacedWindow = null;
         for (int i = atoken.windows.size() - 1; i >= 0 && replacedWindow == null; i--) {
@@ -2093,7 +2099,7 @@
         if (replacedWindow == null) {
             // We expect to already receive a request to remove the old window. If it did not
             // happen, let's just simply add a window.
-            return;
+            return false;
         }
         // We use the visible frame, because we want the animation to morph the window from what
         // was visible to the user to the final destination of the new window.
@@ -2105,6 +2111,19 @@
         mAppTransition.overridePendingAppTransitionClipReveal(frame.left, frame.top,
                 frame.width(), frame.height());
         executeAppTransition();
+        return true;
+    }
+
+    private void prepareNoneTransitionForRelaunching(AppWindowToken atoken) {
+        // Set up a none-transition and add the app to opening apps, so that the display
+        // unfreeze wait for the apps to be drawn.
+        // Note that if the display unfroze already because app unfreeze timed out,
+        // we don't set up the transition anymore and just let it go.
+        if (mDisplayFrozen && !mOpeningApps.contains(atoken) && atoken.isRelaunching()) {
+            mOpeningApps.add(atoken);
+            prepareAppTransition(AppTransition.TRANSIT_NONE, !ALWAYS_KEEP_CURRENT);
+            executeAppTransition();
+        }
     }
 
     /**
@@ -2567,7 +2586,7 @@
 
                     try {
 
-                        win.applyGravityAndUpdateFrame();
+                        win.applyGravityAndUpdateFrame(win.mContainingFrame, win.mDisplayFrame);
                         win.mWinAnimator.computeShownFrameLocked();
 
                         win.mWinAnimator.setSurfaceBoundariesLocked(false);
@@ -2916,8 +2935,9 @@
 
         // If we're starting a drag-resize, we'll be changing the surface size as well as
         // notifying the client to render to with an offset from the surface's top-left.
-        if (win.isDragResizeChanged()) {
+        if (win.isDragResizeChanged() || win.mResizedWhileNotDragResizing) {
             win.setDragResizing();
+            win.mResizedWhileNotDragResizing = false;
             // We can only change top level windows to the full-screen surface when
             // resizing (as we only have one full-screen surface). So there is no need
             // to preserve and destroy windows which are attached to another, they
@@ -3039,7 +3059,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition."
                     + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
                     + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter,
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter, mCurConfiguration.uiMode,
                     mCurConfiguration.orientation, frame, displayFrame, insets, surfaceInsets,
                     isVoiceInteraction, freeform, atoken.mTask.mTaskId);
             if (a != null) {
@@ -3608,6 +3628,8 @@
     }
 
     private int[] onConfigurationChanged() {
+        mPolicy.onConfigurationChanged();
+        getDefaultDisplayContentLocked().getDockedDividerController().onConfigurationChanged();
         mChangedStackList.clear();
         for (int stackNdx = mStackIdToStack.size() - 1; stackNdx >= 0; stackNdx--) {
             final TaskStack stack = mStackIdToStack.valueAt(stackNdx);
@@ -4171,6 +4193,7 @@
                                     WindowManagerPolicy.TRANSIT_EXIT);
                         }
                     }
+                    win.mAnimatingExit = true;
                     changed = true;
                     win.setDisplayLayoutNeeded();
                 }
@@ -4918,17 +4941,6 @@
         }
     }
 
-    public void scheduleShowNonResizeableDockToast(int taskId) {
-        synchronized (mWindowMap) {
-            Task task = mTaskIdToTask.get(taskId);
-            if (task == null) {
-                if (DEBUG_STACK) Slog.i(TAG_WM, "scheduleShowToast: could not find taskId=" + taskId);
-                return;
-            }
-            task.setShowNonResizeableDockToast();
-        }
-    }
-
     @Override
     public void getStackBounds(int stackId, Rect bounds) {
         synchronized (mWindowMap) {
@@ -6013,7 +6025,6 @@
             minLayer = Integer.MAX_VALUE;
         }
 
-        int retryCount = 0;
         WindowState appWin = null;
 
         boolean appIsImTarget;
@@ -6027,193 +6038,172 @@
         final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
                 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
 
-        while (true) {
-            if (retryCount++ > 0) {
-                // Reset max/min layers on retries so we don't accidentally take a screenshot of a
-                // layer based on the previous try.
-                maxLayer = 0;
-                minLayer = Integer.MAX_VALUE;
-                try {
-                    Thread.sleep(100);
-                } catch (InterruptedException e) {
-                }
-            }
-            synchronized(mWindowMap) {
-                // Figure out the part of the screen that is actually the app.
-                appWin = null;
-                final WindowList windows = displayContent.getWindowList();
-                for (int i = windows.size() - 1; i >= 0; i--) {
-                    WindowState ws = windows.get(i);
-                    if (!ws.mHasSurface) {
-                        continue;
-                    }
-                    if (ws.mLayer >= aboveAppLayer) {
-                        continue;
-                    }
-                    if (ws.mIsImWindow) {
-                        if (!appIsImTarget) {
-                            continue;
-                        }
-                    } else if (ws.mIsWallpaper) {
-                        if (appWin == null) {
-                            // We have not ran across the target window yet, so it is probably
-                            // behind the wallpaper. This can happen when the keyguard is up and
-                            // all windows are moved behind the wallpaper. We don't want to
-                            // include the wallpaper layer in the screenshot as it will coverup
-                            // the layer of the target window.
-                            continue;
-                        }
-                        // Fall through. The target window is in front of the wallpaper. For this
-                        // case we want to include the wallpaper layer in the screenshot because
-                        // the target window might have some transparent areas.
-                    } else if (appToken != null) {
-                        if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
-                            // This app window is of no interest if it is not associated with the
-                            // screenshot app.
-                            continue;
-                        }
-                        appWin = ws;
-                    }
-
-                    // Include this window.
-
-                    final WindowStateAnimator winAnim = ws.mWinAnimator;
-                    int layer = winAnim.mSurfaceController.getLayer();
-                    if (maxLayer < layer) {
-                        maxLayer = layer;
-                    }
-                    if (minLayer > layer) {
-                        minLayer = layer;
-                    }
-
-                    // Don't include wallpaper in bounds calculation
-                    if (!includeFullDisplay && !ws.mIsWallpaper) {
-                        final Rect wf = ws.mFrame;
-                        final Rect cr = ws.mContentInsets;
-                        int left = wf.left + cr.left;
-                        int top = wf.top + cr.top;
-                        int right = wf.right - cr.right;
-                        int bottom = wf.bottom - cr.bottom;
-                        frame.union(left, top, right, bottom);
-                        ws.getVisibleBounds(stackBounds);
-                        if (!Rect.intersects(frame, stackBounds)) {
-                            // Set frame empty if there's no intersection.
-                            frame.setEmpty();
-                        }
-                    }
-
-                    if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
-                            ws.isDisplayedLw() && winAnim.getShown()) {
-                        screenshotReady = true;
-                    }
-
-                    if (ws.isObscuringFullscreen(displayInfo)){
-                        break;
-                    }
-                }
-
-                if (appToken != null && appWin == null) {
-                    // Can't find a window to snapshot.
-                    if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
-                            "Screenshot: Couldn't find a surface matching " + appToken);
-                    return null;
-                }
-
-                if (!screenshotReady) {
-                    if (retryCount > MAX_SCREENSHOT_RETRIES) {
-                        Slog.i(TAG_WM, "Screenshot max retries " + retryCount + " of " + appToken +
-                                " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
-                                appWin.mWinAnimator.mDrawState)));
-                        return null;
-                    }
-
-                    // Delay and hope that window gets drawn.
-                    if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot: No image ready for " + appToken
-                            + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
+        synchronized(mWindowMap) {
+            // Figure out the part of the screen that is actually the app.
+            appWin = null;
+            final WindowList windows = displayContent.getWindowList();
+            for (int i = windows.size() - 1; i >= 0; i--) {
+                WindowState ws = windows.get(i);
+                if (!ws.mHasSurface) {
                     continue;
                 }
-
-                // Screenshot is ready to be taken. Everything from here below will continue
-                // through the bottom of the loop and return a value. We only stay in the loop
-                // because we don't want to release the mWindowMap lock until the screenshot is
-                // taken.
-
-                if (maxLayer == 0) {
-                    if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
-                            + ": returning null maxLayer=" + maxLayer);
-                    return null;
+                if (ws.mLayer >= aboveAppLayer) {
+                    continue;
+                }
+                if (ws.mIsImWindow) {
+                    if (!appIsImTarget) {
+                        continue;
+                    }
+                } else if (ws.mIsWallpaper) {
+                    if (appWin == null) {
+                        // We have not ran across the target window yet, so it is probably
+                        // behind the wallpaper. This can happen when the keyguard is up and
+                        // all windows are moved behind the wallpaper. We don't want to
+                        // include the wallpaper layer in the screenshot as it will coverup
+                        // the layer of the target window.
+                        continue;
+                    }
+                    // Fall through. The target window is in front of the wallpaper. For this
+                    // case we want to include the wallpaper layer in the screenshot because
+                    // the target window might have some transparent areas.
+                } else if (appToken != null) {
+                    if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+                        // This app window is of no interest if it is not associated with the
+                        // screenshot app.
+                        continue;
+                    }
+                    appWin = ws;
                 }
 
-                if (!includeFullDisplay) {
-                    // Constrain frame to the screen size.
-                    if (!frame.intersect(0, 0, dw, dh)) {
+                // Include this window.
+
+                final WindowStateAnimator winAnim = ws.mWinAnimator;
+                int layer = winAnim.mSurfaceController.getLayer();
+                if (maxLayer < layer) {
+                    maxLayer = layer;
+                }
+                if (minLayer > layer) {
+                    minLayer = layer;
+                }
+
+                // Don't include wallpaper in bounds calculation
+                if (!includeFullDisplay && !ws.mIsWallpaper) {
+                    final Rect wf = ws.mFrame;
+                    final Rect cr = ws.mContentInsets;
+                    int left = wf.left + cr.left;
+                    int top = wf.top + cr.top;
+                    int right = wf.right - cr.right;
+                    int bottom = wf.bottom - cr.bottom;
+                    frame.union(left, top, right, bottom);
+                    ws.getVisibleBounds(stackBounds);
+                    if (!Rect.intersects(frame, stackBounds)) {
+                        // Set frame empty if there's no intersection.
                         frame.setEmpty();
                     }
-                } else {
-                    // Caller just wants entire display.
-                    frame.set(0, 0, dw, dh);
-                }
-                if (frame.isEmpty()) {
-                    return null;
                 }
 
-                if (width < 0) {
-                    width = (int) (frame.width() * frameScale);
-                }
-                if (height < 0) {
-                    height = (int) (frame.height() * frameScale);
+                if (ws.mAppToken != null && ws.mAppToken.token == appToken &&
+                        ws.isDisplayedLw() && winAnim.getShown()) {
+                    screenshotReady = true;
                 }
 
-                // Tell surface flinger what part of the image to crop. Take the top
-                // right part of the application, and crop the larger dimension to fit.
-                Rect crop = new Rect(frame);
-                if (width / (float) frame.width() < height / (float) frame.height()) {
-                    int cropWidth = (int)((float)width / (float)height * frame.height());
-                    crop.right = crop.left + cropWidth;
-                } else {
-                    int cropHeight = (int)((float)height / (float)width * frame.width());
-                    crop.bottom = crop.top + cropHeight;
-                }
-
-                // The screenshot API does not apply the current screen rotation.
-                int rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
-
-                if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-                    rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
-                }
-
-                // Surfaceflinger is not aware of orientation, so convert our logical
-                // crop to surfaceflinger's portrait orientation.
-                convertCropForSurfaceFlinger(crop, rot, dw, dh);
-
-                if (DEBUG_SCREENSHOT) {
-                    Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
-                            + maxLayer + " appToken=" + appToken);
-                    for (int i = 0; i < windows.size(); i++) {
-                        WindowState win = windows.get(i);
-                        Slog.i(TAG_WM, win + ": " + win.mLayer
-                                + " animLayer=" + win.mWinAnimator.mAnimLayer
-                                + " surfaceLayer=" + win.mWinAnimator.mSurfaceController.getLayer());
-                    }
-                }
-
-                ScreenRotationAnimation screenRotationAnimation =
-                        mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
-                final boolean inRotation = screenRotationAnimation != null &&
-                        screenRotationAnimation.isAnimating();
-                if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
-                        "Taking screenshot while rotating");
-
-                bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
-                        inRotation, rot);
-                if (bm == null) {
-                    Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
-                            + ") to layer " + maxLayer);
-                    return null;
+                if (ws.isObscuringFullscreen(displayInfo)){
+                    break;
                 }
             }
 
-            break;
+            if (appToken != null && appWin == null) {
+                // Can't find a window to snapshot.
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM,
+                        "Screenshot: Couldn't find a surface matching " + appToken);
+                return null;
+            }
+
+            if (!screenshotReady) {
+                Slog.i(TAG_WM, "Failed to capture screenshot of " + appToken +
+                        " appWin=" + (appWin == null ? "null" : (appWin + " drawState=" +
+                        appWin.mWinAnimator.mDrawState)));
+                return null;
+            }
+
+            // Screenshot is ready to be taken. Everything from here below will continue
+            // through the bottom of the loop and return a value. We only stay in the loop
+            // because we don't want to release the mWindowMap lock until the screenshot is
+            // taken.
+
+            if (maxLayer == 0) {
+                if (DEBUG_SCREENSHOT) Slog.i(TAG_WM, "Screenshot of " + appToken
+                        + ": returning null maxLayer=" + maxLayer);
+                return null;
+            }
+
+            if (!includeFullDisplay) {
+                // Constrain frame to the screen size.
+                if (!frame.intersect(0, 0, dw, dh)) {
+                    frame.setEmpty();
+                }
+            } else {
+                // Caller just wants entire display.
+                frame.set(0, 0, dw, dh);
+            }
+            if (frame.isEmpty()) {
+                return null;
+            }
+
+            if (width < 0) {
+                width = (int) (frame.width() * frameScale);
+            }
+            if (height < 0) {
+                height = (int) (frame.height() * frameScale);
+            }
+
+            // Tell surface flinger what part of the image to crop. Take the top
+            // right part of the application, and crop the larger dimension to fit.
+            Rect crop = new Rect(frame);
+            if (width / (float) frame.width() < height / (float) frame.height()) {
+                int cropWidth = (int)((float)width / (float)height * frame.height());
+                crop.right = crop.left + cropWidth;
+            } else {
+                int cropHeight = (int)((float)height / (float)width * frame.width());
+                crop.bottom = crop.top + cropHeight;
+            }
+
+            // The screenshot API does not apply the current screen rotation.
+            int rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+
+            if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+                rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
+            }
+
+            // Surfaceflinger is not aware of orientation, so convert our logical
+            // crop to surfaceflinger's portrait orientation.
+            convertCropForSurfaceFlinger(crop, rot, dw, dh);
+
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG_WM, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+                        + maxLayer + " appToken=" + appToken);
+                for (int i = 0; i < windows.size(); i++) {
+                    WindowState win = windows.get(i);
+                    Slog.i(TAG_WM, win + ": " + win.mLayer
+                            + " animLayer=" + win.mWinAnimator.mAnimLayer
+                            + " surfaceLayer=" + win.mWinAnimator.mSurfaceController.getLayer());
+                }
+            }
+
+            ScreenRotationAnimation screenRotationAnimation =
+                    mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY);
+            final boolean inRotation = screenRotationAnimation != null &&
+                    screenRotationAnimation.isAnimating();
+            if (DEBUG_SCREENSHOT && inRotation) Slog.v(TAG_WM,
+                    "Taking screenshot while rotating");
+
+            bm = SurfaceControl.screenshot(crop, width, height, minLayer, maxLayer,
+                    inRotation, rot);
+            if (bm == null) {
+                Slog.w(TAG_WM, "Screenshot failure taking screenshot for (" + dw + "x" + dh
+                        + ") to layer " + maxLayer);
+                return null;
+            }
         }
 
         if (DEBUG_SCREENSHOT) {
@@ -7596,6 +7586,11 @@
                    + " milliseconds before attempting to detect safe mode.");
         }
 
+        if (Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.SAFE_BOOT_DISALLOWED, 0) != 0) {
+            return false;
+        }
+
         int menuState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY,
                 KeyEvent.KEYCODE_MENU);
         int sState = mInputManager.getKeyCodeState(-1, InputDevice.SOURCE_ANY, KeyEvent.KEYCODE_S);
@@ -7608,7 +7603,8 @@
         mSafeMode = menuState > 0 || sState > 0 || dpadState > 0 || trackballState > 0
                 || volumeDownState > 0;
         try {
-            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0) {
+            if (SystemProperties.getInt(ShutdownThread.REBOOT_SAFEMODE_PROPERTY, 0) != 0
+                    || SystemProperties.getInt(ShutdownThread.RO_SAFEMODE_PROPERTY, 0) != 0) {
                 int auditSafeMode = SystemProperties.getInt(ShutdownThread.AUDIT_SAFEMODE_PROPERTY, 0);
 
                 if (auditSafeMode == 0) {
@@ -7632,6 +7628,7 @@
         if (mSafeMode) {
             Log.i(TAG_WM, "SAFE MODE ENABLED (menu=" + menuState + " s=" + sState
                     + " dpad=" + dpadState + " trackball=" + trackballState + ")");
+            SystemProperties.set(ShutdownThread.RO_SAFEMODE_PROPERTY, "1");
         } else {
             Log.i(TAG_WM, "SAFE MODE not enabled");
         }
@@ -7743,7 +7740,6 @@
         public static final int RESIZE_TASK = 43;
 
         public static final int TWO_FINGER_SCROLL_START = 44;
-        public static final int SHOW_NON_RESIZEABLE_DOCK_TOAST = 45;
 
         public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
 
@@ -8311,16 +8307,6 @@
                     }
                 }
                 break;
-                case SHOW_NON_RESIZEABLE_DOCK_TOAST: {
-                    final Toast toast = Toast.makeText(
-                            mContext, (String) msg.obj, Toast.LENGTH_SHORT);
-                    final int gravity = toast.getGravity();
-                    final int xOffset = toast.getXOffset() + msg.arg1;
-                    final int yOffset = toast.getYOffset() + msg.arg2;
-                    toast.setGravity(gravity, xOffset, yOffset);
-                    toast.show();
-                }
-                break;
                 case WINDOW_REPLACEMENT_TIMEOUT: {
                     final AppWindowToken token = (AppWindowToken) msg.obj;
                     synchronized (mWindowMap) {
@@ -8997,7 +8983,8 @@
                     || winAnimator.mSurfaceResized
                     || w.mOutsetsChanged
                     || configChanged
-                    || dragResizingChanged) {
+                    || dragResizingChanged
+                    || w.mResizedWhileNotDragResizing) {
                 if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
                     Slog.v(TAG_WM, "Resize reasons for w=" + w + ": "
                             + " contentInsetsChanged=" + w.mContentInsetsChanged
@@ -9031,7 +9018,8 @@
                 // the display until this window has been redrawn; to do that,
                 // we need to go through the process of getting informed by the
                 // application when it has finished drawing.
-                if (w.mOrientationChanging || dragResizingChanged) {
+                if (w.mOrientationChanging || dragResizingChanged
+                        || w.mResizedWhileNotDragResizing) {
                     if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
                         Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
                                 + ", mDrawState=DRAW_PENDING in " + w
@@ -9391,6 +9379,10 @@
             return;
         }
 
+        if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
+                "startFreezingDisplayLocked: inTransaction=" + inTransaction
+                + " exitAnim=" + exitAnim + " enterAnim=" + enterAnim
+                + " called by " + Debug.getCallers(8));
         mScreenFrozenLock.acquire();
 
         mDisplayFrozen = true;
@@ -9461,6 +9453,9 @@
             return;
         }
 
+        if (DEBUG_ORIENTATION) Slog.d(TAG_WM,
+                "stopFreezingDisplayLocked: Unfreezing now");
+
         mDisplayFrozen = false;
         mLastDisplayFreezeDuration = (int)(SystemClock.elapsedRealtime() - mDisplayFreezeTime);
         StringBuilder sb = new StringBuilder(128);
@@ -10625,7 +10620,10 @@
     @Override
     public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {
         try {
-            getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+            WindowState focusedWindow = getFocusedWindow();
+            if (focusedWindow != null && focusedWindow.mClient != null) {
+                getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver, deviceId);
+            }
         } catch (RemoteException e) {
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c62292d..7f40079 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -328,6 +328,8 @@
      */
     final Rect mInsetFrame = new Rect();
 
+    private static final Rect sTmpRect = new Rect();
+
     boolean mContentChanged;
 
     // If a window showing a wallpaper: the requested offset for the
@@ -466,6 +468,13 @@
      */
     boolean mResizedWhileGone = false;
 
+    /**
+     * Indicates whether we got resized but drag resizing flag was false. In this case, we also
+     * need to recreate the surface and defer surface bound updates in order to make sure the
+     * buffer contents and the positioning/size stay in sync.
+     */
+    boolean mResizedWhileNotDragResizing;
+
     WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
            WindowState attachedWindow, int appOp, int seq, WindowManager.LayoutParams a,
            int viewVisibility, final DisplayContent displayContent) {
@@ -629,6 +638,20 @@
         return mAttrs.packageName;
     }
 
+    /**
+     * Subtracts the insets calculated by intersecting {@param layoutFrame} with {@param insetFrame}
+     * from {@param frame}. In other words, it applies the insets that would result if
+     * {@param frame} would be shifted to {@param layoutFrame} and then applying the insets from
+     * {@param insetFrame}.
+     */
+    private void subtractInsets(Rect frame, Rect layoutFrame, Rect insetFrame) {
+        final int left = Math.max(0, insetFrame.left - layoutFrame.left);
+        final int top = Math.max(0, insetFrame.top - layoutFrame.top);
+        final int right = Math.max(0, layoutFrame.right - insetFrame.right);
+        final int bottom = Math.max(0, layoutFrame.bottom - insetFrame.bottom);
+        frame.inset(left, top, right, bottom);
+    }
+
     @Override
     public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf, Rect dcf, Rect sf,
             Rect osf) {
@@ -654,11 +677,25 @@
             task.getTempInsetBounds(mInsetFrame);
         }
 
+        // Denotes the actual frame used to calculate the insets and to perform the layout. When
+        // resizing in docked mode, we'd like to freeze the layout, so we also need to freeze the
+        // insets temporarily. By the notion of a task having a different layout frame, we can
+        // achieve that while still moving the task around.
+        final Rect layoutContainingFrame;
+        final Rect layoutDisplayFrame;
+
+        // The offset from the layout containing frame to the actual containing frame.
+        final int layoutXDiff;
+        final int layoutYDiff;
         if (mInsetFrame.isEmpty()  && (fullscreenTask
                 || layoutInParentFrame())) {
             // We use the parent frame as the containing frame for fullscreen and child windows
             mContainingFrame.set(pf);
             mDisplayFrame.set(df);
+            layoutDisplayFrame = df;
+            layoutContainingFrame = pf;
+            layoutXDiff = 0;
+            layoutYDiff = 0;
         } else {
             task.getBounds(mContainingFrame);
             if (mAppToken != null && !mAppToken.mFrozenBounds.isEmpty()) {
@@ -693,6 +730,16 @@
                 }
             }
             mDisplayFrame.set(mContainingFrame);
+            layoutXDiff = !mInsetFrame.isEmpty() ? mInsetFrame.left - mContainingFrame.left : 0;
+            layoutYDiff = !mInsetFrame.isEmpty() ? mInsetFrame.top - mContainingFrame.top : 0;
+            layoutContainingFrame = !mInsetFrame.isEmpty() ? mInsetFrame : mContainingFrame;
+            subtractInsets(mDisplayFrame, layoutContainingFrame, df);
+            if (!layoutInParentFrame()) {
+                subtractInsets(mContainingFrame, layoutContainingFrame, pf);
+                subtractInsets(mInsetFrame, layoutContainingFrame, pf);
+            }
+            layoutDisplayFrame = df;
+            layoutDisplayFrame.intersect(layoutContainingFrame);
         }
 
         final int pw = mContainingFrame.width();
@@ -723,7 +770,11 @@
         final int fw = mFrame.width();
         final int fh = mFrame.height();
 
-        applyGravityAndUpdateFrame();
+        applyGravityAndUpdateFrame(layoutContainingFrame, layoutDisplayFrame);
+
+        // Offset the actual frame by the amount layout frame is off.
+        mFrame.offset(-layoutXDiff, -layoutYDiff);
+        mCompatFrame.offset(-layoutXDiff, -layoutYDiff);
 
         // Calculate the outsets before the content frame gets shrinked to the window frame.
         if (hasOutsets) {
@@ -735,12 +786,6 @@
             mOutsets.set(0, 0, 0, 0);
         }
 
-        // Denotes the actual frame used to calculate the insets. When resizing in docked mode,
-        // we'd like to freeze the layout, so we also need to freeze the insets temporarily. By the
-        // notion of a task having a different inset frame, we can achieve that while still moving
-        // the task around.
-        final Rect frame = !mInsetFrame.isEmpty() ? mInsetFrame : mFrame;
-
         // Make sure the content and visible frames are inside of the
         // final window frame.
         if (windowsAreFloating && !mFrame.isEmpty()) {
@@ -769,29 +814,29 @@
                 mMovedByResize = true;
             }
         } else {
-            mContentFrame.set(Math.max(mContentFrame.left, frame.left),
-                    Math.max(mContentFrame.top, frame.top),
-                    Math.min(mContentFrame.right, frame.right),
-                    Math.min(mContentFrame.bottom, frame.bottom));
+            mContentFrame.set(Math.max(mContentFrame.left, layoutContainingFrame.left),
+                    Math.max(mContentFrame.top, layoutContainingFrame.top),
+                    Math.min(mContentFrame.right, layoutContainingFrame.right),
+                    Math.min(mContentFrame.bottom, layoutContainingFrame.bottom));
 
-            mVisibleFrame.set(Math.max(mVisibleFrame.left, frame.left),
-                    Math.max(mVisibleFrame.top, frame.top),
-                    Math.min(mVisibleFrame.right, frame.right),
-                    Math.min(mVisibleFrame.bottom, frame.bottom));
+            mVisibleFrame.set(Math.max(mVisibleFrame.left, layoutContainingFrame.left),
+                    Math.max(mVisibleFrame.top, layoutContainingFrame.top),
+                    Math.min(mVisibleFrame.right, layoutContainingFrame.right),
+                    Math.min(mVisibleFrame.bottom, layoutContainingFrame.bottom));
 
-            mStableFrame.set(Math.max(mStableFrame.left, frame.left),
-                    Math.max(mStableFrame.top, frame.top),
-                    Math.min(mStableFrame.right, frame.right),
-                    Math.min(mStableFrame.bottom, frame.bottom));
+            mStableFrame.set(Math.max(mStableFrame.left, layoutContainingFrame.left),
+                    Math.max(mStableFrame.top, layoutContainingFrame.top),
+                    Math.min(mStableFrame.right, layoutContainingFrame.right),
+                    Math.min(mStableFrame.bottom, layoutContainingFrame.bottom));
         }
 
         if (fullscreenTask && !windowsAreFloating) {
             // Windows that are not fullscreen can be positioned outside of the display frame,
             // but that is not a reason to provide them with overscan insets.
-            mOverscanInsets.set(Math.max(mOverscanFrame.left - frame.left, 0),
-                    Math.max(mOverscanFrame.top - frame.top, 0),
-                    Math.max(frame.right - mOverscanFrame.right, 0),
-                    Math.max(frame.bottom - mOverscanFrame.bottom, 0));
+            mOverscanInsets.set(Math.max(mOverscanFrame.left - layoutContainingFrame.left, 0),
+                    Math.max(mOverscanFrame.top - layoutContainingFrame.top, 0),
+                    Math.max(layoutContainingFrame.right - mOverscanFrame.right, 0),
+                    Math.max(layoutContainingFrame.bottom - mOverscanFrame.bottom, 0));
         }
 
         if (mAttrs.type == TYPE_DOCK_DIVIDER) {
@@ -812,45 +857,32 @@
             // non-fullscreen mode.
             boolean overrideRightInset = !fullscreenTask && mFrame.right > mTmpRect.right;
             boolean overrideBottomInset = !fullscreenTask && mFrame.bottom > mTmpRect.bottom;
-            mContentInsets.set(mContentFrame.left - frame.left,
-                    mContentFrame.top - frame.top,
+            mContentInsets.set(mContentFrame.left - layoutContainingFrame.left,
+                    mContentFrame.top - layoutContainingFrame.top,
                     overrideRightInset ? mTmpRect.right - mContentFrame.right
-                            : frame.right - mContentFrame.right,
+                            : layoutContainingFrame.right - mContentFrame.right,
                     overrideBottomInset ? mTmpRect.bottom - mContentFrame.bottom
-                            : frame.bottom - mContentFrame.bottom);
+                            : layoutContainingFrame.bottom - mContentFrame.bottom);
 
-            mVisibleInsets.set(mVisibleFrame.left - frame.left,
-                    mVisibleFrame.top - frame.top,
+            mVisibleInsets.set(mVisibleFrame.left - layoutContainingFrame.left,
+                    mVisibleFrame.top - layoutContainingFrame.top,
                     overrideRightInset ? mTmpRect.right - mVisibleFrame.right
-                            : frame.right - mVisibleFrame.right,
+                            : layoutContainingFrame.right - mVisibleFrame.right,
                     overrideBottomInset ? mTmpRect.bottom - mVisibleFrame.bottom
-                            : frame.bottom - mVisibleFrame.bottom);
+                            : layoutContainingFrame.bottom - mVisibleFrame.bottom);
 
-            mStableInsets.set(Math.max(mStableFrame.left - frame.left, 0),
-                    Math.max(mStableFrame.top - frame.top, 0),
+            mStableInsets.set(Math.max(mStableFrame.left - layoutContainingFrame.left, 0),
+                    Math.max(mStableFrame.top - layoutContainingFrame.top, 0),
                     overrideRightInset ? Math.max(mTmpRect.right - mStableFrame.right, 0)
-                            : Math.max(frame.right - mStableFrame.right, 0),
+                            : Math.max(layoutContainingFrame.right - mStableFrame.right, 0),
                     overrideBottomInset ? Math.max(mTmpRect.bottom - mStableFrame.bottom, 0)
-                            :  Math.max(frame.bottom - mStableFrame.bottom, 0));
+                            :  Math.max(layoutContainingFrame.bottom - mStableFrame.bottom, 0));
         }
 
-        if (!mInsetFrame.isEmpty()) {
-            mContentFrame.set(mFrame);
-            mContentFrame.top += mContentInsets.top;
-            mContentFrame.bottom -= mContentInsets.bottom;
-            mContentFrame.left += mContentInsets.left;
-            mContentFrame.right -= mContentInsets.right;
-            mVisibleFrame.set(mFrame);
-            mVisibleFrame.top += mVisibleInsets.top;
-            mVisibleFrame.bottom -= mVisibleInsets.bottom;
-            mVisibleFrame.left += mVisibleInsets.left;
-            mVisibleFrame.right -= mVisibleInsets.right;
-            mStableFrame.set(mFrame);
-            mStableFrame.top += mStableInsets.top;
-            mStableFrame.bottom -= mStableInsets.bottom;
-            mStableFrame.left += mStableInsets.left;
-            mStableFrame.right -= mStableInsets.right;
-        }
+        mContentFrame.offset(-layoutXDiff, -layoutYDiff);
+        mVisibleFrame.offset(-layoutXDiff, -layoutYDiff);
+        mStableFrame.offset(-layoutXDiff, -layoutYDiff);
+
         mCompatFrame.set(mFrame);
         if (mEnforceSizeCompat) {
             // If there is a size compatibility scale being applied to the
@@ -2323,6 +2355,10 @@
         return mDragResizing;
     }
 
+    boolean isDockedResizing() {
+        return mDragResizing && getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+    }
+
     void dump(PrintWriter pw, String prefix, boolean dumpAll) {
         final TaskStack stack = getStack();
         pw.print(prefix); pw.print("mDisplayId="); pw.print(getDisplayId());
@@ -2549,9 +2585,9 @@
         }
     }
 
-    void applyGravityAndUpdateFrame() {
-        final int pw = mContainingFrame.width();
-        final int ph = mContainingFrame.height();
+    void applyGravityAndUpdateFrame(Rect containingFrame, Rect displayFrame) {
+        final int pw = containingFrame.width();
+        final int ph = containingFrame.height();
         final Task task = getTask();
         final boolean nonFullscreenTask = isInMultiWindowMode();
         final boolean fitToDisplay = task != null && !task.isFloating() && !layoutInParentFrame();
@@ -2606,13 +2642,13 @@
         }
 
         // Set mFrame
-        Gravity.apply(mAttrs.gravity, w, h, mContainingFrame,
+        Gravity.apply(mAttrs.gravity, w, h, containingFrame,
                 (int) (x + mAttrs.horizontalMargin * pw),
                 (int) (y + mAttrs.verticalMargin * ph), mFrame);
 
         // Now make sure the window fits in the overall display frame.
         if (fitToDisplay) {
-            Gravity.applyDisplay(mAttrs.gravity, mDisplayFrame, mFrame);
+            Gravity.applyDisplay(mAttrs.gravity, displayFrame, mFrame);
         }
 
         // We need to make sure we update the CompatFrame as it is used for
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 8fd8bc0..9c25f63 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1125,13 +1125,21 @@
         final int top = w.mYOffset + w.mFrame.top;
 
         // Initialize the decor rect to the entire frame.
-        if (w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_DOCKED_DIVIDER) {
+        if (w.isDockedResizing() ||
+                (w.isChildWindow() && w.mAttachedWindow.isDockedResizing())) {
 
             // If we are resizing with the divider, the task bounds might be smaller than the
             // stack bounds. The system decor is used to clip to the task bounds, which we don't
             // want in this case in order to avoid holes.
+            //
+            // We take care to not shrink the width, for surfaces which are larger than
+            // the display region. Of course this area will not eventually be visible
+            // but if we truncate the width now, we will calculate incorrectly
+            // when adjusting to the stack bounds.
             final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo();
-            mSystemDecorRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
+            mSystemDecorRect.set(0, 0,
+                    Math.max(width, displayInfo.logicalWidth),
+                    Math.max(height, displayInfo.logicalHeight));
         } else {
             mSystemDecorRect.set(0, 0, width, height);
         }
@@ -1318,6 +1326,11 @@
         final WindowState w = mWin;
         final Task task = w.getTask();
 
+        // We got resized, so block all updates until we got the new surface.
+        if (w.mResizedWhileNotDragResizing) {
+            return;
+        }
+
         mTmpSize.set(w.mShownPosition.x, w.mShownPosition.y, 0, 0);
         calculateSurfaceBounds(w, w.getAttrs());
 
@@ -1478,11 +1491,6 @@
                 }
             }
             w.mToken.hasVisible = true;
-
-            final Task task = w.getTask();
-            if (task != null) {
-                task.scheduleShowNonResizeableDockToastIfNeeded();
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index eda2f39..04aa735 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -1582,7 +1582,8 @@
                 // synchronize its thumbnail surface with the surface for the
                 // open/close animation (only on the way down)
                 anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
-                        insets, thumbnailHeader, taskId, mService.mCurConfiguration.orientation);
+                        insets, thumbnailHeader, taskId, mService.mCurConfiguration.uiMode,
+                        mService.mCurConfiguration.orientation);
                 openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
                 openingAppAnimator.deferThumbnailDestruction =
                         !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index a5237ca..cd485c5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1312,6 +1312,12 @@
     }
 }
 
+static void nativeToggleCapsLock(JNIEnv* env, jclass /* clazz */,
+         jlong ptr, jint deviceId) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->getInputManager()->getReader()->toggleCapsLockState(deviceId);
+}
+
 static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
         jlong ptr, jobjectArray windowHandleObjArray) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1508,6 +1514,8 @@
             (void*) nativeSetInputFilterEnabled },
     { "nativeInjectInputEvent", "(JLandroid/view/InputEvent;IIIIII)I",
             (void*) nativeInjectInputEvent },
+    { "nativeToggleCapsLock", "(JI)V",
+            (void*) nativeToggleCapsLock },
     { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
             (void*) nativeSetInputWindows },
     { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index ae05042..78b0844 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1112,7 +1112,7 @@
     JavaObject object(env, "android/location/GnssClock");
     GpsClockFlags flags = clock->flags;
 
-    SET_IF(GNSS_CLOCK_HAS_LEAP_SECOND,
+    SET_IF(GPS_CLOCK_HAS_LEAP_SECOND,
            LeapSecond,
            static_cast<int32_t>(clock->leap_second));
 
@@ -1121,8 +1121,9 @@
     // old GPS_CLOCK types (active only in a limited number of older devices),
     // the GPS time information is handled as an always discontinuous HW clock,
     // with the GPS time information put into the full_bias_ns instead - so that
-    // time_ns + full_bias_ns = local estimate of GPS time (as remains true, in
-    // the new GnssClock struct.)
+    // time_ns - full_bias_ns = local estimate of GPS time. Additionally, the
+    // sign of full_bias_ns and bias_ns has flipped between GpsClock &
+    // GnssClock, so that is also handled below.
     switch (clock->type) {
       case GPS_CLOCK_TYPE_UNKNOWN:
         // Clock type unsupported.
@@ -1133,7 +1134,7 @@
         break;
       case GPS_CLOCK_TYPE_GPS_TIME:
         // GPS time, need to convert.
-        flags |= GNSS_CLOCK_HAS_FULL_BIAS;
+        flags |= GPS_CLOCK_HAS_FULL_BIAS;
         clock->full_bias_ns = clock->time_ns;
         clock->time_ns = 0;
         SET(HardwareClockDiscontinuityCount,
@@ -1142,16 +1143,20 @@
     }
 
     SET(TimeNanos, clock->time_ns);
-    SET_IF(GNSS_CLOCK_HAS_TIME_UNCERTAINTY,
+    SET_IF(GPS_CLOCK_HAS_TIME_UNCERTAINTY,
            TimeUncertaintyNanos,
            clock->time_uncertainty_ns);
-    SET_IF(GNSS_CLOCK_HAS_FULL_BIAS, FullBiasNanos, clock->full_bias_ns);
-    SET_IF(GNSS_CLOCK_HAS_BIAS, BiasNanos, clock->bias_ns);
-    SET_IF(GNSS_CLOCK_HAS_BIAS_UNCERTAINTY,
+
+    // Definition of sign for full_bias_ns & bias_ns has been changed since N,
+    // so flip signs here.
+    SET_IF(GPS_CLOCK_HAS_FULL_BIAS, FullBiasNanos, -(clock->full_bias_ns));
+    SET_IF(GPS_CLOCK_HAS_BIAS, BiasNanos, -(clock->bias_ns));
+
+    SET_IF(GPS_CLOCK_HAS_BIAS_UNCERTAINTY,
            BiasUncertaintyNanos,
            clock->bias_uncertainty_ns);
-    SET_IF(GNSS_CLOCK_HAS_DRIFT, DriftNanosPerSecond, clock->drift_nsps);
-    SET_IF(GNSS_CLOCK_HAS_DRIFT_UNCERTAINTY,
+    SET_IF(GPS_CLOCK_HAS_DRIFT, DriftNanosPerSecond, clock->drift_nsps);
+    SET_IF(GPS_CLOCK_HAS_DRIFT_UNCERTAINTY,
            DriftUncertaintyNanosPerSecond,
            clock->drift_uncertainty_nsps);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0d4887d..72eebb5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -1131,9 +1131,7 @@
                 }
                 String tagDAM = parser.getName();
                 if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) {
-                    PersistableBundle bundle = new PersistableBundle();
-                    bundle.restoreFromXml(parser);
-                    result.options = bundle;
+                    result.options = PersistableBundle.restoreFromXml(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag under " + tag +  ": " + tagDAM);
                 }
@@ -3892,9 +3890,6 @@
         // back in to the service.
         final long ident = mInjector.binderClearCallingIdentity();
         try {
-            if (isManagedProfile(userHandle)) {
-                mLockPatternUtils.setSeparateProfileChallengeEnabled(userHandle, true);
-            }
             if (!TextUtils.isEmpty(password)) {
                 mLockPatternUtils.saveLockPassword(password, null, quality, userHandle);
             } else {
@@ -3983,6 +3978,15 @@
                         && timeMs > admin.maximumTimeToUnlock) {
                     timeMs = admin.maximumTimeToUnlock;
                 }
+                // If userInfo.id is a managed profile, we also need to look at
+                // the policies set on the parent.
+                if (admin.hasParentActiveAdmin()) {
+                    final ActiveAdmin parentAdmin = admin.getParentActiveAdmin();
+                    if (parentAdmin.maximumTimeToUnlock > 0
+                            && timeMs > parentAdmin.maximumTimeToUnlock) {
+                        timeMs = parentAdmin.maximumTimeToUnlock;
+                    }
+                }
             }
         }
 
@@ -4016,30 +4020,57 @@
         }
         enforceFullCrossUsersPermission(userHandle);
         synchronized (this) {
-            long time = 0;
-
             if (who != null) {
                 ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle, parent);
-                return admin != null ? admin.maximumTimeToUnlock : time;
+                return admin != null ? admin.maximumTimeToUnlock : 0;
             }
-
             // Return the strictest policy across all participating admins.
             List<ActiveAdmin> admins = getActiveAdminsForLockscreenPoliciesLocked(
                     userHandle, parent);
-            final int N = admins.size();
-            for (int i = 0; i < N; i++) {
-                ActiveAdmin admin = admins.get(i);
-                if (time == 0) {
-                    time = admin.maximumTimeToUnlock;
-                } else if (admin.maximumTimeToUnlock != 0
-                        && time > admin.maximumTimeToUnlock) {
-                    time = admin.maximumTimeToUnlock;
+            return getMaximumTimeToLockPolicyFromAdmins(admins);
+        }
+    }
+
+    @Override
+    public long getMaximumTimeToLockForUserAndProfiles(int userHandle) {
+        if (!mHasFeature) {
+            return 0;
+        }
+        enforceFullCrossUsersPermission(userHandle);
+        synchronized (this) {
+            // All admins for this user.
+            ArrayList<ActiveAdmin> admins = new ArrayList<ActiveAdmin>();
+            for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
+                DevicePolicyData policy = getUserData(userInfo.id);
+                admins.addAll(policy.mAdminList);
+                // If it is a managed profile, it may have parent active admins
+                if (userInfo.isManagedProfile()) {
+                    for (ActiveAdmin admin : policy.mAdminList) {
+                        if (admin.hasParentActiveAdmin()) {
+                            admins.add(admin.getParentActiveAdmin());
+                        }
+                    }
                 }
             }
-            return time;
+            return getMaximumTimeToLockPolicyFromAdmins(admins);
         }
     }
 
+    private long getMaximumTimeToLockPolicyFromAdmins(List<ActiveAdmin> admins) {
+        long time = 0;
+        final int N = admins.size();
+        for (int i = 0; i < N; i++) {
+            ActiveAdmin admin = admins.get(i);
+            if (time == 0) {
+                time = admin.maximumTimeToUnlock;
+            } else if (admin.maximumTimeToUnlock != 0
+                    && time > admin.maximumTimeToUnlock) {
+                time = admin.maximumTimeToUnlock;
+            }
+        }
+        return time;
+    }
+
     @Override
     public void lockNow(boolean parent) {
         if (!mHasFeature) {
@@ -4357,6 +4388,13 @@
         }
     }
 
+    /**
+     * @return {@code true} if the package is installed and set as always-on, {@code false} if it is
+     * not installed and therefore not available.
+     *
+     * @throws SecurityException if the caller is not a profile or device owner.
+     * @throws UnsupportedException if the package does not support being set as always-on.
+     */
     @Override
     public boolean setAlwaysOnVpnPackage(ComponentName admin, String vpnPackage)
             throws SecurityException {
@@ -4366,13 +4404,19 @@
 
         final int userId = mInjector.userHandleGetCallingUserId();
         final long token = mInjector.binderClearCallingIdentity();
-        try{
+        try {
+            if (vpnPackage != null && !isPackageInstalledForUser(vpnPackage, userId)) {
+                return false;
+            }
             ConnectivityManager connectivityManager = (ConnectivityManager)
                     mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
-            return connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage);
+            if (!connectivityManager.setAlwaysOnVpnPackageForUser(userId, vpnPackage)) {
+                throw new UnsupportedOperationException();
+            }
         } finally {
             mInjector.binderRestoreCallingIdentity(token);
         }
+        return true;
     }
 
     @Override
@@ -6395,17 +6439,16 @@
 
     @Override
     public void setTrustAgentConfiguration(ComponentName admin, ComponentName agent,
-            PersistableBundle args) {
+            PersistableBundle args, boolean parent) {
         if (!mHasFeature) {
             return;
         }
         Preconditions.checkNotNull(admin, "admin is null");
         Preconditions.checkNotNull(agent, "agent is null");
         final int userHandle = UserHandle.getCallingUserId();
-        enforceNotManagedProfile(userHandle, "set trust agent configuration");
         synchronized (this) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
-                    DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+                    DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
             ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
             saveSettingsLocked(userHandle);
         }
@@ -6413,7 +6456,7 @@
 
     @Override
     public List<PersistableBundle> getTrustAgentConfiguration(ComponentName admin,
-            ComponentName agent, int userHandle) {
+            ComponentName agent, int userHandle, boolean parent) {
         if (!mHasFeature) {
             return null;
         }
@@ -6423,46 +6466,44 @@
         synchronized (this) {
             final String componentName = agent.flattenToString();
             if (admin != null) {
-                final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle);
+                final ActiveAdmin ap = getActiveAdminUncheckedLocked(admin, userHandle, parent);
                 if (ap == null) return null;
                 TrustAgentInfo trustAgentInfo = ap.trustAgentInfos.get(componentName);
                 if (trustAgentInfo == null || trustAgentInfo.options == null) return null;
-                List<PersistableBundle> result = new ArrayList<PersistableBundle>();
+                List<PersistableBundle> result = new ArrayList<>();
                 result.add(trustAgentInfo.options);
                 return result;
             }
 
             // Return strictest policy for this user and profiles that are visible from this user.
-            final List<UserInfo> profiles = mUserManager.getProfiles(userHandle);
             List<PersistableBundle> result = null;
-
             // Search through all admins that use KEYGUARD_DISABLE_TRUST_AGENTS and keep track
             // of the options. If any admin doesn't have options, discard options for the rest
             // and return null.
+            List<ActiveAdmin> admins =
+                    getActiveAdminsForLockscreenPoliciesLocked(userHandle, parent);
             boolean allAdminsHaveOptions = true;
-            for (UserInfo userInfo : profiles) {
-                DevicePolicyData policy = getUserDataUnchecked(userInfo.id);
-                final int N = policy.mAdminList.size();
-                for (int i=0; i < N; i++) {
-                    final ActiveAdmin active = policy.mAdminList.get(i);
-                    final boolean disablesTrust = (active.disabledKeyguardFeatures
-                            & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
-                    final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
-                    if (info != null && info.options != null && !info.options.isEmpty()) {
-                        if (disablesTrust) {
-                            if (result == null) {
-                                result = new ArrayList<PersistableBundle>();
-                            }
-                            result.add(info.options);
-                        } else {
-                            Log.w(LOG_TAG, "Ignoring admin " + active.info
-                                    + " because it has trust options but doesn't declare "
-                                    + "KEYGUARD_DISABLE_TRUST_AGENTS");
+            final int N = admins.size();
+            for (int i = 0; i < N; i++) {
+                final ActiveAdmin active = admins.get(i);
+
+                final boolean disablesTrust = (active.disabledKeyguardFeatures
+                        & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+                final TrustAgentInfo info = active.trustAgentInfos.get(componentName);
+                if (info != null && info.options != null && !info.options.isEmpty()) {
+                    if (disablesTrust) {
+                        if (result == null) {
+                            result = new ArrayList<>();
                         }
-                    } else if (disablesTrust) {
-                        allAdminsHaveOptions = false;
-                        break;
+                        result.add(info.options);
+                    } else {
+                        Log.w(LOG_TAG, "Ignoring admin " + active.info
+                                + " because it has trust options but doesn't declare "
+                                + "KEYGUARD_DISABLE_TRUST_AGENTS");
                     }
+                } else if (disablesTrust) {
+                    allAdminsHaveOptions = false;
+                    break;
                 }
             }
             return allAdminsHaveOptions ? result : null;
@@ -8037,7 +8078,8 @@
                     }
                 }
             }
-            return null;
+            // We're not specifying the device admin because there isn't one.
+            return intent;
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 8e11511..2f20a4b 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -59,6 +59,7 @@
 import android.os.MessageQueue;
 import android.os.Messenger;
 import android.os.MessageQueue.IdleHandler;
+import android.os.Process;
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -690,6 +691,7 @@
         assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType());
         // Test getActiveNetwork()
         assertNotNull(mCm.getActiveNetwork());
+        assertEquals(mCm.getActiveNetwork(), mCm.getActiveNetworkForUid(Process.myUid()));
         switch (transport) {
             case TRANSPORT_WIFI:
                 assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
@@ -713,6 +715,7 @@
         assertNull(mCm.getActiveNetworkInfo());
         // Test getActiveNetwork()
         assertNull(mCm.getActiveNetwork());
+        assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
         // Test getAllNetworks()
         assertEquals(0, mCm.getAllNetworks().length);
     }
diff --git a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
index 5b70c17..07280bc 100644
--- a/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/ObserverNodeTest.java
@@ -62,7 +62,7 @@
         ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
 
         for (int i = nums.length - 1; i >=0; --i) {
-            root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls);
+            root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls);
             assertEquals(nums[i], calls.size());
             calls.clear();
         }
@@ -92,7 +92,7 @@
         ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
 
         for (int i = uris.length - 1; i >=0; --i) {
-            root.collectObserversLocked(uris[i], 0, null, false, myUserHandle, calls);
+            root.collectObserversLocked(uris[i], 0, null, false, 0, myUserHandle, calls);
             assertEquals(nums[i], calls.size());
             calls.clear();
         }
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
index 6026644..9f53c87 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java
@@ -162,7 +162,7 @@
         final NetworkStats.Entry entry = new NetworkStats.Entry();
         final NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                TEST_IMSI, null, false));
+                TEST_IMSI, null, false, true));
 
         int myUid = Process.myUid();
         int otherUidInSameUser = Process.myUid() + 1;
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
index 82c6b6d..cff5876 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsObserversTest.java
@@ -283,7 +283,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -315,7 +315,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -354,7 +354,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -393,13 +393,13 @@
         NetworkIdentitySet identSet1 = new NetworkIdentitySet();
         identSet1.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveIfaces.put(TEST_IFACE, identSet1);
 
         NetworkIdentitySet identSet2 = new NetworkIdentitySet();
         identSet2.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_2, null /* networkId */, false /* roaming */));
+                IMSI_2, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveIfaces.put(TEST_IFACE2, identSet2);
 
         // Baseline
@@ -441,7 +441,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -481,7 +481,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -521,7 +521,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -561,7 +561,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
@@ -601,7 +601,7 @@
         NetworkIdentitySet identSet = new NetworkIdentitySet();
         identSet.add(new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
-                IMSI_1, null /* networkId */, false /* roaming */));
+                IMSI_1, null /* networkId */, false /* roaming */, true /* metered */));
         mActiveUidIfaces.put(TEST_IFACE, identSet);
 
         // Baseline
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index beec40f..0aeb96f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -144,6 +144,7 @@
     private long mLastAppIdleParoledTime;
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
+    private boolean mSystemServicesReady = false;
 
     @GuardedBy("mLock")
     private AppIdleHistory mAppIdleHistory;
@@ -232,6 +233,8 @@
             if (mPendingOneTimeCheckIdleStates) {
                 postOneTimeCheckIdleStates();
             }
+
+            mSystemServicesReady = true;
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
         }
@@ -810,28 +813,30 @@
             // retain this for safety).
             return false;
         }
-        try {
-            // We allow all whitelisted apps, including those that don't want to be whitelisted
-            // for idle mode, because app idle (aka app standby) is really not as big an issue
-            // for controlling who participates vs. doze mode.
-            if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+        if (mSystemServicesReady) {
+            try {
+                // We allow all whitelisted apps, including those that don't want to be whitelisted
+                // for idle mode, because app idle (aka app standby) is really not as big an issue
+                // for controlling who participates vs. doze mode.
+                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+                    return false;
+                }
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+
+            if (isActiveDeviceAdmin(packageName, userId)) {
                 return false;
             }
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
 
-        if (isActiveDeviceAdmin(packageName, userId)) {
-            return false;
-        }
+            if (isActiveNetworkScorer(packageName)) {
+                return false;
+            }
 
-        if (isActiveNetworkScorer(packageName)) {
-            return false;
-        }
-
-        if (mAppWidgetManager != null
-                && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
-            return false;
+            if (mAppWidgetManager != null
+                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+                return false;
+            }
         }
 
         if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
index 40687b0..f13e019 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java
@@ -41,6 +41,7 @@
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
 import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -56,7 +57,6 @@
  * (ii) Generic sound-trigger models: Supports multiple of these.
  *
  * Currently this just acts as an abstraction over all SoundTrigger API calls.
- *
  * @hide
  */
 public class SoundTriggerHelper implements SoundTrigger.StatusListener {
@@ -84,25 +84,23 @@
     private final PhoneStateListener mPhoneStateListener;
     private final PowerManager mPowerManager;
 
-    // TODO: Since the voice layer currently only handles one recognition
-    // we simplify things by assuming one listener here too.
-    private IRecognitionStatusCallback mKeyphraseListener;
-
     // The SoundTriggerManager layer handles multiple generic recognition models. We store the
     // ModelData here in a hashmap.
     private final HashMap<UUID, ModelData> mGenericModelDataMap;
 
-    // Note: KeyphraseId is not really used.
+    // This ModelData instance ensures that the keyphrase sound model is a singleton and
+    // all other sound models are of type Generic. Any keyphrase sound model will be stored here
+    // and any previously running instances will be replaced. This restriction was earlier
+    // implemented by three instance variables which stored data about the keyphrase
+    // model. That data now gets encapsulated in this ModelData instance.
+    private ModelData mKeyphraseModelData;
+
+    // The keyphrase ID for keyphrase sound models. We store this specially here since ModelData
+    // does not support this.
+    // TODO: The role of the keyphrase ID is a bit unclear. Its just used to ensure that
+    // recognition events have the correct keyphrase ID check.
     private int mKeyphraseId = INVALID_VALUE;
 
-    // Current voice sound model handle. We only allow one voice model to run at any given time.
-    private int mCurrentKeyphraseModelHandle = INVALID_VALUE;
-    private KeyphraseSoundModel mCurrentSoundModel = null;
-    // FIXME: Ideally this should not be stored if allowMultipleTriggers happens at a lower layer.
-    private RecognitionConfig mRecognitionConfig = null;
-
-    // Whether we are requesting recognition to start.
-    private boolean mRequested = false;
     private boolean mCallActive = false;
     private boolean mIsPowerSaveMode = false;
     // Indicates if the native sound trigger service is disabled or not.
@@ -112,8 +110,6 @@
     // Whether we have ANY recognition (keyphrase or generic) running.
     private boolean mRecognitionRunning = false;
 
-    // Keeps track of whether the keyphrase recognition is running.
-    private boolean mKeyphraseStarted = false;
     private boolean mRecognitionAborted = false;
     private PowerSaveModeListener mPowerSaveModeListener;
 
@@ -136,26 +132,89 @@
     }
 
     /**
-     * Starts recognition for the given generic sound model ID.
+     * Starts recognition for the given generic sound model ID. This is a wrapper around {@link
+     * startRecognition()}.
      *
-     * @param soundModel The sound model to use for recognition.
-     * @param listener The listener for the recognition events related to the given keyphrase.
+     * @param modelId UUID of the sound model.
+     * @param soundModel The generic sound model to use for recognition.
+     * @param callback Callack for the recognition events related to the given keyphrase.
+     * @param recognitionConfig Instance of RecognitionConfig containing the parameters for the
+     * recognition.
      * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
      */
     int startGenericRecognition(UUID modelId, GenericSoundModel soundModel,
             IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
-        if (soundModel == null || callback == null || recognitionConfig == null) {
+        MetricsLogger.count(mContext, "sth_start_recognition", 1);
+        if (modelId == null || soundModel == null || callback == null ||
+                recognitionConfig == null) {
             Slog.w(TAG, "Passed in bad data to startGenericRecognition().");
             return STATUS_ERROR;
         }
 
         synchronized (mLock) {
+            ModelData modelData = getOrCreateGenericModelDataLocked(modelId);
+            return startRecognition(soundModel, modelData, callback, recognitionConfig,
+                    INVALID_VALUE /* keyphraseId */);
+        }
+    }
 
+    /**
+     * Starts recognition for the given keyphraseId.
+     *
+     * @param keyphraseId The identifier of the keyphrase for which
+     *        the recognition is to be started.
+     * @param soundModel The sound model to use for recognition.
+     * @param callback The callback for the recognition events related to the given keyphrase.
+     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+     */
+    int startKeyphraseRecognition(int keyphraseId, KeyphraseSoundModel soundModel,
+            IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig) {
+        synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_start_recognition", 1);
+            if (soundModel == null || callback == null || recognitionConfig == null) {
+                return STATUS_ERROR;
+            }
+
+            if (DBG) {
+                Slog.d(TAG, "startKeyphraseRecognition for keyphraseId=" + keyphraseId
+                        + " soundModel=" + soundModel + ", callback=" + callback.asBinder()
+                        + ", recognitionConfig=" + recognitionConfig);
+                Slog.d(TAG, "moduleProperties=" + mModuleProperties);
+                if (mKeyphraseModelData != null) {
+                    Slog.d(TAG, mKeyphraseModelData.toString());
+                } else {
+                    Slog.d(TAG, "Null KeyphraseModelData.");
+                }
+            }
+            if (mKeyphraseModelData == null) {
+                mKeyphraseModelData = ModelData.createKeyphraseModelData(soundModel.uuid);
+            }
+            return startRecognition(soundModel, mKeyphraseModelData, callback, recognitionConfig,
+                    keyphraseId);
+        }
+    }
+
+    /**
+     * Starts recognition for the given sound model. A single routine for both keyphrase and
+     * generic sound models.
+     *
+     * @param soundModel The sound model to use for recognition.
+     * @param modelData Instance of {@link #ModelData} for the given model.
+     * @param callback Callback for the recognition events related to the given keyphrase.
+     * @param recognitionConfig Instance of {@link RecognitionConfig} containing the parameters
+     * @param keyphraseId Keyphrase ID for keyphrase models only. Pass in INVALID_VALUE for other
+     * models.
+     * for the recognition.
+     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+     */
+    int startRecognition(SoundModel soundModel, ModelData modelData,
+            IRecognitionStatusCallback callback, RecognitionConfig recognitionConfig,
+            int keyphraseId) {
+        synchronized (mLock) {
             if (mModuleProperties == null) {
                 Slog.w(TAG, "Attempting startRecognition without the capability");
                 return STATUS_ERROR;
             }
-
             if (mModule == null) {
                 mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null);
                 if (mModule == null) {
@@ -169,13 +228,43 @@
                 initializeTelephonyAndPowerStateListeners();
             }
 
-            // Fetch a ModelData instance from the hash map. Creates a new one if none
-            // exists.
-            ModelData modelData = getOrCreateGenericModelDataLocked(modelId);
+            // If the previous model is different (for the same UUID), ensure that its unloaded
+            // and stopped before proceeding. This works for both keyphrase and generic models.
+            // Specifically for keyphrase since we have 'mKeyphraseModelData' holding a single
+            // allowed instance of such a model, this ensures that a previously loaded (or started)
+            // keyphrase model is appropriately stopped. This ensures no regression with the
+            // previous version of this code as given in the startKeyphrase() routine.
+            //
+            // For generic sound models, all this means is that if we are given a different sound
+            // model with the same UUID, then we will "replace" it.
+            if (modelData.getSoundModel() != null) {
+                boolean stopModel = false; // Stop the model after checking that its started.
+                boolean unloadModel = false;
+                if (modelData.getSoundModel().equals(soundModel) && modelData.isModelStarted()) {
+                    // The model has not changed, but the previous model is "started".
+                    // Stop the previously running model.
+                    stopModel = true;
+                    unloadModel = false; // No need to unload if the model hasn't changed.
+                } else if (!modelData.getSoundModel().equals(soundModel)) {
+                    // We have a different model for this UUID. Stop and unload if needed. This
+                    // helps maintain the singleton restriction for keyphrase sound models.
+                    stopModel = modelData.isModelStarted();
+                    unloadModel = modelData.isModelLoaded();
+                }
+                if (stopModel || unloadModel) {
+                    int status = tryStopAndUnloadLocked(modelData, stopModel, unloadModel);
+                    if (status != STATUS_OK) {
+                        Slog.w(TAG, "Unable to stop or unload previous model: " +
+                                modelData.toString());
+                        return status;
+                    }
+                }
+            }
 
             IRecognitionStatusCallback oldCallback = modelData.getCallback();
-            if (oldCallback != null) {
-                Slog.w(TAG, "Canceling previous recognition for model id: " + modelId);
+            if (oldCallback != null && oldCallback.asBinder() != callback.asBinder()) {
+                Slog.w(TAG, "Canceling previous recognition for model id: " +
+                        modelData.getModelId());
                 try {
                     oldCallback.onError(STATUS_ERROR);
                 } catch (RemoteException e) {
@@ -199,182 +288,49 @@
                 }
                 modelData.setHandle(handle[0]);
                 modelData.setLoaded();
-                Slog.d(TAG, "Generic sound model loaded with handle:" + handle[0]);
+                Slog.d(TAG, "Sound model loaded with handle:" + handle[0]);
             }
             modelData.setCallback(callback);
+            if (modelData.isKeyphraseModel()) {
+                mKeyphraseId = keyphraseId;
+            }
+            modelData.setRequested(true);
             modelData.setRecognitionConfig(recognitionConfig);
+            modelData.setSoundModel(soundModel);
 
-            // Don't notify for synchronous calls.
-            return startGenericRecognitionLocked(modelData, false);
+            return startRecognitionLocked(modelData,
+                    false /* Don't notify for synchronous calls */);
         }
     }
 
     /**
-     * Starts recognition for the given keyphraseId.
-     *
-     * @param keyphraseId The identifier of the keyphrase for which
-     *        the recognition is to be started.
-     * @param soundModel The sound model to use for recognition.
-     * @param listener The listener for the recognition events related to the given keyphrase.
-     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
-     */
-    int startKeyphraseRecognition(int keyphraseId,
-            KeyphraseSoundModel soundModel,
-            IRecognitionStatusCallback listener,
-            RecognitionConfig recognitionConfig) {
-        if (soundModel == null || listener == null || recognitionConfig == null) {
-            return STATUS_ERROR;
-        }
-
-        synchronized (mLock) {
-            if (DBG) {
-                Slog.d(TAG, "startKeyphraseRecognition for keyphraseId=" + keyphraseId
-                        + " soundModel=" + soundModel + ", listener=" + listener.asBinder()
-                        + ", recognitionConfig=" + recognitionConfig);
-                Slog.d(TAG, "moduleProperties=" + mModuleProperties);
-                Slog.d(TAG, "current listener="
-                        + (mKeyphraseListener == null ? "null" : mKeyphraseListener.asBinder()));
-                Slog.d(TAG, "current SoundModel handle=" + mCurrentKeyphraseModelHandle);
-                Slog.d(TAG, "current SoundModel UUID="
-                        + (mCurrentSoundModel == null ? null : mCurrentSoundModel.uuid));
-            }
-
-            if (!mRecognitionRunning) {
-                initializeTelephonyAndPowerStateListeners();
-            }
-
-            if (mModuleProperties == null) {
-                Slog.w(TAG, "Attempting startKeyphraseRecognition without the capability");
-                return STATUS_ERROR;
-            }
-            if (mModule == null) {
-                mModule = SoundTrigger.attachModule(mModuleProperties.id, this, null);
-                if (mModule == null) {
-                    Slog.w(TAG, "startKeyphraseRecognition cannot attach to sound trigger module");
-                    return STATUS_ERROR;
-                }
-            }
-
-            // Unload the previous model if the current one isn't invalid
-            // and, it's not the same as the new one.
-            // This helps use cache and reuse the model and just start/stop it when necessary.
-            if (mCurrentKeyphraseModelHandle != INVALID_VALUE
-                    && !soundModel.equals(mCurrentSoundModel)) {
-                Slog.w(TAG, "Unloading previous sound model");
-                int status = mModule.unloadSoundModel(mCurrentKeyphraseModelHandle);
-                if (status != SoundTrigger.STATUS_OK) {
-                    Slog.w(TAG, "unloadSoundModel call failed with " + status);
-                }
-                internalClearKeyphraseSoundModelLocked();
-                mKeyphraseStarted = false;
-            }
-
-            // If the previous recognition was by a different listener,
-            // Notify them that it was stopped.
-            if (mKeyphraseListener != null && mKeyphraseListener.asBinder() != listener.asBinder()) {
-                Slog.w(TAG, "Canceling previous recognition");
-                try {
-                    mKeyphraseListener.onError(STATUS_ERROR);
-                } catch (RemoteException e) {
-                    Slog.w(TAG, "RemoteException in onDetectionStopped", e);
-                }
-                mKeyphraseListener = null;
-            }
-
-            // Load the sound model if the current one is null.
-            int soundModelHandle = mCurrentKeyphraseModelHandle;
-            if (mCurrentKeyphraseModelHandle == INVALID_VALUE
-                    || mCurrentSoundModel == null) {
-                int[] handle = new int[] { INVALID_VALUE };
-                int status = mModule.loadSoundModel(soundModel, handle);
-                if (status != SoundTrigger.STATUS_OK) {
-                    Slog.w(TAG, "loadSoundModel call failed with " + status);
-                    return status;
-                }
-                if (handle[0] == INVALID_VALUE) {
-                    Slog.w(TAG, "loadSoundModel call returned invalid sound model handle");
-                    return STATUS_ERROR;
-                }
-                soundModelHandle = handle[0];
-            } else {
-                if (DBG) Slog.d(TAG, "Reusing previously loaded sound model");
-            }
-
-            // Start the recognition.
-            mRequested = true;
-            mKeyphraseId = keyphraseId;
-            mCurrentKeyphraseModelHandle = soundModelHandle;
-            mCurrentSoundModel = soundModel;
-            mRecognitionConfig = recognitionConfig;
-            // Register the new listener. This replaces the old one.
-            // There can only be a maximum of one active listener at any given time.
-            mKeyphraseListener = listener;
-
-            return updateRecognitionLocked(false /* don't notify for synchronous calls */);
-        }
-    }
-
-    /**
-     * Stops recognition for the given generic sound model.
+     * Stops recognition for the given generic sound model. This is a wrapper for {@link
+     * #stopRecognition}.
      *
      * @param modelId The identifier of the generic sound model for which
      *        the recognition is to be stopped.
-     * @param listener The listener for the recognition events related to the given sound model.
+     * @param callback The callback for the recognition events related to the given sound model.
      *
      * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
      */
-    int stopGenericRecognition(UUID modelId, IRecognitionStatusCallback listener) {
-        if (listener == null) {
-            return STATUS_ERROR;
-        }
-
+    int stopGenericRecognition(UUID modelId, IRecognitionStatusCallback callback) {
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_stop_recognition", 1);
+            if (callback == null || modelId == null) {
+                Slog.e(TAG, "Null callbackreceived for stopGenericRecognition() for modelid:" +
+                        modelId);
+                return STATUS_ERROR;
+            }
+
             ModelData modelData = mGenericModelDataMap.get(modelId);
             if (modelData == null) {
                 Slog.w(TAG, "Attempting stopRecognition on invalid model with id:" + modelId);
                 return STATUS_ERROR;
             }
 
-            IRecognitionStatusCallback currentCallback = modelData.getCallback();
-            if (DBG) {
-                Slog.d(TAG, "stopRecognition for modelId=" + modelId
-                        + ", listener=" + listener.asBinder());
-                Slog.d(TAG, "current callback ="
-                        + (currentCallback == null ? "null" : currentCallback.asBinder()));
-            }
-
-            if (mModuleProperties == null || mModule == null) {
-                Slog.w(TAG, "Attempting stopRecognition without the capability");
-                return STATUS_ERROR;
-            }
-
-            if (currentCallback == null || !modelData.isModelStarted()) {
-                // startGenericRecognition hasn't been called or it failed.
-                Slog.w(TAG, "Attempting stopGenericRecognition without a successful" +
-                        " startGenericRecognition");
-                return STATUS_ERROR;
-            }
-            if (currentCallback.asBinder() != listener.asBinder()) {
-                // We don't allow a different listener to stop the recognition than the one
-                // that started it.
-                Slog.w(TAG, "Attempting stopGenericRecognition for another recognition");
-                return STATUS_ERROR;
-            }
-
-            int status = stopGenericRecognitionLocked(modelData,
-                    false /* don't notify for synchronous calls */);
+            int status = stopRecognition(modelData, callback);
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "stopGenericRecognition failed: " + status);
-                return status;
-            }
-
-            // We leave the sound model loaded but not started, this helps us when we start
-            // back.
-            // Also clear the internal state once the recognition has been stopped.
-            modelData.setLoaded();
-            modelData.clearCallback();
-            if (!computeRecognitionRunningLocked()) {
-                internalClearGlobalStateLocked();
             }
             return status;
         }
@@ -382,47 +338,30 @@
 
     /**
      * Stops recognition for the given {@link Keyphrase} if a recognition is
-     * currently active.
+     * currently active. This is a wrapper for {@link #stopRecognition()}.
      *
      * @param keyphraseId The identifier of the keyphrase for which
      *        the recognition is to be stopped.
-     * @param listener The listener for the recognition events related to the given keyphrase.
+     * @param callback The callback for the recognition events related to the given keyphrase.
      *
      * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
      */
-    int stopKeyphraseRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
-        if (listener == null) {
-            return STATUS_ERROR;
-        }
-
+    int stopKeyphraseRecognition(int keyphraseId, IRecognitionStatusCallback callback) {
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_stop_recognition", 1);
+            if (callback == null) {
+                Slog.e(TAG, "Null callback received for stopKeyphraseRecognition() for keyphraseId:" +
+                        keyphraseId);
+                return STATUS_ERROR;
+            }
+
             if (DBG) {
-                Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId
-                        + ", listener=" + listener.asBinder());
-                Slog.d(TAG, "current listener="
-                        + (mKeyphraseListener == null ? "null" : mKeyphraseListener.asBinder()));
+                Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId + ", callback =" +
+                        callback.asBinder());
+                Slog.d(TAG, "current callback=" + (mKeyphraseModelData == null ? "null" :
+                            mKeyphraseModelData.getCallback().asBinder()));
             }
-
-            if (mModuleProperties == null || mModule == null) {
-                Slog.w(TAG, "Attempting stopRecognition without the capability");
-                return STATUS_ERROR;
-            }
-
-            if (mKeyphraseListener == null) {
-                // startRecognition hasn't been called or it failed.
-                Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
-                return STATUS_ERROR;
-            }
-            if (mKeyphraseListener.asBinder() != listener.asBinder()) {
-                // We don't allow a different listener to stop the recognition than the one
-                // that started it.
-                Slog.w(TAG, "Attempting stopRecognition for another recognition");
-                return STATUS_ERROR;
-            }
-
-            // Stop recognition if it's the current one, ignore otherwise.
-            mRequested = false;
-            int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
+            int status = stopRecognition(mKeyphraseModelData, callback);
             if (status != SoundTrigger.STATUS_OK) {
                 return status;
             }
@@ -431,25 +370,115 @@
             // back.
             // Also clear the internal state once the recognition has been stopped.
             internalClearKeyphraseStateLocked();
-            internalClearGlobalStateLocked();
             return status;
         }
     }
 
     /**
+     * Stops recognition for the given ModelData instance.
+     *
+     * @param modelData Instance of {@link #ModelData} sound model.
+     * @param callback The callback for the recognition events related to the given keyphrase.
+     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
+     */
+    private int stopRecognition(ModelData modelData, IRecognitionStatusCallback callback) {
+        synchronized (mLock) {
+            if (callback == null) {
+                return STATUS_ERROR;
+            }
+            if (mModuleProperties == null || mModule == null) {
+                Slog.w(TAG, "Attempting stopRecognition without the capability");
+                return STATUS_ERROR;
+            }
+
+            IRecognitionStatusCallback currentCallback = modelData.getCallback();
+            if (modelData == null || currentCallback == null || !modelData.isModelStarted()) {
+                // startGenericRecognition hasn't been called or it failed.
+                Slog.w(TAG, "Attempting stopGenericRecognition without a successful" +
+                        " startGenericRecognition");
+                return STATUS_ERROR;
+            }
+
+            if (currentCallback.asBinder() != callback.asBinder()) {
+                // We don't allow a different listener to stop the recognition than the one
+                // that started it.
+                Slog.w(TAG, "Attempting stopRecognition for another recognition");
+                return STATUS_ERROR;
+            }
+
+            // Request stop recognition via the update() method.
+            modelData.setRequested(false);
+            int status = updateRecognitionLocked(modelData, isRecognitionAllowed(),
+                    false /* don't notify for synchronous calls */);
+            if (status != SoundTrigger.STATUS_OK) {
+                return status;
+            }
+
+            // We leave the sound model loaded but not started, this helps us when we start back.
+            // Also clear the internal state once the recognition has been stopped.
+            modelData.setLoaded();
+            modelData.clearCallback();
+            modelData.setRecognitionConfig(null);
+
+            if (!computeRecognitionRunningLocked()) {
+                internalClearGlobalStateLocked();
+            }
+
+            if (modelData.isKeyphraseModel()) {
+                mKeyphraseId = INVALID_VALUE;
+            }
+            return status;
+        }
+    }
+
+    // Stop a previously started model if it was started. Optionally, unload if the previous model
+    // is stale and is about to be replaced.
+    // Needs to be called with the mLock held.
+    private int tryStopAndUnloadLocked(ModelData modelData, boolean stopModel,
+            boolean unloadModel) {
+        int status = STATUS_OK;
+        if (modelData.isModelNotLoaded()) {
+            return status;
+        }
+        if (stopModel && modelData.isModelStarted()) {
+            status = stopRecognitionLocked(modelData,
+                    false /* don't notify for synchronous calls */);
+            if (status != SoundTrigger.STATUS_OK) {
+                Slog.w(TAG, "stopRecognition failed: " + status);
+                return status;
+            }
+        }
+
+        if (unloadModel && modelData.isModelLoaded()) {
+            Slog.d(TAG, "Unloading previously loaded stale model.");
+            status = mModule.unloadSoundModel(modelData.getHandle());
+            MetricsLogger.count(mContext, "sth_unloading_stale_model", 1);
+            if (status != SoundTrigger.STATUS_OK) {
+                Slog.w(TAG, "unloadSoundModel call failed with " + status);
+            } else {
+                // Clear the ModelData state if successful.
+                modelData.clearState();
+                modelData.clearCallback();
+                modelData.setRecognitionConfig(null);
+            }
+        }
+        return status;
+    }
+
+    /**
      * Stops all recognitions active currently and clears the internal state.
      */
     void stopAllRecognitions() {
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_stop_all_recognitions", 1);
             if (mModuleProperties == null || mModule == null) {
                 return;
             }
 
             // Stop Keyphrase recognition if one exists.
-            if (mCurrentKeyphraseModelHandle != INVALID_VALUE) {
-
-                mRequested = false;
-                int status = updateRecognitionLocked(
+            if (mKeyphraseModelData != null && mKeyphraseModelData.getHandle() != INVALID_VALUE) {
+                mKeyphraseModelData.setRequested(false);
+                int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
                         false /* don't notify for synchronous calls */);
                 internalClearKeyphraseStateLocked();
             }
@@ -457,7 +486,7 @@
             // Stop all generic recognition models.
             for (ModelData model : mGenericModelDataMap.values()) {
                 if (model.isModelStarted()) {
-                    int status = stopGenericRecognitionLocked(model,
+                    int status = stopRecognitionLocked(model,
                             false /* do not notify for synchronous calls */);
                     if (status != STATUS_OK) {
                         // What else can we do if there is an error here.
@@ -476,39 +505,40 @@
     }
 
     int unloadKeyphraseSoundModel(int keyphraseId) {
-        if (mModule == null || mCurrentKeyphraseModelHandle == INVALID_VALUE) {
-            return STATUS_ERROR;
-        }
-        if (mKeyphraseId != keyphraseId) {
-            Slog.w(TAG, "Given sound model is not the one loaded.");
-            return STATUS_ERROR;
-        }
-
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_unload_keyphrase_sound_model", 1);
+            if (mModule == null || mKeyphraseModelData == null ||
+                    mKeyphraseModelData.getHandle() == INVALID_VALUE) {
+                return STATUS_ERROR;
+            }
+
             // Stop recognition if it's the current one.
-            mRequested = false;
-            int status = updateRecognitionLocked(false /* don't notify */);
+            mKeyphraseModelData.setRequested(false);
+            int status = updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
+                    false /* don't notify */);
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "Stop recognition failed for keyphrase ID:" + status);
             }
 
-            status = mModule.unloadSoundModel(mCurrentKeyphraseModelHandle);
+            status = mModule.unloadSoundModel(mKeyphraseModelData.getHandle());
             if (status != SoundTrigger.STATUS_OK) {
                 Slog.w(TAG, "unloadKeyphraseSoundModel call failed with " + status);
             }
-            internalClearKeyphraseSoundModelLocked();
+            mKeyphraseModelData.clearState();
             return status;
         }
     }
 
     int unloadGenericSoundModel(UUID modelId) {
-        if (modelId == null || mModule == null) {
-            return STATUS_ERROR;
-        }
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_unload_generic_sound_model", 1);
+            if (modelId == null || mModule == null) {
+                return STATUS_ERROR;
+            }
             ModelData modelData = mGenericModelDataMap.get(modelId);
             if (modelData == null) {
-                Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" + modelId);
+                Slog.w(TAG, "Unload error: Attempting unload invalid generic model with id:" +
+                        modelId);
                 return STATUS_ERROR;
             }
             if (!modelData.isModelLoaded()) {
@@ -517,7 +547,7 @@
                 return STATUS_OK;
             }
             if (modelData.isModelStarted()) {
-                int status = stopGenericRecognitionLocked(modelData,
+                int status = stopRecognitionLocked(modelData,
                         false /* don't notify for synchronous calls */);
                 if (status != SoundTrigger.STATUS_OK) {
                     Slog.w(TAG, "stopGenericRecognition failed: " + status);
@@ -577,6 +607,7 @@
     }
 
     private void onGenericRecognitionSuccessLocked(GenericRecognitionEvent event) {
+        MetricsLogger.count(mContext, "sth_generic_recognition_event", 1);
         if (event.status != SoundTrigger.RECOGNITION_STATUS_SUCCESS) {
             return;
         }
@@ -608,9 +639,11 @@
             return;
         }
 
+        model.setRequested(config.allowMultipleTriggers);
         // TODO: Remove this block if the lower layer supports multiple triggers.
-        if (config.allowMultipleTriggers) {
-            startGenericRecognitionLocked(model, true /* notify */);
+        if (model.getRequested()) {
+            updateRecognitionLocked(model, isRecognitionAllowed() /* isAllowed */,
+                    true /* notify */);
         }
     }
 
@@ -622,6 +655,7 @@
         }
         if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
         synchronized (mLock) {
+            MetricsLogger.count(mContext, "sth_sound_model_updated", 1);
             onSoundModelUpdatedLocked(event);
         }
     }
@@ -637,6 +671,7 @@
     @Override
     public void onServiceDied() {
         Slog.e(TAG, "onServiceDied!!");
+        MetricsLogger.count(mContext, "sth_service_died", 1);
         synchronized (mLock) {
             onServiceDiedLocked();
         }
@@ -649,7 +684,7 @@
             return;
         }
         mCallActive = callActive;
-        updateRecognitionLocked(true /* notify */);
+        updateAllRecognitionsLocked(true /* notify */);
     }
 
     private void onPowerSaveModeChangedLocked(boolean isPowerSaveMode) {
@@ -657,7 +692,7 @@
             return;
         }
         mIsPowerSaveMode = isPowerSaveMode;
-        updateRecognitionLocked(true /* notify */);
+        updateAllRecognitionsLocked(true /* notify */);
     }
 
     private void onSoundModelUpdatedLocked(SoundModelEvent event) {
@@ -669,11 +704,12 @@
             return;
         }
         mServiceDisabled = disabled;
-        updateRecognitionLocked(true /* notify */);
+        updateAllRecognitionsLocked(true /* notify */);
     }
 
     private void onRecognitionAbortLocked() {
         Slog.w(TAG, "Recognition aborted");
+        MetricsLogger.count(mContext, "sth_recognition_aborted", 1);
         // If abort has been called, the hardware has already stopped recognition, so we shouldn't
         // call it again when we process the state change.
         mRecognitionAborted = true;
@@ -681,23 +717,29 @@
 
     private void onRecognitionFailureLocked() {
         Slog.w(TAG, "Recognition failure");
+        MetricsLogger.count(mContext, "sth_recognition_failure_event", 1);
         try {
-            if (mKeyphraseListener != null) {
-                mKeyphraseListener.onError(STATUS_ERROR);
-            }
+            sendErrorCallbacksToAll(STATUS_ERROR);
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onError", e);
         } finally {
             internalClearKeyphraseStateLocked();
+            internalClearGenericModelStateLocked();
             internalClearGlobalStateLocked();
         }
     }
 
     private void onKeyphraseRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
         Slog.i(TAG, "Recognition success");
+        MetricsLogger.count(mContext, "sth_keyphrase_recognition_event", 1);
 
-        if (mKeyphraseListener == null) {
-            Slog.w(TAG, "received onRecognition event without any listener for it");
+        if (mKeyphraseModelData == null) {
+            Slog.e(TAG, "Received onRecognition event for null keyphrase model data.");
+            return;
+        }
+
+        if (mKeyphraseModelData.getCallback() == null) {
+            Slog.w(TAG, "Received onRecognition event without any listener for it.");
             return;
         }
 
@@ -714,30 +756,62 @@
         }
 
         try {
-            if (mKeyphraseListener != null) {
-                mKeyphraseListener.onKeyphraseDetected((KeyphraseRecognitionEvent) event);
-            }
+            mKeyphraseModelData.getCallback().onKeyphraseDetected(
+                    (KeyphraseRecognitionEvent) event);
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onKeyphraseDetected", e);
         }
 
-        mKeyphraseStarted = false;
-        mRequested = mRecognitionConfig.allowMultipleTriggers;
+        mKeyphraseModelData.setStopped();
+
+        RecognitionConfig config = mKeyphraseModelData.getRecognitionConfig();
+        if (config != null) {
+            // Whether we should continue by starting this again.
+            mKeyphraseModelData.setRequested(config.allowMultipleTriggers);
+        }
         // TODO: Remove this block if the lower layer supports multiple triggers.
-        if (mRequested) {
-            updateRecognitionLocked(true /* notify */);
+        if (mKeyphraseModelData.getRequested()) {
+            updateRecognitionLocked(mKeyphraseModelData, isRecognitionAllowed(),
+                true /* notify */);
+        }
+    }
+
+    private void updateAllRecognitionsLocked(boolean notify) {
+        boolean isAllowed = isRecognitionAllowed();
+        // Keyphrase model.
+        if (mKeyphraseModelData != null) {
+            updateRecognitionLocked(mKeyphraseModelData, isAllowed, notify);
+        }
+        for (UUID modelId : mGenericModelDataMap.keySet()) {
+            ModelData modelData = mGenericModelDataMap.get(modelId);
+            updateRecognitionLocked(modelData, isAllowed, notify);
+        }
+    }
+
+    private int updateRecognitionLocked(ModelData model, boolean isAllowed,
+        boolean notify) {
+        boolean start = model.getRequested() && isAllowed;
+        if (start == model.isModelStarted()) {
+            // No-op.
+            return STATUS_OK;
+        }
+        if (start) {
+            return startRecognitionLocked(model, notify);
+        } else {
+            return stopRecognitionLocked(model, notify);
         }
     }
 
     private void onServiceDiedLocked() {
         try {
-            if (mKeyphraseListener != null) {
-                mKeyphraseListener.onError(SoundTrigger.STATUS_DEAD_OBJECT);
-            }
+          MetricsLogger.count(mContext, "sth_service_died", 1);
+            sendErrorCallbacksToAll(SoundTrigger.STATUS_DEAD_OBJECT);
         } catch (RemoteException e) {
             Slog.w(TAG, "RemoteException in onError", e);
         } finally {
-            internalClearKeyphraseSoundModelLocked();
+            if (mKeyphraseModelData != null) {
+                mKeyphraseModelData.clearState();
+            }
             internalClearKeyphraseStateLocked();
             internalClearGenericModelStateLocked();
             internalClearGlobalStateLocked();
@@ -748,78 +822,6 @@
         }
     }
 
-    private int updateRecognitionLocked(boolean notify) {
-        if (mModule == null || mModuleProperties == null
-                || mCurrentKeyphraseModelHandle == INVALID_VALUE || mKeyphraseListener == null) {
-            // Nothing to do here.
-            return STATUS_OK;
-        }
-
-        boolean start = mRequested && !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;
-        if (start == mKeyphraseStarted) {
-            // No-op.
-            return STATUS_OK;
-        }
-
-        // See if the recognition needs to be started.
-        if (start) {
-            // Start recognition.
-            int status = mModule.startRecognition(mCurrentKeyphraseModelHandle,
-                    mRecognitionConfig);
-            if (status != SoundTrigger.STATUS_OK) {
-                Slog.w(TAG, "startKeyphraseRecognition failed with " + status);
-                // Notify of error if needed.
-                if (notify) {
-                    try {
-                        mKeyphraseListener.onError(status);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "RemoteException in onError", e);
-                    }
-                }
-            } else {
-                mKeyphraseStarted = true;
-                // Notify of resume if needed.
-                if (notify) {
-                    try {
-                        mKeyphraseListener.onRecognitionResumed();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "RemoteException in onRecognitionResumed", e);
-                    }
-                }
-            }
-            return status;
-        } else {
-            // Stop recognition (only if we haven't been aborted).
-            int status = STATUS_OK;
-            if (!mRecognitionAborted) {
-                status = mModule.stopRecognition(mCurrentKeyphraseModelHandle);
-            } else {
-                mRecognitionAborted = false;
-            }
-            if (status != SoundTrigger.STATUS_OK) {
-                Slog.w(TAG, "stopRecognition call failed with " + status);
-                if (notify) {
-                    try {
-                        mKeyphraseListener.onError(status);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "RemoteException in onError", e);
-                    }
-                }
-            } else {
-                mKeyphraseStarted = false;
-                // Notify of pause if needed.
-                if (notify) {
-                    try {
-                        mKeyphraseListener.onRecognitionPaused();
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
-                    }
-                }
-            }
-            return status;
-        }
-    }
-
     // internalClearGlobalStateLocked() gets split into two routines. Cleanup that is
     // specific to keyphrase sound models named as internalClearKeyphraseStateLocked() and
     // internalClearGlobalStateLocked() for global state. The global cleanup routine will be used
@@ -836,12 +838,14 @@
     }
 
     private void internalClearKeyphraseStateLocked() {
-        mKeyphraseStarted = false;
-        mRequested = false;
+        if (mKeyphraseModelData != null) {
+            mKeyphraseModelData.setStopped();
+            mKeyphraseModelData.setRequested(false);
+            mKeyphraseModelData.setRecognitionConfig(null);
+            mKeyphraseModelData.setCallback(null);
+        }
 
         mKeyphraseId = INVALID_VALUE;
-        mRecognitionConfig = null;
-        mKeyphraseListener = null;
     }
 
     private void internalClearGenericModelStateLocked() {
@@ -852,13 +856,6 @@
         }
     }
 
-    // This routine is a replacement for internalClearSoundModelLocked(). However, we
-    // should see why this should be different from internalClearKeyphraseStateLocked().
-    private void internalClearKeyphraseSoundModelLocked() {
-        mCurrentKeyphraseModelHandle = INVALID_VALUE;
-        mCurrentSoundModel = null;
-    }
-
     class MyCallStateListener extends PhoneStateListener {
         @Override
         public void onCallStateChanged(int state, String arg1) {
@@ -888,17 +885,13 @@
             pw.print("  module properties=");
             pw.println(mModuleProperties == null ? "null" : mModuleProperties);
             pw.print("  keyphrase ID="); pw.println(mKeyphraseId);
-            pw.print("  sound model handle="); pw.println(mCurrentKeyphraseModelHandle);
-            pw.print("  sound model UUID=");
-            pw.println(mCurrentSoundModel == null ? "null" : mCurrentSoundModel.uuid);
-            pw.print("  current listener=");
-            pw.println(mKeyphraseListener == null ? "null" : mKeyphraseListener.asBinder());
 
-            pw.print("  requested="); pw.println(mRequested);
-            pw.print("  started="); pw.println(mKeyphraseStarted);
             pw.print("  call active="); pw.println(mCallActive);
             pw.print("  power save mode active="); pw.println(mIsPowerSaveMode);
             pw.print("  service disabled="); pw.println(mServiceDisabled);
+            if (mKeyphraseModelData != null) {
+                pw.println(mKeyphraseModelData.toString());
+            }
         }
     }
 
@@ -919,11 +912,25 @@
         mIsPowerSaveMode = mPowerManager.isPowerSaveMode();
     }
 
+    // Sends an error callback to all models with a valid registered callback.
+    private void sendErrorCallbacksToAll(int errorCode) throws RemoteException {
+        IRecognitionStatusCallback keyphraseListener = mKeyphraseModelData.getCallback();
+        if (keyphraseListener != null) {
+            keyphraseListener.onError(STATUS_ERROR);
+        }
+        for (UUID modelId: mGenericModelDataMap.keySet()) {
+            ModelData modelData = mGenericModelDataMap.get(modelId);
+            IRecognitionStatusCallback keyphraseCallback = mKeyphraseModelData.getCallback();
+            if (keyphraseCallback != null) {
+                keyphraseCallback.onError(STATUS_ERROR);
+            }
+        }
+    }
+
     private ModelData getOrCreateGenericModelDataLocked(UUID modelId) {
         ModelData modelData = mGenericModelDataMap.get(modelId);
         if (modelData == null) {
-            modelData = new ModelData(modelId);
-            modelData.setTypeGeneric();
+            modelData = ModelData.createGenericModelData(modelId);
             mGenericModelDataMap.put(modelId, modelData);
         }
         return modelData;
@@ -949,25 +956,30 @@
         return !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;
     }
 
-    private int startGenericRecognitionLocked(ModelData modelData, boolean notify) {
+    // A single routine that implements the start recognition logic for both generic and keyphrase
+    // models.
+    private int startRecognitionLocked(ModelData modelData, boolean notify) {
         IRecognitionStatusCallback callback = modelData.getCallback();
         int handle = modelData.getHandle();
         RecognitionConfig config = modelData.getRecognitionConfig();
         if (callback == null || handle == INVALID_VALUE || config == null) {
             // Nothing to do here.
-            Slog.w(TAG, "startGenericRecognition: Bad data passed in.");
+            Slog.w(TAG, "startRecognition: Bad data passed in.");
+            MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
             return STATUS_ERROR;
         }
 
         if (!isRecognitionAllowed()) {
             // Nothing to do here.
-            Slog.w(TAG, "startGenericRecognition requested but not allowed.");
+            Slog.w(TAG, "startRecognition requested but not allowed.");
+            MetricsLogger.count(mContext, "sth_start_recognition_not_allowed", 1);
             return STATUS_OK;
         }
 
         int status = mModule.startRecognition(handle, config);
         if (status != SoundTrigger.STATUS_OK) {
-            Slog.w(TAG, "startGenericRecognition failed with " + status);
+            Slog.w(TAG, "startRecognition failed with " + status);
+            MetricsLogger.count(mContext, "sth_start_recognition_error", 1);
             // Notify of error if needed.
             if (notify) {
                 try {
@@ -978,6 +990,7 @@
             }
         } else {
             Slog.i(TAG, "startRecognition successful.");
+            MetricsLogger.count(mContext, "sth_start_recognition_success", 1);
             modelData.setStarted();
             // Notify of resume if needed.
             if (notify) {
@@ -988,17 +1001,31 @@
                 }
             }
         }
-        if (DBG) dumpGenericModelStateLocked();
+        if (DBG) {
+            Slog.d(TAG, "Model being started :" + modelData.toString());
+        }
         return status;
     }
 
-    private int stopGenericRecognitionLocked(ModelData modelData, boolean notify) {
+    private int stopRecognitionLocked(ModelData modelData, boolean notify) {
         IRecognitionStatusCallback callback = modelData.getCallback();
 
         // Stop recognition (only if we haven't been aborted).
-        int status = mModule.stopRecognition(modelData.getHandle());
+        int status = STATUS_OK;
+
+        // This logic for "recognition aborted" now works for both generic and keyphrase models.
+        // The idea here is to "skip" the stopRecognition() call if the lower layer has
+        // aborted recognition. Also we "consume" the abort state as well, so if there is another
+        // stopRecognition() request, it will go through -- this seems to have been the previously
+        // intended design.
+        if (!mRecognitionAborted) {
+            status = mModule.stopRecognition(modelData.getHandle());
+        } else {
+            mRecognitionAborted = false;
+        }
         if (status != SoundTrigger.STATUS_OK) {
             Slog.w(TAG, "stopRecognition call failed with " + status);
+            MetricsLogger.count(mContext, "sth_stop_recognition_error", 1);
             if (notify) {
                 try {
                     callback.onError(status);
@@ -1008,6 +1035,7 @@
             }
         } else {
             modelData.setStopped();
+            MetricsLogger.count(mContext, "sth_stop_recognition_success", 1);
             // Notify of pause if needed.
             if (notify) {
                 try {
@@ -1017,7 +1045,9 @@
                 }
             }
         }
-        if (DBG) dumpGenericModelStateLocked();
+        if (DBG) {
+            Slog.d(TAG, "Model being stopped :" + modelData.toString());
+        }
         return status;
     }
 
@@ -1035,8 +1065,9 @@
             mRecognitionRunning = false;
             return mRecognitionRunning;
         }
-        if (mKeyphraseListener != null && mKeyphraseStarted &&
-            mCurrentKeyphraseModelHandle != INVALID_VALUE && mCurrentSoundModel != null) {
+        if (mKeyphraseModelData != null && mKeyphraseModelData.getCallback() != null &&
+                mKeyphraseModelData.isModelStarted() &&
+            mKeyphraseModelData.getHandle() != INVALID_VALUE) {
             mRecognitionRunning = true;
             return mRecognitionRunning;
         }
@@ -1065,26 +1096,55 @@
 
         // One of MODEL_NOTLOADED, MODEL_LOADED, MODEL_STARTED (which implies loaded).
         private int mModelState;
-
         private UUID mModelId;
 
+        // mRequested captures the explicit intent that a start was requested for this model. We
+        // continue to capture and retain this state even after the model gets started, so that we
+        // know when a model gets stopped due to "other" reasons, that we should start it again.
+        // This was the intended behavior of the "mRequested" variable in the previous version of
+        // this code that we are replicating here.
+        //
+        // The "other" reasons include power save, abort being called from the lower layer (due
+        // to concurrent capture not being supported) and phone call state. Once we recover from
+        // these transient disruptions, we would start such models again where mRequested == true.
+        // Thus, mRequested gets reset only when there is an explicit intent to stop the model
+        // coming from the SoundTriggerService layer that uses this class (and thus eventually
+        // from the app that manages this model).
+        private boolean mRequested = false;
+
         // One of SoundModel.TYPE_GENERIC or SoundModel.TYPE_KEYPHRASE. Initially set
         // to SoundModel.TYPE_UNKNOWN;
         private int mModelType = SoundModel.TYPE_UNKNOWN;
+
         private IRecognitionStatusCallback mCallback = null;
         private RecognitionConfig mRecognitionConfig = null;
 
-
         // Model handle is an integer used by the HAL as an identifier for sound
         // models.
         private int mModelHandle = INVALID_VALUE;
 
-        ModelData(UUID modelId) {
+        // The SoundModel instance, one of KeyphraseSoundModel or GenericSoundModel.
+        private SoundModel mSoundModel = null;
+
+        private ModelData(UUID modelId, int modelType) {
             mModelId = modelId;
+            // Private constructor, since we require modelType to be one of TYPE_GENERIC,
+            // TYPE_KEYPHRASE or TYPE_UNKNOWN.
+            mModelType = modelType;
         }
 
-        synchronized void setTypeGeneric() {
-            mModelType = SoundModel.TYPE_GENERIC_SOUND;
+        static ModelData createKeyphraseModelData(UUID modelId) {
+            return new ModelData(modelId, SoundModel.TYPE_KEYPHRASE);
+        }
+
+        static ModelData createGenericModelData(UUID modelId) {
+            return new ModelData(modelId, SoundModel.TYPE_GENERIC_SOUND);
+        }
+
+        // Note that most of the functionality in this Java class will not work for
+        // SoundModel.TYPE_UNKNOWN nevertheless we have it since lower layers support it.
+        static ModelData createModelDataOfUnknownType(UUID modelId) {
+            return new ModelData(modelId, SoundModel.TYPE_UNKNOWN);
         }
 
         synchronized void setCallback(IRecognitionStatusCallback callback) {
@@ -1099,6 +1159,10 @@
             return (mModelState == MODEL_LOADED || mModelState == MODEL_STARTED);
         }
 
+        synchronized boolean isModelNotLoaded() {
+            return mModelState == MODEL_NOTLOADED;
+        }
+
         synchronized void setStarted() {
             mModelState = MODEL_STARTED;
         }
@@ -1136,11 +1200,40 @@
             return mModelHandle;
         }
 
+        synchronized UUID getModelId() {
+            return mModelId;
+        }
+
         synchronized RecognitionConfig getRecognitionConfig() {
             return mRecognitionConfig;
         }
 
-        String stateToString() {
+        // Whether a start recognition was requested.
+        synchronized boolean getRequested() {
+            return mRequested;
+        }
+
+        synchronized void setRequested(boolean requested) {
+            mRequested = requested;
+        }
+
+        synchronized void setSoundModel(SoundModel soundModel) {
+            mSoundModel = soundModel;
+        }
+
+        synchronized SoundModel getSoundModel() {
+            return mSoundModel;
+        }
+
+        synchronized int getModelType() {
+            return mModelType;
+        }
+
+        synchronized boolean isKeyphraseModel() {
+            return mModelType == SoundModel.TYPE_KEYPHRASE;
+        }
+
+        synchronized String stateToString() {
             switch(mModelState) {
                 case MODEL_NOTLOADED: return "NOT_LOADED";
                 case MODEL_LOADED: return "LOADED";
@@ -1149,8 +1242,24 @@
             return "Unknown state";
         }
 
-        public String toString() {
-            return "Handle: " + mModelHandle + "ModelState: " + stateToString();
+        synchronized String requestedToString() {
+            return "Requested: " + (mRequested ? "Yes" : "No");
+        }
+
+        synchronized String callbackToString() {
+            return "Callback: " + (mCallback != null ? mCallback.asBinder() : "null");
+        }
+
+        synchronized String uuidToString() {
+            return "UUID: " + mModelId;
+        }
+
+        synchronized public String toString() {
+            return "Handle: " + mModelHandle + "\n" +
+                    "ModelState: " + stateToString() + "\n" +
+                    requestedToString() + "\n" +
+                    callbackToString() + "\n" +
+                    uuidToString();
         }
     }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 461611d..adc7c21 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -419,6 +419,13 @@
     public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
             "carrier_data_call_apn_delay_faster_long";
 
+    /**
+     * Default APN types that are metered by the carrier
+     * @hide
+     */
+    public static final String KEY_CARRIER_METERED_APN_TYPES_STRINGS =
+            "carrier_metered_apn_types_strings";
+
     /* The following 3 fields are related to carrier visual voicemail. */
 
     /**
@@ -699,6 +706,8 @@
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
         sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
         sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
+        sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
+                new String[]{"default", "mms", "dun", "supl"});
 
         sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
         sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 0da1bb1..6c8be39 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -86,6 +86,155 @@
     }
 
     private Test[] mTests = new Test[] {
+            new Test("Post a group") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6000, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("low priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_LOW)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6001, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("default priority group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setGroup("group1")
+                            .build();
+                    mNM.notify(6002, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group 1")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group1")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(6003, n);
+                }
+            },
+            new Test("Post a group (2) w/o summary") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("Min priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6100, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("low priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_LOW)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6101, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("default priority group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setGroup("group2")
+                            .build();
+                    mNM.notify(6102, n);
+                }
+            },
+            new Test("Summary for group 2") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group 2")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("group2")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(6103, n);
+                }
+            },
+            new Test("Group up public-secret") {
+                public void run()
+                {
+                    Notification n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("public notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PUBLIC)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("public", 7009, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("private only notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PRIVATE)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("no public", 7010, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("private version of notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_PRIVATE)
+                            .setGroup("public-secret")
+                            .setPublicVersion(new Notification.Builder(NotificationTestList.this)
+                                    .setSmallIcon(R.drawable.icon2)
+                                    .setContentTitle("public notification of private notification")
+                                    .setPriority(Notification.PRIORITY_DEFAULT)
+                                    .setVisibility(Notification.VISIBILITY_PUBLIC)
+                                    .build())
+                            .build();
+                    mNM.notify("priv with pub", 7011, n);
+                    n = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("secret notification")
+                            .setDefaults(Notification.DEFAULT_LIGHTS|Notification.DEFAULT_VIBRATE)
+                            .setPriority(Notification.PRIORITY_DEFAULT)
+                            .setVisibility(Notification.VISIBILITY_SECRET)
+                            .setGroup("public-secret")
+                            .build();
+                    mNM.notify("secret", 7012, n);
+
+                    Notification s = new Notification.Builder(NotificationTestList.this)
+                            .setSmallIcon(R.drawable.icon2)
+                            .setContentTitle("summary group public-secret")
+                            .setLights(0xff0000ff, 1, 0)
+                            .setPriority(Notification.PRIORITY_MIN)
+                            .setGroup("public-secret")
+                            .setGroupSummary(true)
+                            .build();
+                    mNM.notify(7113, s);
+                }
+            },
+            new Test("Cancel priority autogroup") {
+                public void run()
+                {
+                    try {
+                        mNM.cancel(Integer.MAX_VALUE);
+                    } catch (Exception e) {
+                        Toast.makeText(NotificationTestList.this, "cancel failed (yay)",
+                                Toast.LENGTH_LONG).show();
+                    }
+                }
+            },
             new Test("Min priority") {
                 public void run()
                 {
@@ -95,7 +244,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_MIN)
                             .build();
-                    mNM.notify(7000, n);
+                    mNM.notify("min", 7000, n);
                 }
             },
             new Test("Min priority, high pri flag") {
@@ -123,7 +272,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_LOW)
                             .build();
-                    mNM.notify(7002, n);
+                    mNM.notify("low", 7002, n);
                 }
             },
             new Test("Default priority") {
@@ -135,7 +284,7 @@
                             .setLights(0xff0000ff, 1, 0)
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .build();
-                    mNM.notify(7004, n);
+                    mNM.notify("default", 7004, n);
                 }
             },
             new Test("High priority") {
@@ -150,7 +299,7 @@
                                     getPackageName() + "/raw/ringer"))
                             .setPriority(Notification.PRIORITY_HIGH)
                             .build();
-                    mNM.notify(7006, n);
+                    mNM.notify("high", 7006, n);
                 }
             },
             new Test("Max priority") {
@@ -166,7 +315,7 @@
                             .setPriority(Notification.PRIORITY_MAX)
                             .setFullScreenIntent(makeIntent2(), false)
                             .build();
-                    mNM.notify(7007, n);
+                    mNM.notify("max", 7007, n);
                 }
             },
             new Test("Max priority with delay") {
@@ -199,7 +348,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_PUBLIC)
                             .build();
-                    mNM.notify(7009, n);
+                    mNM.notify("public", 7009, n);
                 }
             },
             new Test("private notification, no public") {
@@ -212,7 +361,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_PRIVATE)
                             .build();
-                    mNM.notify(7010, n);
+                    mNM.notify("no public", 7010, n);
                 }
             },
             new Test("private notification, has public") {
@@ -231,7 +380,7 @@
                                     .setVisibility(Notification.VISIBILITY_PUBLIC)
                                     .build())
                             .build();
-                    mNM.notify(7011, n);
+                    mNM.notify("priv with pub", 7011, n);
                 }
             },
             new Test("secret notification") {
@@ -244,7 +393,7 @@
                             .setPriority(Notification.PRIORITY_DEFAULT)
                             .setVisibility(Notification.VISIBILITY_SECRET)
                             .build();
-                    mNM.notify(7012, n);
+                    mNM.notify("secret", 7012, n);
                 }
             },
         new Test("Off") {
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 0bb88a7..db40416 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -492,6 +492,21 @@
     SortedVector<String8> reasons;
 };
 
+struct Feature {
+    Feature() : required(false), version(-1) {}
+    Feature(bool required, int32_t version = -1) : required(required), version(version) {}
+
+    /**
+     * Whether the feature is required.
+     */
+    bool required;
+
+    /**
+     * What version of the feature is requested.
+     */
+    int32_t version;
+};
+
 /**
  * Represents a <feature-group> tag in the AndroidManifest.xml
  */
@@ -506,7 +521,7 @@
     /**
      * Explicit features defined in the group
      */
-    KeyedVector<String8, bool> features;
+    KeyedVector<String8, Feature> features;
 
     /**
      * OpenGL ES version required
@@ -541,11 +556,18 @@
 
     const size_t numFeatures = grp.features.size();
     for (size_t i = 0; i < numFeatures; i++) {
-        const bool required = grp.features[i];
+        const Feature& feature = grp.features[i];
+        const bool required = feature.required;
+        const int32_t version = feature.version;
 
         const String8& featureName = grp.features.keyAt(i);
-        printf("  uses-feature%s: name='%s'\n", (required ? "" : "-not-required"),
+        printf("  uses-feature%s: name='%s'", (required ? "" : "-not-required"),
                 ResTable::normalizeForOutput(featureName.string()).string());
+
+        if (version > 0) {
+            printf(" version='%d'", version);
+        }
+        printf("\n");
     }
 
     const size_t numImpliedFeatures =
@@ -590,15 +612,15 @@
 static void addParentFeatures(FeatureGroup* grp, const String8& name) {
     if (name == "android.hardware.camera.autofocus" ||
             name == "android.hardware.camera.flash") {
-        grp->features.add(String8("android.hardware.camera"), true);
+        grp->features.add(String8("android.hardware.camera"), Feature(true));
     } else if (name == "android.hardware.location.gps" ||
             name == "android.hardware.location.network") {
-        grp->features.add(String8("android.hardware.location"), true);
+        grp->features.add(String8("android.hardware.location"), Feature(true));
     } else if (name == "android.hardware.touchscreen.multitouch") {
-        grp->features.add(String8("android.hardware.touchscreen"), true);
+        grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
     } else if (name == "android.hardware.touchscreen.multitouch.distinct") {
-        grp->features.add(String8("android.hardware.touchscreen.multitouch"), true);
-        grp->features.add(String8("android.hardware.touchscreen"), true);
+        grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true));
+        grp->features.add(String8("android.hardware.touchscreen"), Feature(true));
     } else if (name == "android.hardware.opengles.aep") {
         const int openGLESVersion31 = 0x00030001;
         if (openGLESVersion31 > grp->openGLESVersion) {
@@ -727,6 +749,9 @@
         return 1;
     }
 
+    // Source for AndroidManifest.xml
+    const String8 manifestFile = String8::format("%s@AndroidManifest.xml", filename);
+
     // The dynamicRefTable can be null if there are no resources for this asset cookie.
     // This fine.
     const DynamicRefTable* dynamicRefTable = res.getDynamicRefTableForCookie(assetsCookie);
@@ -1424,10 +1449,28 @@
                     } else if (tag == "uses-feature") {
                         String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            int req = AaptXml::getIntegerAttribute(tree,
-                                    REQUIRED_ATTR, 1);
+                            const char* androidSchema =
+                                    "http://schemas.android.com/apk/res/android";
 
-                            commonFeatures.features.add(name, req);
+                            int32_t req = AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1,
+                                                                       &error);
+                            if (error != "") {
+                                SourcePos(manifestFile, tree.getLineNumber()).error(
+                                        "failed to read attribute 'android:required': %s",
+                                        error.string());
+                                goto bail;
+                            }
+
+                            int32_t version = AaptXml::getIntegerAttribute(tree, androidSchema,
+                                                                           "version", 0, &error);
+                            if (error != "") {
+                                SourcePos(manifestFile, tree.getLineNumber()).error(
+                                        "failed to read attribute 'android:version': %s",
+                                        error.string());
+                                goto bail;
+                            }
+
+                            commonFeatures.features.add(name, Feature(req != 0, version));
                             if (req) {
                                 addParentFeatures(&commonFeatures, name);
                             }
@@ -1751,12 +1794,27 @@
                             }
                         }
                     } else if (withinFeatureGroup && tag == "uses-feature") {
+                        const String8 androidSchema("http://schemas.android.com/apk/res/android");
                         FeatureGroup& top = featureGroups.editTop();
 
                         String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error);
                         if (name != "" && error == "") {
-                            top.features.add(name, true);
+                            Feature feature(true);
+
+                            int32_t featureVers = AaptXml::getIntegerAttribute(
+                                    tree, androidSchema.string(), "version", 0, &error);
+                            if (error == "") {
+                                feature.version = featureVers;
+                            } else {
+                                SourcePos(manifestFile, tree.getLineNumber()).error(
+                                        "failed to read attribute 'android:version': %s",
+                                        error.string());
+                                goto bail;
+                            }
+
+                            top.features.add(name, feature);
                             addParentFeatures(&top, name);
+
                         } else {
                             int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR,
                                     &error);
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 876a422..3a1e2bb 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -66,6 +66,7 @@
 	ResourceValues.cpp \
 	SdkConstants.cpp \
 	StringPool.cpp \
+	xml/XmlActionExecutor.cpp \
 	xml/XmlDom.cpp \
 	xml/XmlPullParser.cpp \
 	xml/XmlUtil.cpp
@@ -107,6 +108,7 @@
 	SdkConstants_test.cpp \
 	StringPool_test.cpp \
 	ValueVisitor_test.cpp \
+	xml/XmlActionExecutor_test.cpp \
 	xml/XmlDom_test.cpp \
 	xml/XmlPullParser_test.cpp \
 	xml/XmlUtil_test.cpp
@@ -129,33 +131,40 @@
 	libbase \
 	libprotobuf-cpp-lite_static
 
-# Do not add any shared libraries. AAPT2 is built to run on many
-# environments that may not have the required dependencies.
-hostSharedLibs :=
 
-ifneq ($(strip $(USE_MINGW)),)
-	hostStaticLibs += libz
-else
-	hostLdLibs += -lz
-endif
+# Statically link libz for MinGW (Win SDK under Linux),
+# and dynamically link for all others.
+hostStaticLibs_windows := libz
+hostLdLibs_linux := -lz
+hostLdLibs_darwin := -lz
 
 cFlags := -Wall -Werror -Wno-unused-parameter -UNDEBUG
+cFlags_darwin := -D_DARWIN_UNLIMITED_STREAMS
+cFlags_windows := -Wno-maybe-uninitialized # Incorrectly marking use of Maybe.value() as error.
 cppFlags := -std=c++11 -Wno-missing-field-initializers -fno-exceptions -fno-rtti
 protoIncludes := $(call generated-sources-dir-for,STATIC_LIBRARIES,libaapt2,HOST)
 
 # ==========================================================
+# NOTE: Do not add any shared libraries.
+# AAPT2 is built to run on many environments
+# that may not have the required dependencies.
+# ==========================================================
+
+# ==========================================================
 # Build the host static library: libaapt2
 # ==========================================================
 include $(CLEAR_VARS)
-LOCAL_MODULE_CLASS := STATIC_LIBRARIES
 LOCAL_MODULE := libaapt2
-
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_CFLAGS_darwin := $(cFlags_darwin)
+LOCAL_CFLAGS_windows := $(cFlags_windows)
+LOCAL_CPPFLAGS := $(cppFlags)
+LOCAL_C_INCLUDES := $(protoIncludes)
 LOCAL_SRC_FILES := $(sources)
-LOCAL_STATIC_LIBRARIES += $(hostStaticLibs)
-LOCAL_CFLAGS += $(cFlags)
-LOCAL_CPPFLAGS += $(cppFlags)
-LOCAL_C_INCLUDES += $(protoIncludes)
-
+LOCAL_STATIC_LIBRARIES := $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 # ==========================================================
@@ -164,16 +173,18 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libaapt2_tests
 LOCAL_MODULE_TAGS := tests
-
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_CFLAGS_darwin := $(cFlags_darwin)
+LOCAL_CFLAGS_windows := $(cFlags_windows)
+LOCAL_CPPFLAGS := $(cppFlags)
+LOCAL_C_INCLUDES := $(protoIncludes)
 LOCAL_SRC_FILES := $(testSources)
-
-LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
-LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
-LOCAL_LDLIBS += $(hostLdLibs)
-LOCAL_CFLAGS += $(cFlags)
-LOCAL_CPPFLAGS += $(cppFlags)
-LOCAL_C_INCLUDES += $(protoIncludes)
-
+LOCAL_STATIC_LIBRARIES := libaapt2 $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
+LOCAL_LDLIBS := $(hostLdLibs)
+LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
 include $(BUILD_HOST_NATIVE_TEST)
 
 # ==========================================================
@@ -181,16 +192,18 @@
 # ==========================================================
 include $(CLEAR_VARS)
 LOCAL_MODULE := aapt2
-
+LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_CFLAGS := $(cFlags)
+LOCAL_CFLAGS_darwin := $(cFlags_darwin)
+LOCAL_CFLAGS_windows := $(cFlags_windows)
+LOCAL_CPPFLAGS := $(cppFlags)
+LOCAL_C_INCLUDES := $(protoIncludes)
 LOCAL_SRC_FILES := $(main) $(toolSources)
-
-LOCAL_STATIC_LIBRARIES += libaapt2 $(hostStaticLibs)
-LOCAL_SHARED_LIBRARIES += $(hostSharedLibs)
-LOCAL_LDLIBS += $(hostLdLibs)
-LOCAL_CFLAGS += $(cFlags)
-LOCAL_CPPFLAGS += $(cppFlags)
-LOCAL_C_INCLUDES += $(protoIncludes)
-
+LOCAL_STATIC_LIBRARIES := libaapt2 $(hostStaticLibs)
+LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
+LOCAL_LDLIBS := $(hostLdLibs)
+LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
+LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
 include $(BUILD_HOST_EXECUTABLE)
 
 ifeq ($(ONE_SHOT_MAKEFILE),)
diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h
index ab4d284..e86f2a8 100644
--- a/tools/aapt2/Diagnostics.h
+++ b/tools/aapt2/Diagnostics.h
@@ -21,6 +21,7 @@
 #include "util/StringPiece.h"
 #include "util/Util.h"
 
+#include <android-base/macros.h>
 #include <iostream>
 #include <sstream>
 #include <string>
@@ -46,7 +47,11 @@
     DiagMessage(const Source& src) : mSource(src) {
     }
 
-    template <typename T> DiagMessage& operator<<(const T& value) {
+    DiagMessage(size_t line) : mSource(Source().withLine(line)) {
+    }
+
+    template <typename T>
+    DiagMessage& operator<<(const T& value) {
         mMessage << value;
         return *this;
     }
@@ -59,36 +64,82 @@
 struct IDiagnostics {
     virtual ~IDiagnostics() = default;
 
-    virtual void error(const DiagMessage& message) = 0;
-    virtual void warn(const DiagMessage& message) = 0;
-    virtual void note(const DiagMessage& message) = 0;
+    enum class Level {
+        Note,
+        Warn,
+        Error
+    };
+
+    virtual void log(Level level, DiagMessageActual& actualMsg) = 0;
+
+    virtual void error(const DiagMessage& message) {
+        DiagMessageActual actual = message.build();
+        log(Level::Error, actual);
+    }
+
+    virtual void warn(const DiagMessage& message) {
+        DiagMessageActual actual = message.build();
+        log(Level::Warn, actual);
+    }
+
+    virtual void note(const DiagMessage& message) {
+        DiagMessageActual actual = message.build();
+        log(Level::Note, actual);
+    }
 };
 
-struct StdErrDiagnostics : public IDiagnostics {
+class StdErrDiagnostics : public IDiagnostics {
+public:
+    StdErrDiagnostics() = default;
+
+    void log(Level level, DiagMessageActual& actualMsg) override {
+        const char* tag;
+
+        switch (level) {
+        case Level::Error:
+            mNumErrors++;
+            if (mNumErrors > 20) {
+                return;
+            }
+            tag = "error";
+            break;
+
+        case Level::Warn:
+            tag = "warn";
+            break;
+
+        case Level::Note:
+            tag = "note";
+            break;
+        }
+
+        if (!actualMsg.source.path.empty()) {
+            std::cerr << actualMsg.source << ": ";
+        }
+        std::cerr << tag << ": " << actualMsg.message << "." << std::endl;
+    }
+
+private:
     size_t mNumErrors = 0;
 
-    void emit(const DiagMessage& msg, const char* tag) {
-        DiagMessageActual actual = msg.build();
-        if (!actual.source.path.empty()) {
-            std::cerr << actual.source << ": ";
-        }
-        std::cerr << tag << actual.message << "." << std::endl;
+    DISALLOW_COPY_AND_ASSIGN(StdErrDiagnostics);
+};
+
+class SourcePathDiagnostics : public IDiagnostics {
+public:
+    SourcePathDiagnostics(const Source& src, IDiagnostics* diag) : mSource(src), mDiag(diag) {
     }
 
-    void error(const DiagMessage& msg) override {
-        if (mNumErrors < 20) {
-            emit(msg, "error: ");
-        }
-        mNumErrors++;
+    void log(Level level, DiagMessageActual& actualMsg) override {
+        actualMsg.source.path = mSource.path;
+        mDiag->log(level, actualMsg);
     }
 
-    void warn(const DiagMessage& msg) override {
-        emit(msg, "warn: ");
-    }
+private:
+    Source mSource;
+    IDiagnostics* mDiag;
 
-    void note(const DiagMessage& msg) override {
-        emit(msg, "note: ");
-    }
+    DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/ResourceTable_test.cpp b/tools/aapt2/ResourceTable_test.cpp
index 180bd11..d6c52ab 100644
--- a/tools/aapt2/ResourceTable_test.cpp
+++ b/tools/aapt2/ResourceTable_test.cpp
@@ -28,33 +28,23 @@
 
 namespace aapt {
 
-struct ResourceTableTest : public ::testing::Test {
-    struct EmptyDiagnostics : public IDiagnostics {
-        void error(const DiagMessage& msg) override {}
-        void warn(const DiagMessage& msg) override {}
-        void note(const DiagMessage& msg) override {}
-    };
-
-    EmptyDiagnostics mDiagnostics;
-};
-
-TEST_F(ResourceTableTest, FailToAddResourceWithBadName) {
+TEST(ResourceTableTest, FailToAddResourceWithBadName) {
     ResourceTable table;
 
     EXPECT_FALSE(table.addResource(
             ResourceNameRef(u"android", ResourceType::kId, u"hey,there"),
             ConfigDescription{}, "",
             test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 
     EXPECT_FALSE(table.addResource(
             ResourceNameRef(u"android", ResourceType::kId, u"hey:there"),
             ConfigDescription{}, "",
             test::ValueBuilder<Id>().setSource("test.xml", 21u).build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 }
 
-TEST_F(ResourceTableTest, AddOneResource) {
+TEST(ResourceTableTest, AddOneResource) {
     ResourceTable table;
 
     EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/id"),
@@ -62,12 +52,12 @@
                                   "",
                                   test::ValueBuilder<Id>()
                                           .setSource("test/path/file.xml", 23u).build(),
-                                  &mDiagnostics));
+                                  test::getDiagnostics()));
 
     ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
 }
 
-TEST_F(ResourceTableTest, AddMultipleResources) {
+TEST(ResourceTableTest, AddMultipleResources) {
     ResourceTable table;
 
     ConfigDescription config;
@@ -79,21 +69,21 @@
             config,
             "",
             test::ValueBuilder<Id>().setSource("test/path/file.xml", 10u).build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 
     EXPECT_TRUE(table.addResource(
             test::parseNameOrDie(u"@android:attr/id"),
             config,
             "",
             test::ValueBuilder<Id>().setSource("test/path/file.xml", 12u).build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 
     EXPECT_TRUE(table.addResource(
             test::parseNameOrDie(u"@android:string/ok"),
             config,
             "",
             test::ValueBuilder<Id>().setSource("test/path/file.xml", 14u).build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 
     EXPECT_TRUE(table.addResource(
             test::parseNameOrDie(u"@android:string/ok"),
@@ -102,7 +92,7 @@
             test::ValueBuilder<BinaryPrimitive>(android::Res_value{})
                     .setSource("test/path/file.xml", 20u)
                     .build(),
-            &mDiagnostics));
+            test::getDiagnostics()));
 
     ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/layout_width"));
     ASSERT_NE(nullptr, test::getValue<Id>(&table, u"@android:attr/id"));
@@ -111,37 +101,37 @@
                                                                 languageConfig));
 }
 
-TEST_F(ResourceTableTest, OverrideWeakResourceValue) {
+TEST(ResourceTableTest, OverrideWeakResourceValue) {
     ResourceTable table;
 
     ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
-                                  "", util::make_unique<Attribute>(true), &mDiagnostics));
+                                  "", util::make_unique<Attribute>(true), test::getDiagnostics()));
 
     Attribute* attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
     ASSERT_NE(nullptr, attr);
     EXPECT_TRUE(attr->isWeak());
 
     ASSERT_TRUE(table.addResource(test::parseNameOrDie(u"@android:attr/foo"), ConfigDescription{},
-                                  "", util::make_unique<Attribute>(false), &mDiagnostics));
+                                  "", util::make_unique<Attribute>(false), test::getDiagnostics()));
 
     attr = test::getValue<Attribute>(&table, u"@android:attr/foo");
     ASSERT_NE(nullptr, attr);
     EXPECT_FALSE(attr->isWeak());
 }
 
-TEST_F(ResourceTableTest, ProductVaryingValues) {
+TEST(ResourceTableTest, ProductVaryingValues) {
     ResourceTable table;
 
     EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
                                   test::parseConfigOrDie("land"),
                                   "tablet",
                                   util::make_unique<Id>(),
-                                  &mDiagnostics));
+                                  test::getDiagnostics()));
     EXPECT_TRUE(table.addResource(test::parseNameOrDie(u"@android:string/foo"),
                                   test::parseConfigOrDie("land"),
                                   "phone",
                                   util::make_unique<Id>(),
-                                  &mDiagnostics));
+                                  test::getDiagnostics()));
 
     EXPECT_NE(nullptr, test::getValueForConfigAndProduct<Id>(&table, u"@android:string/foo",
                                                              test::parseConfigOrDie("land"),
diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp
index e93c2fba..2b2d348 100644
--- a/tools/aapt2/StringPool_test.cpp
+++ b/tools/aapt2/StringPool_test.cpp
@@ -20,8 +20,6 @@
 #include <gtest/gtest.h>
 #include <string>
 
-using namespace android;
-
 namespace aapt {
 
 TEST(StringPoolTest, InsertOneString) {
@@ -171,24 +169,28 @@
 }
 
 TEST(StringPoolTest, FlattenEmptyStringPoolUtf8) {
+    using namespace android; // For NO_ERROR on Windows.
+
     StringPool pool;
     BigBuffer buffer(1024);
     StringPool::flattenUtf8(&buffer, pool);
 
     std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-    android::ResStringPool test;
-    ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+    ResStringPool test;
+    ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
 }
 
 TEST(StringPoolTest, FlattenOddCharactersUtf16) {
+    using namespace android; // For NO_ERROR on Windows.
+
     StringPool pool;
     pool.makeRef(u"\u093f");
     BigBuffer buffer(1024);
     StringPool::flattenUtf16(&buffer, pool);
 
     std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-    android::ResStringPool test;
-    ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+    ResStringPool test;
+    ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
     size_t len = 0;
     const char16_t* str = test.stringAt(0, &len);
     EXPECT_EQ(1u, len);
@@ -199,6 +201,8 @@
 constexpr const char16_t* sLongString = u"バッテリーを長持ちさせるため、バッテリーセーバーは端末のパフォーマンスを抑え、バイブレーション、位置情報サービス、大半のバックグラウンドデータを制限します。メール、SMSや、同期を使 用するその他のアプリは、起動しても更新されないことがあります。バッテリーセーバーは端末の充電中は自動的にOFFになります。";
 
 TEST(StringPoolTest, FlattenUtf8) {
+    using namespace android; // For NO_ERROR on Windows.
+
     StringPool pool;
 
     StringPool::Ref ref1 = pool.makeRef(u"hello");
@@ -219,8 +223,8 @@
 
     std::unique_ptr<uint8_t[]> data = util::copy(buffer);
     {
-        android::ResStringPool test;
-        ASSERT_EQ(test.setTo(data.get(), buffer.size()), android::NO_ERROR);
+        ResStringPool test;
+        ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR);
 
         EXPECT_EQ(util::getString(test, 0), u"hello");
         EXPECT_EQ(util::getString(test, 1), u"goodbye");
diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp
index be26b52..99c2077 100644
--- a/tools/aapt2/compile/PseudolocaleGenerator.cpp
+++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp
@@ -20,6 +20,8 @@
 #include "compile/PseudolocaleGenerator.h"
 #include "compile/Pseudolocalizer.h"
 
+#include <algorithm>
+
 namespace aapt {
 
 std::unique_ptr<StyledString> pseudolocalizeStyledString(StyledString* string,
diff --git a/tools/aapt2/flatten/TableFlattener.cpp b/tools/aapt2/flatten/TableFlattener.cpp
index da81046..28a7928 100644
--- a/tools/aapt2/flatten/TableFlattener.cpp
+++ b/tools/aapt2/flatten/TableFlattener.cpp
@@ -24,6 +24,7 @@
 #include "util/BigBuffer.h"
 
 #include <android-base/macros.h>
+#include <algorithm>
 #include <type_traits>
 #include <numeric>
 
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 3eac633..570cd96 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -21,6 +21,7 @@
 #include "xml/XmlDom.h"
 
 #include <androidfw/ResourceTypes.h>
+#include <algorithm>
 #include <utils/misc.h>
 #include <vector>
 
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index fef5ca3..4e6eb81 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -46,6 +46,8 @@
 
     ::testing::AssertionResult flatten(xml::XmlResource* doc, android::ResXMLTree* outTree,
                                        XmlFlattenerOptions options = {}) {
+        using namespace android; // For NO_ERROR on windows because it is a macro.
+
         BigBuffer buffer(1024);
         XmlFlattener flattener(&buffer, options);
         if (!flattener.consume(mContext.get(), doc)) {
@@ -53,7 +55,7 @@
         }
 
         std::unique_ptr<uint8_t[]> data = util::copy(buffer);
-        if (outTree->setTo(data.get(), buffer.size(), true) != android::NO_ERROR) {
+        if (outTree->setTo(data.get(), buffer.size(), true) != NO_ERROR) {
             return ::testing::AssertionFailure() << "flattened XML is corrupt";
         }
         return ::testing::AssertionSuccess();
diff --git a/tools/aapt2/java/AnnotationProcessor.cpp b/tools/aapt2/java/AnnotationProcessor.cpp
index ba74439..b7e7f90 100644
--- a/tools/aapt2/java/AnnotationProcessor.cpp
+++ b/tools/aapt2/java/AnnotationProcessor.cpp
@@ -21,7 +21,7 @@
 
 namespace aapt {
 
-void AnnotationProcessor::appendCommentLine(const std::string& comment) {
+void AnnotationProcessor::appendCommentLine(std::string& comment) {
     static const std::string sDeprecated = "@deprecated";
     static const std::string sSystemApi = "@SystemApi";
 
@@ -29,8 +29,14 @@
         mAnnotationBitMask |= kDeprecated;
     }
 
-    if (comment.find(sSystemApi) != std::string::npos) {
+    std::string::size_type idx = comment.find(sSystemApi);
+    if (idx != std::string::npos) {
         mAnnotationBitMask |= kSystemApi;
+        comment.erase(comment.begin() + idx, comment.begin() + idx + sSystemApi.size());
+    }
+
+    if (util::trimWhitespace(comment).empty()) {
+        return;
     }
 
     if (!mHasComments) {
@@ -46,7 +52,8 @@
     for (StringPiece16 line : util::tokenize(comment, u'\n')) {
         line = util::trimWhitespace(line);
         if (!line.empty()) {
-            appendCommentLine(util::utf16ToUtf8(line));
+            std::string utf8Line = util::utf16ToUtf8(line);
+            appendCommentLine(utf8Line);
         }
     }
 }
@@ -55,7 +62,8 @@
     for (StringPiece line : util::tokenize(comment, '\n')) {
         line = util::trimWhitespace(line);
         if (!line.empty()) {
-            appendCommentLine(line.toString());
+            std::string utf8Line = line.toString();
+            appendCommentLine(utf8Line);
         }
     }
 }
diff --git a/tools/aapt2/java/AnnotationProcessor.h b/tools/aapt2/java/AnnotationProcessor.h
index 0fc5b08..8309dd9 100644
--- a/tools/aapt2/java/AnnotationProcessor.h
+++ b/tools/aapt2/java/AnnotationProcessor.h
@@ -43,7 +43,6 @@
  *  /\*
  *   * This is meant to be hidden because
  *   * It is system api. Also it is @deprecated
- *   * @SystemApi
  *   *\/
  *
  * Output Annotations:
@@ -79,7 +78,7 @@
     bool mHasComments = false;
     uint32_t mAnnotationBitMask = 0;
 
-    void appendCommentLine(const std::string& line);
+    void appendCommentLine(std::string& line);
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/java/AnnotationProcessor_test.cpp b/tools/aapt2/java/AnnotationProcessor_test.cpp
index da96b84..5a39add 100644
--- a/tools/aapt2/java/AnnotationProcessor_test.cpp
+++ b/tools/aapt2/java/AnnotationProcessor_test.cpp
@@ -14,57 +14,18 @@
  * limitations under the License.
  */
 
-#include "ResourceParser.h"
-#include "ResourceTable.h"
-#include "ResourceValues.h"
 #include "java/AnnotationProcessor.h"
-#include "test/Builders.h"
-#include "test/Context.h"
-#include "xml/XmlPullParser.h"
-
-#include <gtest/gtest.h>
+#include "test/Test.h"
 
 namespace aapt {
 
-struct AnnotationProcessorTest : public ::testing::Test {
-    std::unique_ptr<IAaptContext> mContext;
-    ResourceTable mTable;
-
-    void SetUp() override {
-        mContext = test::ContextBuilder().build();
-    }
-
-    ::testing::AssertionResult parse(const StringPiece& str) {
-        ResourceParserOptions options;
-        ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{}, ConfigDescription{},
-                              options);
-        std::stringstream in;
-        in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
-        xml::XmlPullParser xmlParser(in);
-        if (parser.parse(&xmlParser)) {
-            return ::testing::AssertionSuccess();
-        }
-        return ::testing::AssertionFailure();
-    }
-};
-
-TEST_F(AnnotationProcessorTest, EmitsDeprecated) {
-    ASSERT_TRUE(parse(R"EOF(
-    <resources>
-      <declare-styleable name="foo">      
-        <!-- Some comment, and it should contain
-             a marker word, something that marks
-             this resource as nor needed.
-             {@deprecated That's the marker! } -->
-        <attr name="autoText" format="boolean" />
-      </declare-styleable>
-    </resources>)EOF"));
-
-    Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/autoText");
-    ASSERT_NE(nullptr, attr);
+TEST(AnnotationProcessorTest, EmitsDeprecated) {
+    const char* comment = "Some comment, and it should contain a marker word, "
+                          "something that marks this resource as nor needed. "
+                          "{@deprecated That's the marker! }";
 
     AnnotationProcessor processor;
-    processor.appendComment(attr->getComment());
+    processor.appendComment(comment);
 
     std::stringstream result;
     processor.writeToStream(&result, "");
@@ -73,6 +34,19 @@
     EXPECT_NE(std::string::npos, annotations.find("@Deprecated"));
 }
 
+TEST(AnnotationProcessorTest, EmitsSystemApiAnnotationAndRemovesFromComment) {
+    AnnotationProcessor processor;
+    processor.appendComment("@SystemApi This is a system API");
+
+    std::stringstream result;
+    processor.writeToStream(&result, "");
+    std::string annotations = result.str();
+
+    EXPECT_NE(std::string::npos, annotations.find("@android.annotation.SystemApi"));
+    EXPECT_EQ(std::string::npos, annotations.find("@SystemApi"));
+    EXPECT_NE(std::string::npos, annotations.find("This is a system API"));
+}
+
 } // namespace aapt
 
 
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 53e0f6f..d45328f 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -125,7 +125,7 @@
     void writeToStream(const StringPiece& prefix, bool final, std::ostream* out) const override {
         ClassMember::writeToStream(prefix, final, out);
 
-        *out << "public static final int[] " << mName << "={";
+        *out << prefix << "public static final int[] " << mName << "={";
 
         const auto begin = mElements.begin();
         const auto end = mElements.end();
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index 092bab24..24347a1 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -188,8 +188,8 @@
 
 struct StyleableAttr {
     const Reference* attrRef;
-    std::shared_ptr<Attribute> attribute;
     std::string fieldName;
+    std::unique_ptr<SymbolTable::Symbol> symbol;
 };
 
 static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
@@ -245,8 +245,9 @@
         // legal values for this attribute.
         const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
                 mangledReference);
-        if (symbol) {
-            styleableAttr.attribute = symbol->attribute;
+        if (symbol && symbol->attribute) {
+            // Copy the symbol data structure because the returned instance can be destroyed.
+            styleableAttr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
         }
         sortedAttributes.push_back(std::move(styleableAttr));
     }
@@ -273,6 +274,16 @@
                 "<tr><th>Attribute</th><th>Description</th></tr>\n";
 
         for (const StyleableAttr& entry : sortedAttributes) {
+            if (!entry.symbol) {
+                continue;
+            }
+
+            if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                    !entry.symbol->isPublic) {
+                // Don't write entries for non-public attributes.
+                continue;
+            }
+
             const ResourceName& attrName = entry.attrRef->name.value();
             styleableComment << "<tr><td>";
             styleableComment << "<code>{@link #"
@@ -284,14 +295,30 @@
             styleableComment << "</td>";
 
             styleableComment << "<td>";
-            if (entry.attribute) {
-                styleableComment << entry.attribute->getComment();
+
+            // Only use the comment up until the first '.'. This is to stay compatible with
+            // the way old AAPT did it (presumably to keep it short and to avoid including
+            // annotations like @hide which would affect this Styleable).
+            StringPiece16 attrCommentLine = entry.symbol->attribute->getComment();
+            auto iter = std::find(attrCommentLine.begin(), attrCommentLine.end(), u'.');
+            if (iter != attrCommentLine.end()) {
+                attrCommentLine = attrCommentLine.substr(
+                        0, (iter - attrCommentLine.begin()) + 1);
             }
-            styleableComment << "</td></tr>\n";
+            styleableComment << attrCommentLine << "</td></tr>\n";
         }
         styleableComment << "</table>\n";
 
         for (const StyleableAttr& entry : sortedAttributes) {
+            if (!entry.symbol) {
+                continue;
+            }
+
+            if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                    !entry.symbol->isPublic) {
+                // Don't write entries for non-public attributes.
+                continue;
+            }
             styleableComment << "@see #" << entry.fieldName << "\n";
         }
 
@@ -310,6 +337,17 @@
     // Now we emit the indices into the array.
     for (size_t i = 0; i < attrCount; i++) {
         const StyleableAttr& styleableAttr = sortedAttributes[i];
+
+        if (!styleableAttr.symbol) {
+            continue;
+        }
+
+        if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
+                !styleableAttr.symbol->isPublic) {
+            // Don't write entries for non-public attributes.
+            continue;
+        }
+
         const ResourceName& attrName = styleableAttr.attrRef->name.value();
 
         StringPiece16 packageName = attrName.package;
@@ -318,13 +356,13 @@
         }
 
         std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
-                sortedAttributes[i].fieldName, i);
+                sortedAttributes[i].fieldName, static_cast<uint32_t>(i));
 
         AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
 
         StringPiece16 comment = styleableAttr.attrRef->getComment();
-        if (styleableAttr.attribute && comment.empty()) {
-            comment = styleableAttr.attribute->getComment();
+        if (styleableAttr.symbol->attribute && comment.empty()) {
+            comment = styleableAttr.symbol->attribute->getComment();
         }
 
         if (!comment.empty()) {
@@ -342,10 +380,8 @@
 
         attrProcessor->appendNewLine();
 
-        if (styleableAttr.attribute) {
-            addAttributeFormatDoc(attrProcessor, styleableAttr.attribute.get());
-            attrProcessor->appendNewLine();
-        }
+        addAttributeFormatDoc(attrProcessor, styleableAttr.symbol->attribute.get());
+        attrProcessor->appendNewLine();
 
         std::stringstream doclavaName;
         doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 4f041b8..7d0aa83 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -43,7 +43,8 @@
     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
             .setPackageId(u"android", 0x01)
             .addSimple(u"@android:id/hey-man", ResourceId(0x01020000))
-            .addSimple(u"@android:attr/cool.attr", ResourceId(0x01010000))
+            .addValue(u"@android:attr/cool.attr", ResourceId(0x01010000),
+                      test::AttributeBuilder(false).build())
             .addValue(u"@android:styleable/hey.dude", ResourceId(0x01030000),
                       test::StyleableBuilder()
                               .addItem(u"@android:attr/cool.attr", ResourceId(0x01010000))
@@ -199,8 +200,10 @@
     std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
                 .setPackageId(u"android", 0x01)
                 .setPackageId(u"com.lib", 0x02)
-                .addSimple(u"@android:attr/bar", ResourceId(0x01010000))
-                .addSimple(u"@com.lib:attr/bar", ResourceId(0x02010000))
+                .addValue(u"@android:attr/bar", ResourceId(0x01010000),
+                          test::AttributeBuilder(false).build())
+                .addValue(u"@com.lib:attr/bar", ResourceId(0x02010000),
+                           test::AttributeBuilder(false).build())
                 .addValue(u"@android:styleable/foo", ResourceId(0x01030000),
                           test::StyleableBuilder()
                                   .addItem(u"@android:attr/bar", ResourceId(0x01010000))
@@ -239,13 +242,15 @@
     ASSERT_TRUE(generator.generate(u"android", &out));
     std::string actual = out.str();
 
-    EXPECT_NE(std::string::npos, actual.find(
-    R"EOF(/**
+    const char* expectedText =
+R"EOF(/**
      * This is a comment
      * @deprecated
      */
     @Deprecated
-    public static final int foo=0x01010000;)EOF"));
+    public static final int foo=0x01010000;)EOF";
+
+    EXPECT_NE(std::string::npos, actual.find(expectedText));
 }
 
 TEST(JavaClassGeneratorTest, CommentsForEnumAndFlagAttributesArePresent) {
diff --git a/tools/aapt2/java/ManifestClassGenerator_test.cpp b/tools/aapt2/java/ManifestClassGenerator_test.cpp
index a9ec318..d3bca70 100644
--- a/tools/aapt2/java/ManifestClassGenerator_test.cpp
+++ b/tools/aapt2/java/ManifestClassGenerator_test.cpp
@@ -104,28 +104,33 @@
     std::string actual;
     ASSERT_TRUE(getManifestClassText(context.get(), manifest.get(), &actual));
 
-    EXPECT_NE(std::string::npos, actual.find(
+    const char* expectedAccessInternet =
 R"EOF(    /**
      * Required to access the internet.
      * Added in API 1.
      */
-    public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF"));
+    public static final String ACCESS_INTERNET="android.permission.ACCESS_INTERNET";)EOF";
 
-    EXPECT_NE(std::string::npos, actual.find(
+    EXPECT_NE(std::string::npos, actual.find(expectedAccessInternet));
+
+    const char* expectedPlayOutside =
 R"EOF(    /**
      * @deprecated This permission is for playing outside.
      */
     @Deprecated
-    public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF"));
+    public static final String PLAY_OUTSIDE="android.permission.PLAY_OUTSIDE";)EOF";
 
-    EXPECT_NE(std::string::npos, actual.find(
+    EXPECT_NE(std::string::npos, actual.find(expectedPlayOutside));
+
+    const char* expectedSecret =
 R"EOF(    /**
      * This is a private permission for system only!
      * @hide
-     * @SystemApi
      */
     @android.annotation.SystemApi
-    public static final String SECRET="android.permission.SECRET";)EOF"));
+    public static final String SECRET="android.permission.SECRET";)EOF";
+
+    EXPECT_NE(std::string::npos, actual.find(expectedSecret));
 }
 
 } // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 9baf1d8..3779638 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -17,66 +17,199 @@
 #include "ResourceUtils.h"
 #include "link/ManifestFixer.h"
 #include "util/Util.h"
+#include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
 
 namespace aapt {
 
-static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
-    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
-    if (!attr) {
-        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
-                                         << "missing 'package' attribute");
-    } else if (ResourceUtils::isReference(attr->value)) {
-        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
-                                         << "value for attribute 'package' must not be a "
-                                            "reference");
-    } else if (!util::isJavaPackageName(attr->value)) {
-        context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
-                                         << "invalid package name '" << attr->value << "'");
-    } else {
-        return true;
+/**
+ * This is how PackageManager builds class names from AndroidManifest.xml entries.
+ */
+static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
+                                SourcePathDiagnostics* diag) {
+    std::u16string className = attr->value;
+    if (className.find(u'.') == std::u16string::npos) {
+        // There is no '.', so add one to the beginning.
+        className = u".";
+        className += attr->value;
     }
+
+    // We allow unqualified class names (ie: .HelloActivity)
+    // Since we don't know the package name, we can just make a fake one here and
+    // the test will be identical as long as the real package name is valid too.
+    Maybe<std::u16string> fullyQualifiedClassName =
+            util::getFullyQualifiedClassName(u"a", className);
+
+    StringPiece16 qualifiedClassName = fullyQualifiedClassName
+            ? fullyQualifiedClassName.value() : className;
+    if (!util::isJavaClassName(qualifiedClassName)) {
+        diag->error(DiagMessage(el->lineNumber)
+                    << "attribute 'android:name' in <"
+                    << el->name << "> tag must be a valid Java class name");
+        return false;
+    }
+    return true;
+}
+
+static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
+    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
+        return nameIsJavaClassName(el, attr, diag);
+    }
+    return true;
+}
+
+static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
+    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
+        return nameIsJavaClassName(el, attr, diag);
+    }
+    diag->error(DiagMessage(el->lineNumber)
+                << "<" << el->name << "> is missing attribute 'android:name'");
     return false;
 }
 
-static bool includeVersionName(IAaptContext* context, const Source& source,
-                               const StringPiece16& versionName, xml::Element* manifestEl) {
-    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
-        return true;
+static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
+    xml::Attribute* attr = el->findAttribute({}, u"package");
+    if (!attr) {
+        diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
+        return false;
+    } else if (ResourceUtils::isReference(attr->value)) {
+        diag->error(DiagMessage(el->lineNumber)
+                    << "attribute 'package' in <manifest> tag must not be a reference");
+        return false;
+    } else if (!util::isJavaPackageName(attr->value)) {
+        diag->error(DiagMessage(el->lineNumber)
+                    << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
+                    << attr->value << "'");
+        return false;
     }
-
-    manifestEl->attributes.push_back(xml::Attribute{
-            xml::kSchemaAndroid, u"versionName", versionName.toString() });
     return true;
 }
 
-static bool includeVersionCode(IAaptContext* context, const Source& source,
-                               const StringPiece16& versionCode, xml::Element* manifestEl) {
-    if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
+bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
+    // First verify some options.
+    if (mOptions.renameManifestPackage) {
+        if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
+            diag->error(DiagMessage() << "invalid manifest package override '"
+                        << mOptions.renameManifestPackage.value() << "'");
+            return false;
+        }
+    }
+
+    if (mOptions.renameInstrumentationTargetPackage) {
+        if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
+            diag->error(DiagMessage() << "invalid instrumentation target package override '"
+                        << mOptions.renameInstrumentationTargetPackage.value() << "'");
+            return false;
+        }
+    }
+
+    // Common intent-filter actions.
+    xml::XmlNodeAction intentFilterAction;
+    intentFilterAction[u"action"];
+    intentFilterAction[u"category"];
+    intentFilterAction[u"data"];
+
+    // Common meta-data actions.
+    xml::XmlNodeAction metaDataAction;
+
+    // Manifest actions.
+    xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
+    manifestAction.action(verifyManifest);
+    manifestAction.action([&](xml::Element* el) -> bool {
+        if (mOptions.versionNameDefault) {
+            if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
+                el->attributes.push_back(xml::Attribute{
+                        xml::kSchemaAndroid,
+                        u"versionName",
+                        mOptions.versionNameDefault.value() });
+            }
+        }
+
+        if (mOptions.versionCodeDefault) {
+            if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
+                el->attributes.push_back(xml::Attribute{
+                        xml::kSchemaAndroid,
+                        u"versionCode",
+                        mOptions.versionCodeDefault.value() });
+            }
+        }
         return true;
-    }
+    });
 
-    manifestEl->attributes.push_back(xml::Attribute{
-            xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
-    return true;
-}
+    // Uses-sdk actions.
+    manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
+        if (mOptions.minSdkVersionDefault &&
+                el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
+            // There was no minSdkVersion defined and we have a default to assign.
+            el->attributes.push_back(xml::Attribute{
+                    xml::kSchemaAndroid, u"minSdkVersion",
+                    mOptions.minSdkVersionDefault.value() });
+        }
 
-static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
-                       const ManifestFixerOptions& options) {
-    if (options.minSdkVersionDefault &&
-            el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
-        // There was no minSdkVersion defined and we have a default to assign.
-        el->attributes.push_back(xml::Attribute{
-                xml::kSchemaAndroid, u"minSdkVersion", options.minSdkVersionDefault.value() });
-    }
+        if (mOptions.targetSdkVersionDefault &&
+                el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
+            // There was no targetSdkVersion defined and we have a default to assign.
+            el->attributes.push_back(xml::Attribute{
+                    xml::kSchemaAndroid, u"targetSdkVersion",
+                    mOptions.targetSdkVersionDefault.value() });
+        }
+        return true;
+    });
 
-    if (options.targetSdkVersionDefault &&
-            el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
-        // There was no targetSdkVersion defined and we have a default to assign.
-        el->attributes.push_back(xml::Attribute{
-                xml::kSchemaAndroid, u"targetSdkVersion",
-                options.targetSdkVersionDefault.value() });
-    }
+    // Instrumentation actions.
+    manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
+        if (!mOptions.renameInstrumentationTargetPackage) {
+            return true;
+        }
+
+        if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
+            attr->value = mOptions.renameInstrumentationTargetPackage.value();
+        }
+        return true;
+    });
+
+    manifestAction[u"eat-comment"];
+    manifestAction[u"protected-broadcast"];
+    manifestAction[u"uses-permission"];
+    manifestAction[u"permission"];
+    manifestAction[u"permission-tree"];
+    manifestAction[u"permission-group"];
+
+    manifestAction[u"uses-configuration"];
+    manifestAction[u"uses-feature"];
+    manifestAction[u"uses-library"];
+    manifestAction[u"supports-screens"];
+    manifestAction[u"compatible-screens"];
+    manifestAction[u"supports-gl-texture"];
+
+    // Application actions.
+    xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
+    applicationAction.action(optionalNameIsJavaClassName);
+
+    // Activity actions.
+    applicationAction[u"activity"].action(requiredNameIsJavaClassName);
+    applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
+    applicationAction[u"activity"][u"meta-data"] = metaDataAction;
+
+    // Activity alias actions.
+    applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
+    applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
+
+    // Service actions.
+    applicationAction[u"service"].action(requiredNameIsJavaClassName);
+    applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
+    applicationAction[u"service"][u"meta-data"] = metaDataAction;
+
+    // Receiver actions.
+    applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
+    applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
+    applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
+
+    // Provider actions.
+    applicationAction[u"provider"].action(requiredNameIsJavaClassName);
+    applicationAction[u"provider"][u"grant-uri-permissions"];
+    applicationAction[u"provider"][u"meta-data"] = metaDataAction;
+    applicationAction[u"provider"][u"path-permissions"];
     return true;
 }
 
@@ -103,14 +236,7 @@
     StringPiece16 mPackage;
 };
 
-static bool renameManifestPackage(IAaptContext* context, const Source& source,
-                                  const StringPiece16& packageOverride, xml::Element* manifestEl) {
-    if (!util::isJavaPackageName(packageOverride)) {
-        context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
-                                         << packageOverride << "'");
-        return false;
-    }
-
+static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
     xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
 
     // We've already verified that the manifest element is present, with a package name specified.
@@ -124,32 +250,6 @@
     return true;
 }
 
-static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
-                                               const StringPiece16& packageOverride,
-                                               xml::Element* manifestEl) {
-    if (!util::isJavaPackageName(packageOverride)) {
-        context->getDiagnostics()->error(DiagMessage()
-                                         << "invalid instrumentation target package override '"
-                                         << packageOverride << "'");
-        return false;
-    }
-
-    xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
-    if (!instrumentationEl) {
-        // No error if there is no work to be done.
-        return true;
-    }
-
-    xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
-    if (!attr) {
-        // No error if there is no work to be done.
-        return true;
-    }
-
-    attr->value = packageOverride.toString();
-    return true;
-}
-
 bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
     xml::Element* root = xml::findRootElement(doc->root.get());
     if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
@@ -158,59 +258,31 @@
         return false;
     }
 
-    if (!verifyManifest(context, doc->file.source, root)) {
-        return false;
-    }
-
-    if (mOptions.versionCodeDefault) {
-        if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
-                                root)) {
-            return false;
-        }
-    }
-
-    if (mOptions.versionNameDefault) {
-        if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
-                                root)) {
-            return false;
-        }
-    }
-
-    if (mOptions.renameManifestPackage) {
-        // Rename manifest package.
-        if (!renameManifestPackage(context, doc->file.source,
-                                   mOptions.renameManifestPackage.value(), root)) {
-            return false;
-        }
-    }
-
-    if (mOptions.renameInstrumentationTargetPackage) {
-        if (!renameInstrumentationTargetPackage(context, doc->file.source,
-                                                mOptions.renameInstrumentationTargetPackage.value(),
-                                                root)) {
-            return false;
-        }
-    }
-
-    bool foundUsesSdk = false;
-    for (xml::Element* el : root->getChildElements()) {
-        if (!el->namespaceUri.empty()) {
-            continue;
-        }
-
-        if (el->name == u"uses-sdk") {
-            foundUsesSdk = true;
-            fixUsesSdk(context, doc->file.source, el, mOptions);
-        }
-    }
-
-    if (!foundUsesSdk && (mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)) {
+    if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
+            && root->findChild({}, u"uses-sdk") == nullptr) {
+        // Auto insert a <uses-sdk> element.
         std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
         usesSdk->name = u"uses-sdk";
-        fixUsesSdk(context, doc->file.source, usesSdk.get(), mOptions);
         root->addChild(std::move(usesSdk));
     }
 
+    xml::XmlActionExecutor executor;
+    if (!buildRules(&executor, context->getDiagnostics())) {
+        return false;
+    }
+
+    if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
+                          doc)) {
+        return false;
+    }
+
+    if (mOptions.renameManifestPackage) {
+        // Rename manifest package outside of the XmlActionExecutor.
+        // We need to extract the old package name and FullyQualify all class names.
+        if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
+            return false;
+        }
+    }
     return true;
 }
 
diff --git a/tools/aapt2/link/ManifestFixer.h b/tools/aapt2/link/ManifestFixer.h
index b8d9c83..4d9356a 100644
--- a/tools/aapt2/link/ManifestFixer.h
+++ b/tools/aapt2/link/ManifestFixer.h
@@ -19,6 +19,7 @@
 
 #include "process/IResourceTableConsumer.h"
 #include "util/Maybe.h"
+#include "xml/XmlActionExecutor.h"
 #include "xml/XmlDom.h"
 
 #include <string>
@@ -38,13 +39,17 @@
  * Verifies that the manifest is correctly formed and inserts defaults
  * where specified with ManifestFixerOptions.
  */
-struct ManifestFixer : public IXmlResourceConsumer {
-    ManifestFixerOptions mOptions;
-
+class ManifestFixer : public IXmlResourceConsumer {
+public:
     ManifestFixer(const ManifestFixerOptions& options) : mOptions(options) {
     }
 
     bool consume(IAaptContext* context, xml::XmlResource* doc) override;
+
+private:
+    bool buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag);
+
+    ManifestFixerOptions mOptions;
 };
 
 } // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer_test.cpp b/tools/aapt2/link/ManifestFixer_test.cpp
index 18c47df..f993720 100644
--- a/tools/aapt2/link/ManifestFixer_test.cpp
+++ b/tools/aapt2/link/ManifestFixer_test.cpp
@@ -166,9 +166,9 @@
     std::unique_ptr<xml::XmlResource> doc = verifyWithOptions(R"EOF(
       <manifest xmlns:android="http://schemas.android.com/apk/res/android"
                 package="android">
-        <application name=".MainApplication" text="hello">
-          <activity name=".activity.Start" />
-          <receiver name="com.google.android.Receiver" />
+        <application android:name=".MainApplication" text="hello">
+          <activity android:name=".activity.Start" />
+          <receiver android:name="com.google.android.Receiver" />
         </application>
       </manifest>)EOF", options);
     ASSERT_NE(nullptr, doc);
@@ -185,7 +185,7 @@
     xml::Element* applicationEl = manifestEl->findChild({}, u"application");
     ASSERT_NE(nullptr, applicationEl);
 
-    attr = applicationEl->findAttribute({}, u"name");
+    attr = applicationEl->findAttribute(xml::kSchemaAndroid, u"name");
     ASSERT_NE(nullptr, attr);
     EXPECT_EQ(std::u16string(u"android.MainApplication"), attr->value);
 
@@ -197,14 +197,14 @@
     el = applicationEl->findChild({}, u"activity");
     ASSERT_NE(nullptr, el);
 
-    attr = el->findAttribute({}, u"name");
+    attr = el->findAttribute(xml::kSchemaAndroid, u"name");
     ASSERT_NE(nullptr, el);
     EXPECT_EQ(std::u16string(u"android.activity.Start"), attr->value);
 
     el = applicationEl->findChild({}, u"receiver");
     ASSERT_NE(nullptr, el);
 
-    attr = el->findAttribute({}, u"name");
+    attr = el->findAttribute(xml::kSchemaAndroid, u"name");
     ASSERT_NE(nullptr, el);
     EXPECT_EQ(std::u16string(u"com.google.android.Receiver"), attr->value);
 }
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index 0a6a4a5..e684bb0 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -51,9 +51,28 @@
 class SymbolTable {
 public:
     struct Symbol {
+        Symbol() : Symbol(Maybe<ResourceId>{}) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i) : Symbol(i, nullptr) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr) :
+                Symbol(i, attr, false) {
+        }
+
+        Symbol(const Maybe<ResourceId>& i, const std::shared_ptr<Attribute>& attr, bool pub) :
+                id(i), attribute(attr), isPublic(pub) {
+        }
+
+        Symbol(const Symbol&) = default;
+        Symbol(Symbol&&) = default;
+        Symbol& operator=(const Symbol&) = default;
+        Symbol& operator=(Symbol&&) = default;
+
         Maybe<ResourceId> id;
         std::shared_ptr<Attribute> attribute;
-        bool isPublic;
+        bool isPublic = false;
     };
 
     SymbolTable() : mCache(200), mIdCache(200) {
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 86883f8..82e4fb0 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -388,6 +388,10 @@
 std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
                                                       const Source& source,
                                                       IDiagnostics* diag) {
+    // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
+    // causes errors when qualifying it with android::
+    using namespace android;
+
     std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
 
     if (!pbTable.has_string_pool()) {
@@ -395,29 +399,29 @@
         return {};
     }
 
-    android::ResStringPool valuePool;
-    android::status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
-                                               pbTable.string_pool().data().size());
-    if (result != android::NO_ERROR) {
+    ResStringPool valuePool;
+    status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
+                                      pbTable.string_pool().data().size());
+    if (result != NO_ERROR) {
         diag->error(DiagMessage(source) << "invalid string pool");
         return {};
     }
 
-    android::ResStringPool sourcePool;
+    ResStringPool sourcePool;
     if (pbTable.has_source_pool()) {
         result = sourcePool.setTo(pbTable.source_pool().data().data(),
                                   pbTable.source_pool().data().size());
-        if (result != android::NO_ERROR) {
+        if (result != NO_ERROR) {
             diag->error(DiagMessage(source) << "invalid source pool");
             return {};
         }
     }
 
-    android::ResStringPool symbolPool;
+    ResStringPool symbolPool;
     if (pbTable.has_symbol_pool()) {
         result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
                                   pbTable.symbol_pool().data().size());
-        if (result != android::NO_ERROR) {
+        if (result != NO_ERROR) {
             diag->error(DiagMessage(source) << "invalid symbol pool");
             return {};
         }
diff --git a/tools/aapt2/split/TableSplitter.cpp b/tools/aapt2/split/TableSplitter.cpp
index 0f7649b..4bfdb12 100644
--- a/tools/aapt2/split/TableSplitter.cpp
+++ b/tools/aapt2/split/TableSplitter.cpp
@@ -18,6 +18,7 @@
 #include "ResourceTable.h"
 #include "split/TableSplitter.h"
 
+#include <algorithm>
 #include <map>
 #include <set>
 #include <unordered_map>
diff --git a/tools/aapt2/test/Builders.h b/tools/aapt2/test/Builders.h
index 8c56ebc..8eb4bc8 100644
--- a/tools/aapt2/test/Builders.h
+++ b/tools/aapt2/test/Builders.h
@@ -238,7 +238,7 @@
     std::stringstream in;
     in << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" << str;
     StdErrDiagnostics diag;
-    std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, {});
+    std::unique_ptr<xml::XmlResource> doc = xml::inflate(&in, &diag, Source("test.xml"));
     assert(doc);
     return doc;
 }
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 348c32a..faccd477 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -41,15 +41,20 @@
 namespace test {
 
 struct DummyDiagnosticsImpl : public IDiagnostics {
-    void error(const DiagMessage& message) override {
-        DiagMessageActual actual = message.build();
-        std::cerr << actual.source << ": error: " << actual.message << "." << std::endl;
+    void log(Level level, DiagMessageActual& actualMsg) override {
+        switch (level) {
+        case Level::Note:
+            return;
+
+        case Level::Warn:
+            std::cerr << actualMsg.source << ": warn: " << actualMsg.message << "." << std::endl;
+            break;
+
+        case Level::Error:
+            std::cerr << actualMsg.source << ": error: " << actualMsg.message << "." << std::endl;
+            break;
+        }
     }
-    void warn(const DiagMessage& message) override {
-        DiagMessageActual actual = message.build();
-        std::cerr << actual.source << ": warn: " << actual.message << "." << std::endl;
-    }
-    void note(const DiagMessage& message) override {}
 };
 
 inline IDiagnostics* getDiagnostics() {
diff --git a/tools/aapt2/unflatten/BinaryResourceParser.cpp b/tools/aapt2/unflatten/BinaryResourceParser.cpp
index 33b505e..ec46751 100644
--- a/tools/aapt2/unflatten/BinaryResourceParser.cpp
+++ b/tools/aapt2/unflatten/BinaryResourceParser.cpp
@@ -26,7 +26,7 @@
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/TypeWrappers.h>
 #include <android-base/macros.h>
-
+#include <algorithm>
 #include <map>
 #include <string>
 
diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp
index 6428e98..bb093ab 100644
--- a/tools/aapt2/util/Files.cpp
+++ b/tools/aapt2/util/Files.cpp
@@ -17,6 +17,7 @@
 #include "util/Files.h"
 #include "util/Util.h"
 
+#include <algorithm>
 #include <cerrno>
 #include <cstdio>
 #include <dirent.h>
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index 10a2803..595db96 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -17,6 +17,8 @@
 #ifndef AAPT_MAYBE_H
 #define AAPT_MAYBE_H
 
+#include "util/TypeTraits.h"
+
 #include <cassert>
 #include <type_traits>
 #include <utility>
@@ -276,13 +278,15 @@
 }
 
 /**
- * Define the == operator between Maybe<T> and Maybe<U> if the operator T == U is defined.
- * Otherwise this won't be defined and the compiler will yell at the callsite instead of inside
- * Maybe.h.
+ * Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
+ * That way the compiler will show an error at the callsite when comparing two Maybe<> objects
+ * whose inner types can't be compared.
  */
 template <typename T, typename U>
-auto operator==(const Maybe<T>& a, const Maybe<U>& b)
--> decltype(std::declval<T> == std::declval<U>) {
+typename std::enable_if<
+        has_eq_op<T, U>::value,
+        bool
+>::type operator==(const Maybe<T>& a, const Maybe<U>& b) {
     if (a && b) {
         return a.value() == b.value();
     } else if (!a && !b) {
@@ -295,8 +299,10 @@
  * Same as operator== but negated.
  */
 template <typename T, typename U>
-auto operator!=(const Maybe<T>& a, const Maybe<U>& b)
--> decltype(std::declval<T> == std::declval<U>) {
+typename std::enable_if<
+        has_eq_op<T, U>::value,
+        bool
+>::type operator!=(const Maybe<T>& a, const Maybe<U>& b) {
     return !(a == b);
 }
 
diff --git a/tools/aapt2/xml/XmlActionExecutor.cpp b/tools/aapt2/xml/XmlActionExecutor.cpp
new file mode 100644
index 0000000..0ef67ea
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "xml/XmlActionExecutor.h"
+
+namespace aapt {
+namespace xml {
+
+static bool wrapperOne(XmlNodeAction::ActionFunc& f, Element* el, SourcePathDiagnostics*) {
+    return f(el);
+}
+
+static bool wrapperTwo(XmlNodeAction::ActionFuncWithDiag& f, Element* el,
+                       SourcePathDiagnostics* diag) {
+    return f(el, diag);
+}
+
+void XmlNodeAction::action(XmlNodeAction::ActionFunc f) {
+    mActions.emplace_back(std::bind(wrapperOne, std::move(f),
+                                    std::placeholders::_1,
+                                    std::placeholders::_2));
+}
+
+void XmlNodeAction::action(XmlNodeAction::ActionFuncWithDiag f) {
+    mActions.emplace_back(std::bind(wrapperTwo, std::move(f),
+                                    std::placeholders::_1,
+                                    std::placeholders::_2));
+}
+
+static void printElementToDiagMessage(const Element* el, DiagMessage* msg) {
+    *msg << "<";
+    if (!el->namespaceUri.empty()) {
+        *msg << el->namespaceUri << ":";
+    }
+    *msg << el->name << ">";
+}
+
+bool XmlNodeAction::execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag,
+                            Element* el) const {
+    bool error = false;
+    for (const ActionFuncWithDiag& action : mActions) {
+        error |= !action(el, diag);
+    }
+
+    for (Element* childEl : el->getChildElements()) {
+        if (childEl->namespaceUri.empty()) {
+            std::map<std::u16string, XmlNodeAction>::const_iterator iter =
+                    mMap.find(childEl->name);
+            if (iter != mMap.end()) {
+                error |= !iter->second.execute(policy, diag, childEl);
+                continue;
+            }
+        }
+
+        if (policy == XmlActionExecutorPolicy::Whitelist) {
+            DiagMessage errorMsg(childEl->lineNumber);
+            errorMsg << "unknown element ";
+            printElementToDiagMessage(childEl, &errorMsg);
+            errorMsg << " found";
+            diag->error(errorMsg);
+            error = true;
+        }
+    }
+    return !error;
+}
+
+bool XmlActionExecutor::execute(XmlActionExecutorPolicy policy, IDiagnostics* diag,
+                                XmlResource* doc) const {
+    SourcePathDiagnostics sourceDiag(doc->file.source, diag);
+
+    Element* el = findRootElement(doc);
+    if (!el) {
+        if (policy == XmlActionExecutorPolicy::Whitelist) {
+            sourceDiag.error(DiagMessage() << "no root XML tag found");
+            return false;
+        }
+        return true;
+    }
+
+    if (el->namespaceUri.empty()) {
+        std::map<std::u16string, XmlNodeAction>::const_iterator iter = mMap.find(el->name);
+        if (iter != mMap.end()) {
+            return iter->second.execute(policy, &sourceDiag, el);
+        }
+    }
+
+    if (policy == XmlActionExecutorPolicy::Whitelist) {
+        DiagMessage errorMsg(el->lineNumber);
+        errorMsg << "unknown element ";
+        printElementToDiagMessage(el, &errorMsg);
+        errorMsg << " found";
+        sourceDiag.error(errorMsg);
+        return false;
+    }
+    return true;
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlActionExecutor.h b/tools/aapt2/xml/XmlActionExecutor.h
new file mode 100644
index 0000000..36b94db
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_XML_XMLPATTERN_H
+#define AAPT_XML_XMLPATTERN_H
+
+#include "Diagnostics.h"
+#include "xml/XmlDom.h"
+
+#include <android-base/macros.h>
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+namespace aapt {
+namespace xml {
+
+enum class XmlActionExecutorPolicy {
+    /**
+     * Actions on run if elements are matched, errors occur only when actions return false.
+     */
+    None,
+
+    /**
+     * The actions defined must match and run. If an element is found that does not match
+     * an action, an error occurs.
+     */
+    Whitelist,
+};
+
+/**
+ * Contains the actions to perform at this XML node. This is a recursive data structure that
+ * holds XmlNodeActions for child XML nodes.
+ */
+class XmlNodeAction {
+public:
+    using ActionFuncWithDiag = std::function<bool(Element*, SourcePathDiagnostics*)>;
+    using ActionFunc = std::function<bool(Element*)>;
+
+    /**
+     * Find or create a child XmlNodeAction that will be performed for the child element
+     * with the name `name`.
+     */
+    XmlNodeAction& operator[](const std::u16string& name) {
+        return mMap[name];
+    }
+
+    /**
+     * Add an action to be performed at this XmlNodeAction.
+     */
+    void action(ActionFunc f);
+    void action(ActionFuncWithDiag);
+
+private:
+    friend class XmlActionExecutor;
+
+    bool execute(XmlActionExecutorPolicy policy, SourcePathDiagnostics* diag, Element* el) const;
+
+    std::map<std::u16string, XmlNodeAction> mMap;
+    std::vector<ActionFuncWithDiag> mActions;
+};
+
+/**
+ * Allows the definition of actions to execute at specific XML elements defined by their
+ * hierarchy.
+ */
+class XmlActionExecutor {
+public:
+    XmlActionExecutor() = default;
+
+    /**
+     * Find or create a root XmlNodeAction that will be performed for the root XML element
+     * with the name `name`.
+     */
+    XmlNodeAction& operator[](const std::u16string& name) {
+        return mMap[name];
+    }
+
+    /**
+     * Execute the defined actions for this XmlResource.
+     * Returns true if all actions return true, otherwise returns false.
+     */
+    bool execute(XmlActionExecutorPolicy policy, IDiagnostics* diag, XmlResource* doc) const;
+
+private:
+    std::map<std::u16string, XmlNodeAction> mMap;
+
+    DISALLOW_COPY_AND_ASSIGN(XmlActionExecutor);
+};
+
+} // namespace xml
+} // namespace aapt
+
+#endif /* AAPT_XML_XMLPATTERN_H */
diff --git a/tools/aapt2/xml/XmlActionExecutor_test.cpp b/tools/aapt2/xml/XmlActionExecutor_test.cpp
new file mode 100644
index 0000000..ebf287a
--- /dev/null
+++ b/tools/aapt2/xml/XmlActionExecutor_test.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/Test.h"
+#include "xml/XmlActionExecutor.h"
+
+namespace aapt {
+namespace xml {
+
+TEST(XmlActionExecutorTest, BuildsAccessibleNestedPattern) {
+    XmlActionExecutor executor;
+    XmlNodeAction& manifestAction = executor[u"manifest"];
+    XmlNodeAction& applicationAction = manifestAction[u"application"];
+
+    Element* manifestEl = nullptr;
+    manifestAction.action([&](Element* manifest) -> bool {
+        manifestEl = manifest;
+        return true;
+    });
+
+    Element* applicationEl = nullptr;
+    applicationAction.action([&](Element* application) -> bool {
+        applicationEl = application;
+        return true;
+    });
+
+    std::unique_ptr<XmlResource> doc = test::buildXmlDom("<manifest><application /></manifest>");
+
+    StdErrDiagnostics diag;
+    ASSERT_TRUE(executor.execute(XmlActionExecutorPolicy::None, &diag, doc.get()));
+    ASSERT_NE(nullptr, manifestEl);
+    EXPECT_EQ(std::u16string(u"manifest"), manifestEl->name);
+
+    ASSERT_NE(nullptr, applicationEl);
+    EXPECT_EQ(std::u16string(u"application"), applicationEl->name);
+}
+
+TEST(XmlActionExecutorTest, FailsWhenUndefinedHierarchyExists) {
+    XmlActionExecutor executor;
+    executor[u"manifest"][u"application"];
+
+    std::unique_ptr<XmlResource> doc = test::buildXmlDom(
+            "<manifest><application /><activity /></manifest>");
+    StdErrDiagnostics diag;
+    ASSERT_FALSE(executor.execute(XmlActionExecutorPolicy::Whitelist, &diag, doc.get()));
+}
+
+} // namespace xml
+} // namespace aapt
diff --git a/tools/aapt2/xml/XmlDom.cpp b/tools/aapt2/xml/XmlDom.cpp
index d27b62fd..0ce333a 100644
--- a/tools/aapt2/xml/XmlDom.cpp
+++ b/tools/aapt2/xml/XmlDom.cpp
@@ -228,20 +228,24 @@
 
 std::unique_ptr<XmlResource> inflate(const void* data, size_t dataLen, IDiagnostics* diag,
                                      const Source& source) {
+    // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
+    // causes errors when qualifying it with android::
+    using namespace android;
+
     std::unique_ptr<Node> root;
     std::stack<Node*> nodeStack;
 
-    android::ResXMLTree tree;
-    if (tree.setTo(data, dataLen) != android::NO_ERROR) {
+    ResXMLTree tree;
+    if (tree.setTo(data, dataLen) != NO_ERROR) {
         return {};
     }
 
-    android::ResXMLParser::event_code_t code;
-    while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
-            code != android::ResXMLParser::END_DOCUMENT) {
+    ResXMLParser::event_code_t code;
+    while ((code = tree.next()) != ResXMLParser::BAD_DOCUMENT &&
+            code != ResXMLParser::END_DOCUMENT) {
         std::unique_ptr<Node> newNode;
         switch (code) {
-            case android::ResXMLParser::START_NAMESPACE: {
+            case ResXMLParser::START_NAMESPACE: {
                 std::unique_ptr<Namespace> node = util::make_unique<Namespace>();
                 size_t len;
                 const char16_t* str16 = tree.getNamespacePrefix(&len);
@@ -257,7 +261,7 @@
                 break;
             }
 
-            case android::ResXMLParser::START_TAG: {
+            case ResXMLParser::START_TAG: {
                 std::unique_ptr<Element> node = util::make_unique<Element>();
                 size_t len;
                 const char16_t* str16 = tree.getElementNamespace(&len);
@@ -276,7 +280,7 @@
                 break;
             }
 
-            case android::ResXMLParser::TEXT: {
+            case ResXMLParser::TEXT: {
                 std::unique_ptr<Text> node = util::make_unique<Text>();
                 size_t len;
                 const char16_t* str16 = tree.getText(&len);
@@ -287,8 +291,8 @@
                 break;
             }
 
-            case android::ResXMLParser::END_NAMESPACE:
-            case android::ResXMLParser::END_TAG:
+            case ResXMLParser::END_NAMESPACE:
+            case ResXMLParser::END_TAG:
                 assert(!nodeStack.empty());
                 nodeStack.pop();
                 break;
diff --git a/tools/aapt2/xml/XmlUtil_test.cpp b/tools/aapt2/xml/XmlUtil_test.cpp
index 7796b3e..319e770 100644
--- a/tools/aapt2/xml/XmlUtil_test.cpp
+++ b/tools/aapt2/xml/XmlUtil_test.cpp
@@ -33,22 +33,22 @@
             xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res/a");
     AAPT_ASSERT_TRUE(p);
     EXPECT_EQ(std::u16string(u"a"), p.value().package);
-    EXPECT_EQ(false, p.value().privateNamespace);
+    EXPECT_FALSE(p.value().privateNamespace);
 
     p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/android");
     AAPT_ASSERT_TRUE(p);
     EXPECT_EQ(std::u16string(u"android"), p.value().package);
-    EXPECT_EQ(true, p.value().privateNamespace);
+    EXPECT_TRUE(p.value().privateNamespace);
 
     p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/prv/res/com.test");
     AAPT_ASSERT_TRUE(p);
     EXPECT_EQ(std::u16string(u"com.test"), p.value().package);
-    EXPECT_EQ(true, p.value().privateNamespace);
+    EXPECT_TRUE(p.value().privateNamespace);
 
     p = xml::extractPackageFromNamespace(u"http://schemas.android.com/apk/res-auto");
     AAPT_ASSERT_TRUE(p);
     EXPECT_EQ(std::u16string(), p.value().package);
-    EXPECT_EQ(true, p.value().privateNamespace);
+    EXPECT_TRUE(p.value().privateNamespace);
 }
 
 } // namespace aapt
diff --git a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
index 01af669..381eb1f 100644
--- a/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/Choreographer_Delegate.java
@@ -15,8 +15,11 @@
  */
 package android.view;
 
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import java.lang.reflect.Field;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -64,4 +67,18 @@
 
         thisChoreographer.doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
     }
+
+    public static void dispose() {
+        try {
+            Field threadInstanceField = Choreographer.class.getDeclaredField("sThreadInstance");
+            threadInstanceField.setAccessible(true);
+            @SuppressWarnings("unchecked") ThreadLocal<Choreographer> threadInstance =
+                    (ThreadLocal<Choreographer>) threadInstanceField.get(null);
+            threadInstance.remove();
+        } catch (ReflectiveOperationException e) {
+            assert false;
+            Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                    "Unable to clear Choreographer memory.", e, null);
+        }
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
index 30512aa..ea9a255 100644
--- a/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
+++ b/tools/layoutlib/bridge/src/android/view/RectShadowPainter.java
@@ -44,6 +44,11 @@
     private static final float PERPENDICULAR_ANGLE = 90f;
 
     public static void paintShadow(Outline viewOutline, float elevation, Canvas canvas) {
+        Rect outline = new Rect();
+        if (!viewOutline.getRect(outline)) {
+            throw new IllegalArgumentException("Outline is not a rect shadow");
+        }
+
         float shadowSize = elevationToShadow(elevation);
         int saved = modifyCanvas(canvas, shadowSize);
         if (saved == -1) {
@@ -54,8 +59,7 @@
             cornerPaint.setStyle(Style.FILL);
             Paint edgePaint = new Paint(cornerPaint);
             edgePaint.setAntiAlias(false);
-            Rect outline = viewOutline.mRect;
-            float radius = viewOutline.mRadius;
+            float radius = viewOutline.getRadius();
             float outerArcRadius = radius + shadowSize;
             int[] colors = {START_COLOR, START_COLOR, END_COLOR};
             cornerPaint.setShader(new RadialGradient(0, 0, outerArcRadius, colors,
diff --git a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
index 51d32e3..23caaf8 100644
--- a/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/ViewGroup_Delegate.java
@@ -64,7 +64,7 @@
     private static void drawShadow(ViewGroup parent, Canvas canvas, View child,
             Outline outline) {
         float elevation = getElevation(child, parent);
-        if(outline.mRect != null) {
+        if(outline.mMode == Outline.MODE_ROUND_RECT && outline.mRect != null) {
             RectShadowPainter.paintShadow(outline, elevation, canvas);
             return;
         }
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 2399b3a..89272fa 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
@@ -639,7 +639,8 @@
         }
         // Add value to defaultPropsMap if needed
         if (typeArrayAndPropertiesPair.getSecond() != null) {
-            Object key = getCurrentParser().getViewCookie();
+            BridgeXmlBlockParser parser = getCurrentParser();
+            Object key = parser != null ? parser.getViewCookie() : null;
             if (key != null) {
                 PropertiesMap defaultPropMap = mDefaultPropMaps.get(key);
                 if (defaultPropMap == null) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ce7104e..866b248 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -1453,6 +1453,7 @@
 
         if (createdLooper) {
             Bridge.cleanupThread();
+            Choreographer_Delegate.dispose();
         }
     }
 }
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index d8ead23..0e788e0c 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index 65d1dc5..bad296b 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
index 2da2cb9..adb58a3 100644
--- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/allwidgets.xml
@@ -197,6 +197,14 @@
         android:inputType="numberPassword"
         android:text="numeric password" />
 
+    <ToggleButton
+        android:id="@+id/toggleButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignTop="@id/editText4"
+        android:layout_toEndOf="@id/editText4"
+        android:text="New ToggleButton" />
+
     <EditText
         android:id="@id/editText5"
         android:layout_width="wrap_content"
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
index 034c8b2..8f570ae 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -31,8 +31,8 @@
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.layoutlib.bridge.android.RenderParamsFlags;
-import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.RenderAction;
 import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
 import com.android.layoutlib.bridge.intensive.setup.LayoutLibTestCallback;
 import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
@@ -42,8 +42,12 @@
 import com.android.utils.ILogger;
 
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -56,9 +60,12 @@
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
 
+import com.google.android.collect.Lists;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -105,6 +112,21 @@
     private static  ILogger sLogger;
     private static Bridge sBridge;
 
+    /** List of log messages generated by a render call. It can be used to find specific errors */
+    private static ArrayList<String> sRenderMessages = Lists.newArrayList();
+
+    @Rule
+    public static TestWatcher sRenderMessageWatcher = new TestWatcher() {
+        @Override
+        protected void succeeded(Description description) {
+            // We only check error messages if the rest of the test case was successful.
+            if (!sRenderMessages.isEmpty()) {
+                fail(description.getMethodName() + " render error message: " + sRenderMessages.get
+                        (0));
+            }
+        }
+    };
+
     static {
         // Test that System Properties are properly set.
         PLATFORM_DIR = getPlatformDir();
@@ -279,6 +301,11 @@
                 ConfigGenerator.getEnumMap(attrs), getLayoutLog());
     }
 
+    @Before
+    public void beforeTestCase() {
+        sRenderMessages.clear();
+    }
+
     /** Test activity.xml */
     @Test
     public void testActivity() throws ClassNotFoundException {
@@ -289,6 +316,9 @@
     @Test
     public void testAllWidgets() throws ClassNotFoundException {
         renderAndVerify("allwidgets.xml", "allwidgets.png");
+
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
     }
 
     @Test
@@ -299,6 +329,9 @@
     @Test
     public void testAllWidgetsTablet() throws ClassNotFoundException {
         renderAndVerify("allwidgets.xml", "allwidgets_tab.png", ConfigGenerator.NEXUS_7_2012);
+
+        // We expect fidelity warnings for Path.isConvex. Fail for anything else.
+        sRenderMessages.removeIf(message -> message.equals("Path.isConvex is not supported."));
     }
 
     private static void gc() {
@@ -649,6 +682,6 @@
     }
 
     private static void failWithMsg(@NonNull String msgFormat, Object... args) {
-        fail(args == null ? "" : String.format(msgFormat, args));
+        sRenderMessages.add(args == null ? msgFormat : String.format(msgFormat, args));
     }
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 1c7a308..0f84506 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -149,12 +149,11 @@
     void setAllowScansWithTraffic(int enabled);
     int getAllowScansWithTraffic();
 
-    void setHalBasedAutojoinOffload(int enabled);
-    int getHalBasedAutojoinOffload();
-
     boolean enableAutoJoinWhenAssociated(boolean enabled);
     boolean getEnableAutoJoinWhenAssociated();
 
+    void enableWifiConnectivityManager(boolean enabled);
+
     WifiConnectionStatistics getConnectionStatistics();
 
     void disableEphemeralNetwork(String SSID);
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 9e15d60..394934f 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -719,7 +719,7 @@
      * Get CA certificates.
      */
     @Nullable public X509Certificate[] getCaCertificates() {
-        if (mCaCerts != null || mCaCerts.length > 0) {
+        if (mCaCerts != null && mCaCerts.length > 0) {
             return mCaCerts;
         } else {
             return null;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 8c1fbc3..6984fe2 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -28,7 +28,6 @@
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -43,7 +42,6 @@
 import com.android.server.net.NetworkPinner;
 
 import java.net.InetAddress;
-import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
@@ -2721,25 +2719,14 @@
             throw e.rethrowFromSystemServer();
         }
     }
-    /**
-     * Set setting for enabling autojoin Offload thru Wifi HAL layer
-     * @hide
-     */
-    public void setHalBasedAutojoinOffload(int enabled) {
-        try {
-            mService.setHalBasedAutojoinOffload(enabled);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
 
     /**
-     * Get setting for enabling autojoin Offload thru Wifi HAL layer
+     * Enable/disable WifiConnectivityManager
      * @hide
      */
-    public int getHalBasedAutojoinOffload() {
+    public void enableWifiConnectivityManager(boolean enabled) {
         try {
-            return mService.getHalBasedAutojoinOffload();
+            mService.enableWifiConnectivityManager(enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 73ddbbc..f8c1ea3 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -810,19 +810,17 @@
     }
     /**
      * Stop an ongoing wifi PNO scan
-     * @param pnoSettings specifies various parameters for PNO; for more information look at
-     * {@link PnoSettings}
      * @param listener specifies which scan to cancel; must be same object as passed in {@link
      *  #startPnoScan}
      * TODO(rpius): Check if we can remove pnoSettings param in stop.
      * {@hide}
      */
-    public void stopPnoScan(PnoSettings pnoSettings, ScanListener listener) {
+    public void stopPnoScan(ScanListener listener) {
         Preconditions.checkNotNull(listener, "listener cannot be null");
         int key = removeListener(listener);
         if (key == INVALID_KEY) return;
         validateChannel();
-        sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key, pnoSettings);
+        sAsyncChannel.sendMessage(CMD_STOP_PNO_SCAN, 0, key);
     }
 
     /** specifies information about an access point of interest */