Merge "Minor cleanup for FingerprintDialog"
diff --git a/Android.bp b/Android.bp
index c5160fd..6cb1e5c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -418,6 +418,8 @@
         "media/java/android/media/IMediaRouterService.aidl",
         "media/java/android/media/IMediaScannerListener.aidl",
         "media/java/android/media/IMediaScannerService.aidl",
+        "media/java/android/media/IMediaSession2.aidl",
+        "media/java/android/media/IMediaSession2Callback.aidl",
         "media/java/android/media/IPlaybackConfigDispatcher.aidl",
         ":libaudioclient_aidl",
         "media/java/android/media/IRecordingConfigDispatcher.aidl",
diff --git a/api/current.txt b/api/current.txt
index 5365cac..ef8fec2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6420,6 +6420,7 @@
     method public android.content.ComponentName getMandatoryBackupTransport();
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
+    method public java.util.List<java.lang.String> getMeteredDataDisabled(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
     method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
@@ -6524,6 +6525,7 @@
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
+    method public java.util.List<java.lang.String> setMeteredDataDisabled(android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setNetworkLoggingEnabled(android.content.ComponentName, boolean);
     method public void setOrganizationColor(android.content.ComponentName, int);
     method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
@@ -7100,6 +7102,7 @@
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
     field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     field public static final java.lang.String HINT_ACTIONS = "actions";
+    field public static final java.lang.String HINT_CALLER_NEEDED = "caller_needed";
     field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
     field public static final java.lang.String HINT_LARGE = "large";
     field public static final java.lang.String HINT_LIST = "list";
@@ -7128,8 +7131,6 @@
     method public android.app.slice.Slice.Builder addAction(android.app.PendingIntent, android.app.slice.Slice, java.lang.String);
     method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.lang.String...);
     method public android.app.slice.Slice.Builder addBundle(android.os.Bundle, java.lang.String, java.util.List<java.lang.String>);
-    method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.lang.String...);
-    method public deprecated android.app.slice.Slice.Builder addColor(int, java.lang.String, java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addHints(java.lang.String...);
     method public android.app.slice.Slice.Builder addHints(java.util.List<java.lang.String>);
     method public android.app.slice.Slice.Builder addIcon(android.graphics.drawable.Icon, java.lang.String, java.lang.String...);
@@ -7152,7 +7153,6 @@
     method public int describeContents();
     method public android.app.PendingIntent getAction();
     method public android.os.Bundle getBundle();
-    method public deprecated int getColor();
     method public java.lang.String getFormat();
     method public java.util.List<java.lang.String> getHints();
     method public android.graphics.drawable.Icon getIcon();
@@ -7167,7 +7167,6 @@
     field public static final android.os.Parcelable.Creator<android.app.slice.SliceItem> CREATOR;
     field public static final java.lang.String FORMAT_ACTION = "action";
     field public static final java.lang.String FORMAT_BUNDLE = "bundle";
-    field public static final deprecated java.lang.String FORMAT_COLOR = "color";
     field public static final java.lang.String FORMAT_IMAGE = "image";
     field public static final java.lang.String FORMAT_INT = "int";
     field public static final java.lang.String FORMAT_REMOTE_INPUT = "input";
@@ -7182,9 +7181,10 @@
     method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
     method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
     method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
-    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
-    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
+    method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
+    method public deprecated void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
+    method public void registerSliceCallback(android.net.Uri, java.util.List<android.app.slice.SliceSpec>, android.app.slice.SliceManager.SliceCallback);
+    method public void registerSliceCallback(android.net.Uri, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor, android.app.slice.SliceManager.SliceCallback);
     method public void unpinSlice(android.net.Uri);
     method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback);
   }
@@ -7196,6 +7196,7 @@
   public abstract class SliceProvider extends android.content.ContentProvider {
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
+    method public final java.lang.String getBindingPackage();
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
     method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
@@ -10872,7 +10873,8 @@
     field public android.content.pm.ServiceInfo[] services;
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
-    field public android.content.pm.Signature[] signatures;
+    field public deprecated android.content.pm.Signature[] signatures;
+    field public android.content.pm.Signature[][] signingCertificateHistory;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
     field public deprecated int versionCode;
@@ -11075,6 +11077,8 @@
     method public abstract android.graphics.drawable.Drawable getUserBadgedIcon(android.graphics.drawable.Drawable, android.os.UserHandle);
     method public abstract java.lang.CharSequence getUserBadgedLabel(java.lang.CharSequence, android.os.UserHandle);
     method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo);
+    method public boolean hasSigningCertificate(java.lang.String, byte[], int);
+    method public boolean hasSigningCertificate(int, byte[], int);
     method public abstract boolean hasSystemFeature(java.lang.String);
     method public abstract boolean hasSystemFeature(java.lang.String, int);
     method public abstract boolean isInstantApp();
@@ -11100,6 +11104,8 @@
     method public abstract void setInstallerPackageName(java.lang.String, java.lang.String);
     method public abstract void updateInstantAppCookie(byte[]);
     method public abstract void verifyPendingInstall(int, int);
+    field public static final int CERT_INPUT_RAW_X509 = 0; // 0x0
+    field public static final int CERT_INPUT_SHA256 = 1; // 0x1
     field public static final int COMPONENT_ENABLED_STATE_DEFAULT = 0; // 0x0
     field public static final int COMPONENT_ENABLED_STATE_DISABLED = 2; // 0x2
     field public static final int COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED = 4; // 0x4
@@ -11218,7 +11224,8 @@
     field public static final int GET_RESOLVED_FILTER = 64; // 0x40
     field public static final int GET_SERVICES = 4; // 0x4
     field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
-    field public static final int GET_SIGNATURES = 64; // 0x40
+    field public static final deprecated int GET_SIGNATURES = 64; // 0x40
+    field public static final int GET_SIGNING_CERTIFICATES = 134217728; // 0x8000000
     field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
     field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
     field public static final int INSTALL_REASON_DEVICE_RESTORE = 2; // 0x2
@@ -11532,15 +11539,15 @@
 
   public final class AssetManager implements java.lang.AutoCloseable {
     method public void close();
-    method public final java.lang.String[] getLocales();
-    method public final java.lang.String[] list(java.lang.String) throws java.io.IOException;
-    method public final java.io.InputStream open(java.lang.String) throws java.io.IOException;
-    method public final java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
-    method public final android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
-    method public final android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
-    method public final android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
-    method public final android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
-    method public final android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
+    method public java.lang.String[] getLocales();
+    method public java.lang.String[] list(java.lang.String) throws java.io.IOException;
+    method public java.io.InputStream open(java.lang.String) throws java.io.IOException;
+    method public java.io.InputStream open(java.lang.String, int) throws java.io.IOException;
+    method public android.content.res.AssetFileDescriptor openFd(java.lang.String) throws java.io.IOException;
+    method public android.content.res.AssetFileDescriptor openNonAssetFd(java.lang.String) throws java.io.IOException;
+    method public android.content.res.AssetFileDescriptor openNonAssetFd(int, java.lang.String) throws java.io.IOException;
+    method public android.content.res.XmlResourceParser openXmlResourceParser(java.lang.String) throws java.io.IOException;
+    method public android.content.res.XmlResourceParser openXmlResourceParser(int, java.lang.String) throws java.io.IOException;
     field public static final int ACCESS_BUFFER = 3; // 0x3
     field public static final int ACCESS_RANDOM = 1; // 0x1
     field public static final int ACCESS_STREAMING = 2; // 0x2
@@ -13505,6 +13512,58 @@
     ctor public EmbossMaskFilter(float[], float, float, float);
   }
 
+  public final class ImageDecoder implements java.lang.AutoCloseable {
+    method public void close();
+    method public static android.graphics.ImageDecoder.Source createSource(android.content.ContentResolver, android.net.Uri);
+    method public static android.graphics.ImageDecoder.Source createSource(java.nio.ByteBuffer);
+    method public static android.graphics.ImageDecoder.Source createSource(java.io.File);
+    method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
+    method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException;
+    method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
+    method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source) throws java.io.IOException;
+    method public android.util.Size getSampledSize(int);
+    method public void setAllocator(int);
+    method public void setAsAlphaMask(boolean);
+    method public void setCrop(android.graphics.Rect);
+    method public void setMutable(boolean);
+    method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
+    method public void setPostProcessor(android.graphics.PostProcessor);
+    method public void setPreferRamOverQuality(boolean);
+    method public void setRequireUnpremultiplied(boolean);
+    method public void setResize(int, int);
+    method public void setResize(int);
+    field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
+    field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
+    field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
+    field public static final int ALLOCATOR_SOFTWARE = 1; // 0x1
+    field public static final int ERROR_SOURCE_ERROR = 3; // 0x3
+    field public static final int ERROR_SOURCE_EXCEPTION = 1; // 0x1
+    field public static final int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
+  }
+
+  public static abstract class ImageDecoder.Error implements java.lang.annotation.Annotation {
+  }
+
+  public static class ImageDecoder.ImageInfo {
+    method public java.lang.String getMimeType();
+    method public android.util.Size getSize();
+  }
+
+  public static class ImageDecoder.IncompleteException extends java.io.IOException {
+    ctor public ImageDecoder.IncompleteException();
+  }
+
+  public static abstract interface ImageDecoder.OnHeaderDecodedListener {
+    method public abstract void onHeaderDecoded(android.graphics.ImageDecoder, android.graphics.ImageDecoder.ImageInfo, android.graphics.ImageDecoder.Source);
+  }
+
+  public static abstract interface ImageDecoder.OnPartialImageListener {
+    method public abstract boolean onPartialImage(int, android.graphics.ImageDecoder.Source);
+  }
+
+  public static abstract class ImageDecoder.Source {
+  }
+
   public class ImageFormat {
     ctor public ImageFormat();
     method public static int getBitsPerPixel(int);
@@ -14075,6 +14134,10 @@
     ctor public PorterDuffXfermode(android.graphics.PorterDuff.Mode);
   }
 
+  public abstract interface PostProcessor {
+    method public abstract int onPostProcess(android.graphics.Canvas);
+  }
+
   public class RadialGradient extends android.graphics.Shader {
     ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
     ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
@@ -21544,6 +21607,7 @@
     method public android.location.LocationProvider getProvider(java.lang.String);
     method public java.util.List<java.lang.String> getProviders(boolean);
     method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
+    method public boolean isLocationEnabled();
     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);
@@ -21731,6 +21795,7 @@
     method public android.media.AudioFocusRequest.Builder setAcceptsDelayedFocusGain(boolean);
     method public android.media.AudioFocusRequest.Builder setAudioAttributes(android.media.AudioAttributes);
     method public android.media.AudioFocusRequest.Builder setFocusGain(int);
+    method public android.media.AudioFocusRequest.Builder setForceDucking(boolean);
     method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener);
     method public android.media.AudioFocusRequest.Builder setOnAudioFocusChangeListener(android.media.AudioManager.OnAudioFocusChangeListener, android.os.Handler);
     method public android.media.AudioFocusRequest.Builder setWillPauseWhenDucked(boolean);
@@ -23093,24 +23158,29 @@
     method public android.media.MediaDescription.Builder setTitle(java.lang.CharSequence);
   }
 
-  public final class MediaDrm {
+  public final class MediaDrm implements java.lang.AutoCloseable {
     ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
+    method public void close();
     method public void closeSession(byte[]);
-    method protected void finalize();
+    method public int getConnectedHdcpLevel();
     method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
     method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException;
+    method public int getMaxHdcpLevel();
+    method public int getMaxSessionCount();
+    method public int getOpenSessionCount();
     method public byte[] getPropertyByteArray(java.lang.String);
     method public java.lang.String getPropertyString(java.lang.String);
     method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
     method public byte[] getSecureStop(byte[]);
     method public java.util.List<byte[]> getSecureStops();
+    method public int getSecurityLevel(byte[]);
     method public static final boolean isCryptoSchemeSupported(java.util.UUID);
     method public static final boolean isCryptoSchemeSupported(java.util.UUID, java.lang.String);
     method public byte[] openSession() throws android.media.NotProvisionedException, android.media.ResourceBusyException;
     method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
     method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
     method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
-    method public final void release();
+    method public deprecated void release();
     method public void releaseAllSecureStops();
     method public void releaseSecureStops(byte[]);
     method public void removeKeys(byte[]);
@@ -23120,11 +23190,22 @@
     method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler);
     method public void setPropertyByteArray(java.lang.String, byte[]);
     method public void setPropertyString(java.lang.String, java.lang.String);
+    method public void setSecurityLevel(byte[], int);
     field public static final deprecated int EVENT_KEY_EXPIRED = 3; // 0x3
     field public static final int EVENT_KEY_REQUIRED = 2; // 0x2
     field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1
     field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5
     field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4
+    field public static final int HDCP_LEVEL_UNKNOWN = 0; // 0x0
+    field public static final int HDCP_NONE = 1; // 0x1
+    field public static final int HDCP_NO_DIGITAL_OUTPUT = 2147483647; // 0x7fffffff
+    field public static final int HDCP_V1 = 2; // 0x2
+    field public static final int HDCP_V2 = 3; // 0x3
+    field public static final int HDCP_V2_1 = 4; // 0x4
+    field public static final int HDCP_V2_2 = 5; // 0x5
+    field public static final int HW_SECURE_ALL = 5; // 0x5
+    field public static final int HW_SECURE_CRYPTO = 3; // 0x3
+    field public static final int HW_SECURE_DECODE = 4; // 0x4
     field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
     field public static final int KEY_TYPE_RELEASE = 3; // 0x3
     field public static final int KEY_TYPE_STREAMING = 1; // 0x1
@@ -23133,6 +23214,9 @@
     field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
     field public static final java.lang.String PROPERTY_VENDOR = "vendor";
     field public static final java.lang.String PROPERTY_VERSION = "version";
+    field public static final int SECURITY_LEVEL_UNKNOWN = 0; // 0x0
+    field public static final int SW_SECURE_CRYPTO = 1; // 0x1
+    field public static final int SW_SECURE_DECODE = 2; // 0x2
   }
 
   public final class MediaDrm.CryptoSession {
@@ -23142,6 +23226,9 @@
     method public boolean verify(byte[], byte[], byte[]);
   }
 
+  public static abstract class MediaDrm.HdcpLevel implements java.lang.annotation.Annotation {
+  }
+
   public static final class MediaDrm.KeyRequest {
     method public byte[] getData();
     method public java.lang.String getDefaultUrl();
@@ -23182,6 +23269,9 @@
     method public java.lang.String getDefaultUrl();
   }
 
+  public static abstract class MediaDrm.SecurityLevel implements java.lang.annotation.Annotation {
+  }
+
   public class MediaDrmException extends java.lang.Exception {
     ctor public MediaDrmException(java.lang.String);
   }
@@ -26181,6 +26271,7 @@
     method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
+    method public byte[] getNetworkWatchlistConfigHash();
     method public static deprecated android.net.Network getProcessDefaultNetwork();
     method public int getRestrictBackgroundStatus();
     method public boolean isActiveNetworkMetered();
@@ -35793,7 +35884,6 @@
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
     field public static final java.lang.String ACTION_CAST_SETTINGS = "android.settings.CAST_SETTINGS";
-    field public static final java.lang.String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_CHANNEL_NOTIFICATION_SETTINGS = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
     field public static final java.lang.String ACTION_DATA_ROAMING_SETTINGS = "android.settings.DATA_ROAMING_SETTINGS";
     field public static final java.lang.String ACTION_DATE_SETTINGS = "android.settings.DATE_SETTINGS";
@@ -35854,7 +35944,6 @@
     field public static final java.lang.String EXTRA_APP_PACKAGE = "android.provider.extra.APP_PACKAGE";
     field public static final java.lang.String EXTRA_AUTHORITIES = "authorities";
     field public static final java.lang.String EXTRA_BATTERY_SAVER_MODE_ENABLED = "android.settings.extra.battery_saver_mode_enabled";
-    field public static final java.lang.String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
     field public static final java.lang.String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_ENABLED = "android.settings.extra.do_not_disturb_mode_enabled";
     field public static final java.lang.String EXTRA_DO_NOT_DISTURB_MODE_MINUTES = "android.settings.extra.do_not_disturb_mode_minutes";
@@ -35966,11 +36055,11 @@
     field public static final deprecated java.lang.String HTTP_PROXY = "http_proxy";
     field public static final java.lang.String INPUT_METHOD_SELECTOR_VISIBILITY = "input_method_selector_visibility";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
-    field public static final java.lang.String LOCATION_MODE = "location_mode";
-    field public static final int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
-    field public static final int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
-    field public static final int LOCATION_MODE_OFF = 0; // 0x0
-    field public static final int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
+    field public static final deprecated java.lang.String LOCATION_MODE = "location_mode";
+    field public static final deprecated int LOCATION_MODE_BATTERY_SAVING = 2; // 0x2
+    field public static final deprecated int LOCATION_MODE_HIGH_ACCURACY = 3; // 0x3
+    field public static final deprecated int LOCATION_MODE_OFF = 0; // 0x0
+    field public static final deprecated int LOCATION_MODE_SENSORS_ONLY = 1; // 0x1
     field public static final deprecated java.lang.String LOCATION_PROVIDERS_ALLOWED = "location_providers_allowed";
     field public static final deprecated java.lang.String LOCK_PATTERN_ENABLED = "lock_pattern_autolock";
     field public static final deprecated java.lang.String LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED = "lock_pattern_tactile_feedback_enabled";
@@ -37788,6 +37877,7 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
+    method public boolean isStrongBoxBacked();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
   }
@@ -37805,6 +37895,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setDigests(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setEncryptionPaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setInvalidatedByBiometricEnrollment(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setIsStrongBoxBacked(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeySize(int);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityEnd(java.util.Date);
     method public android.security.keystore.KeyGenParameterSpec.Builder setKeyValidityForConsumptionEnd(java.util.Date);
@@ -37865,6 +37956,7 @@
     field public static final java.lang.String ENCRYPTION_PADDING_PKCS7 = "PKCS7Padding";
     field public static final java.lang.String ENCRYPTION_PADDING_RSA_OAEP = "OAEPPadding";
     field public static final java.lang.String ENCRYPTION_PADDING_RSA_PKCS1 = "PKCS1Padding";
+    field public static final deprecated java.lang.String KEY_ALGORITHM_3DES = "DESede";
     field public static final java.lang.String KEY_ALGORITHM_AES = "AES";
     field public static final java.lang.String KEY_ALGORITHM_EC = "EC";
     field public static final java.lang.String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
@@ -37875,11 +37967,13 @@
     field public static final java.lang.String KEY_ALGORITHM_RSA = "RSA";
     field public static final int ORIGIN_GENERATED = 1; // 0x1
     field public static final int ORIGIN_IMPORTED = 2; // 0x2
+    field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
     field public static final int ORIGIN_UNKNOWN = 4; // 0x4
     field public static final int PURPOSE_DECRYPT = 2; // 0x2
     field public static final int PURPOSE_ENCRYPT = 1; // 0x1
     field public static final int PURPOSE_SIGN = 4; // 0x4
     field public static final int PURPOSE_VERIFY = 8; // 0x8
+    field public static final int PURPOSE_WRAP_KEY = 32; // 0x20
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PKCS1 = "PKCS1";
     field public static final java.lang.String SIGNATURE_PADDING_RSA_PSS = "PSS";
   }
@@ -37919,12 +38013,24 @@
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
   }
 
+  public class StrongBoxUnavailableException extends java.security.ProviderException {
+    ctor public StrongBoxUnavailableException();
+  }
+
   public class UserNotAuthenticatedException extends java.security.InvalidKeyException {
     ctor public UserNotAuthenticatedException();
     ctor public UserNotAuthenticatedException(java.lang.String);
     ctor public UserNotAuthenticatedException(java.lang.String, java.lang.Throwable);
   }
 
+  public class WrappedKeyEntry implements java.security.KeyStore.Entry {
+    ctor public WrappedKeyEntry(byte[], java.lang.String, java.lang.String, java.security.spec.AlgorithmParameterSpec);
+    method public java.security.spec.AlgorithmParameterSpec getAlgorithmParameterSpec();
+    method public java.lang.String getTransformation();
+    method public byte[] getWrappedKeyBytes();
+    method public java.lang.String getWrappingKeyAlias();
+  }
+
 }
 
 package android.service.autofill {
@@ -40816,6 +40922,7 @@
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string";
     field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string";
     field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string";
+    field public static final java.lang.String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING = "config_plans_package_override_string";
     field public static final java.lang.String KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL = "config_telephony_use_own_number_for_voicemail_bool";
     field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool";
     field public static final java.lang.String KEY_DATA_LIMIT_THRESHOLD_BYTES_LONG = "data_limit_threshold_bytes_long";
@@ -41509,10 +41616,16 @@
     method public static int getDefaultSmsSubscriptionId();
     method public static int getDefaultSubscriptionId();
     method public static int getDefaultVoiceSubscriptionId();
+    method public java.util.List<android.telephony.SubscriptionPlan> getSubscriptionPlans(int);
     method public boolean isNetworkRoaming(int);
     method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
+    method public void setSubscriptionOverrideCongested(int, boolean, long);
+    method public void setSubscriptionOverrideUnmetered(int, boolean, long);
+    method public void setSubscriptionPlans(int, java.util.List<android.telephony.SubscriptionPlan>);
     field public static final java.lang.String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
     field public static final java.lang.String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
+    field public static final java.lang.String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS";
+    field public static final java.lang.String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS";
     field public static final int DATA_ROAMING_DISABLE = 0; // 0x0
     field public static final int DATA_ROAMING_ENABLE = 1; // 0x1
     field public static final java.lang.String EXTRA_SUBSCRIPTION_INDEX = "android.telephony.extra.SUBSCRIPTION_INDEX";
@@ -41524,6 +41637,38 @@
     method public void onSubscriptionsChanged();
   }
 
+  public final class SubscriptionPlan implements android.os.Parcelable {
+    method public java.util.Iterator<android.util.Pair<java.time.ZonedDateTime, java.time.ZonedDateTime>> cycleIterator();
+    method public int describeContents();
+    method public int getDataLimitBehavior();
+    method public long getDataLimitBytes();
+    method public long getDataUsageBytes();
+    method public long getDataUsageTime();
+    method public java.lang.CharSequence getSummary();
+    method public java.lang.CharSequence getTitle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final long BYTES_UNKNOWN = -1L; // 0xffffffffffffffffL
+    field public static final long BYTES_UNLIMITED = 9223372036854775807L; // 0x7fffffffffffffffL
+    field public static final android.os.Parcelable.Creator<android.telephony.SubscriptionPlan> CREATOR;
+    field public static final int LIMIT_BEHAVIOR_BILLED = 1; // 0x1
+    field public static final int LIMIT_BEHAVIOR_DISABLED = 0; // 0x0
+    field public static final int LIMIT_BEHAVIOR_THROTTLED = 2; // 0x2
+    field public static final int LIMIT_BEHAVIOR_UNKNOWN = -1; // 0xffffffff
+    field public static final long TIME_UNKNOWN = -1L; // 0xffffffffffffffffL
+  }
+
+  public static class SubscriptionPlan.Builder {
+    method public android.telephony.SubscriptionPlan build();
+    method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
+    method public static android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
+    method public static android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
+    method public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
+    method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
+    method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
+    method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
+    method public android.telephony.SubscriptionPlan.Builder setTitle(java.lang.CharSequence);
+  }
+
   public class TelephonyManager {
     method public boolean canChangeDtmfToneLength();
     method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
@@ -43170,9 +43315,13 @@
     ctor public BulletSpan();
     ctor public BulletSpan(int);
     ctor public BulletSpan(int, int);
+    ctor public BulletSpan(int, int, int);
     ctor public BulletSpan(android.os.Parcel);
     method public int describeContents();
     method public void drawLeadingMargin(android.graphics.Canvas, android.graphics.Paint, int, int, int, int, int, java.lang.CharSequence, int, int, boolean, android.text.Layout);
+    method public int getBulletRadius();
+    method public int getColor();
+    method public int getGapWidth();
     method public int getLeadingMargin(boolean);
     method public int getSpanTypeId();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45615,6 +45764,7 @@
     field public static final int KEYCODE_PROG_YELLOW = 185; // 0xb9
     field public static final int KEYCODE_Q = 45; // 0x2d
     field public static final int KEYCODE_R = 46; // 0x2e
+    field public static final int KEYCODE_REFRESH = 285; // 0x11d
     field public static final int KEYCODE_RIGHT_BRACKET = 72; // 0x48
     field public static final int KEYCODE_RO = 217; // 0xd9
     field public static final int KEYCODE_S = 47; // 0x2f
@@ -47783,6 +47933,7 @@
     method protected final int getLocalFeatures();
     method public android.media.session.MediaController getMediaController();
     method public abstract int getNavigationBarColor();
+    method public int getNavigationBarDividerColor();
     method public android.transition.Transition getReenterTransition();
     method public android.transition.Transition getReturnTransition();
     method public android.transition.Transition getSharedElementEnterTransition();
@@ -47851,6 +48002,7 @@
     method public void setLogo(int);
     method public void setMediaController(android.media.session.MediaController);
     method public abstract void setNavigationBarColor(int);
+    method public void setNavigationBarDividerColor(int);
     method public void setReenterTransition(android.transition.Transition);
     method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
     method public final void setRestrictedCaptionAreaListener(android.view.Window.OnRestrictedCaptionAreaChangedListener);
diff --git a/api/system-current.txt b/api/system-current.txt
index 6119d5f..0a66d4a 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -794,8 +794,6 @@
     field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
     field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
     field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
-    field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.intent.action.SIM_APPLICATION_STATE_CHANGED";
-    field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.intent.action.SIM_CARD_STATE_CHANGED";
     field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
     field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
     field public static final java.lang.String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
@@ -2335,11 +2333,15 @@
     method public deprecated boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public void flushGnssBatch();
     method public int getGnssBatchSize();
+    method public boolean isLocationEnabledForUser(android.os.UserHandle);
+    method public boolean isProviderEnabledForUser(java.lang.String, android.os.UserHandle);
     method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler);
     method public deprecated void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
     method public deprecated void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method public void requestLocationUpdates(android.location.LocationRequest, android.location.LocationListener, android.os.Looper);
     method public void requestLocationUpdates(android.location.LocationRequest, android.app.PendingIntent);
+    method public void setLocationEnabledForUser(boolean, android.os.UserHandle);
+    method public boolean setProviderEnabledForUser(java.lang.String, boolean, android.os.UserHandle);
     method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback);
   }
 
@@ -2869,6 +2871,7 @@
   }
 
   public final class IpSecManager {
+    method public void applyTunnelModeTransform(android.net.IpSecManager.IpSecTunnelInterface, int, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(java.net.InetAddress, java.net.InetAddress, android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
   }
 
@@ -3447,6 +3450,7 @@
     field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
     field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
     field public static final java.lang.String ACTION_UPDATE_LANG_ID = "android.intent.action.UPDATE_LANG_ID";
+    field public static final java.lang.String ACTION_UPDATE_NETWORK_WATCHLIST = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
     field public static final java.lang.String ACTION_UPDATE_PINS = "android.intent.action.UPDATE_PINS";
     field public static final java.lang.String ACTION_UPDATE_SMART_SELECTION = "android.intent.action.UPDATE_SMART_SELECTION";
     field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES";
@@ -4391,6 +4395,12 @@
 
 package android.telephony {
 
+  public static final class AccessNetworkConstants.TransportType {
+    ctor public AccessNetworkConstants.TransportType();
+    field public static final int WLAN = 2; // 0x2
+    field public static final int WWAN = 1; // 0x1
+  }
+
   public class CarrierConfigManager {
     method public static android.os.PersistableBundle getDefaultConfig();
     method public void updateConfigForPhoneId(int, java.lang.String);
@@ -4526,6 +4536,9 @@
     method public int[] supplyPukReportResult(java.lang.String, java.lang.String);
     method public void toggleRadioOnOff();
     method public void updateServiceLocation();
+    field public static final java.lang.String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
     field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
     field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
     field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -4603,18 +4616,21 @@
     method public abstract android.telephony.data.DataService.DataServiceProvider createDataServiceProvider(int);
     field public static final java.lang.String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
     field public static final java.lang.String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
+    field public static final int REQUEST_REASON_HANDOVER = 3; // 0x3
+    field public static final int REQUEST_REASON_NORMAL = 1; // 0x1
+    field public static final int REQUEST_REASON_SHUTDOWN = 2; // 0x2
   }
 
   public class DataService.DataServiceProvider {
     ctor public DataService.DataServiceProvider(int);
-    method public void deactivateDataCall(int, boolean, boolean, android.telephony.data.DataServiceCallback);
+    method public void deactivateDataCall(int, int, android.telephony.data.DataServiceCallback);
     method public void getDataCallList(android.telephony.data.DataServiceCallback);
     method public final int getSlotId();
     method public final void notifyDataCallListChanged(java.util.List<android.telephony.data.DataCallResponse>);
     method protected void onDestroy();
     method public void setDataProfile(java.util.List<android.telephony.data.DataProfile>, boolean, android.telephony.data.DataServiceCallback);
     method public void setInitialAttachApn(android.telephony.data.DataProfile, boolean, android.telephony.data.DataServiceCallback);
-    method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, boolean, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
+    method public void setupDataCall(int, android.telephony.data.DataProfile, boolean, boolean, int, android.net.LinkProperties, android.telephony.data.DataServiceCallback);
   }
 
   public class DataServiceCallback {
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index 368a70b..2b00d9e 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -91,9 +91,7 @@
 
 gen_src_dir:=
 
-ifeq ($(BUILD_WITH_INCIDENTD_RC), true)
 LOCAL_INIT_RC := incidentd.rc
-endif
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 01f4a84..a7daa3f 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -136,7 +136,7 @@
 
 LOCAL_MODULE_CLASS := EXECUTABLES
 
-#LOCAL_INIT_RC := statsd.rc
+LOCAL_INIT_RC := statsd.rc
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ca097d0..ba628b8 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -725,7 +725,7 @@
     if (checkCallingPermission(String16(kPermissionDump))) {
         ConfigKey configKey(ipc->getCallingUid(), key);
         StatsdConfig cfg;
-        if (!cfg.ParseFromArray(&config[0], config.size())) {
+        if (config.empty() || !cfg.ParseFromArray(&config[0], config.size())) {
             *success = false;
             return Status::ok();
         }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index a07bd2f..7f0ebb4 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -85,6 +85,8 @@
         AppStartCancelChanged app_start_cancel_changed = 49;
         AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
         LmkEventOccurred lmk_event_occurred = 51;
+        PictureInPictureStateChanged picture_in_picture_state_changed = 52;
+        WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -107,6 +109,8 @@
         CpuIdleTime cpu_idle_time = 10015;
         CpuActiveTime cpu_active_time = 10016;
         CpuClusterTime cpu_cluster_time = 10017;
+        DiskSpace disk_space = 10018;
+        SystemUptime system_uptime = 10019;
     }
 }
 
@@ -704,6 +708,22 @@
 }
 
 /**
+ * Logs wifi multicast locks held by an app
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message WifiMulticastLockStateChanged {
+    repeated AttributionNode attribution_node = 1;
+
+    enum State {
+        OFF = 0;
+        ON = 1;
+    }
+    optional State state = 2;
+}
+
+/**
  * Logs phone signal strength changes.
  *
  * Logged from:
@@ -938,6 +958,31 @@
 }
 
 /**
+ * Logs a picture-in-picture action
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+ *      frameworks/base/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+ */
+message PictureInPictureStateChanged {
+    optional int32 uid = 1;
+
+    optional string package_name = 2;
+
+    optional string class_name = 3;
+
+    // Picture-in-Picture action occurred, similar to
+    // frameworks/base/proto/src/metrics_constants.proto
+    enum State {
+        ENTERED = 1;
+        EXPANDED_TO_FULL_SCREEN = 2;
+        MINIMIZED = 3;
+        DISMISSED = 4;
+    }
+    optional State state = 4;
+}
+
+/**
  * Pulls bytes transferred via wifi (Sum of foreground and background usage).
  *
  * Pulled from:
@@ -1271,4 +1316,27 @@
     optional uint64 uid = 1;
     optional uint64 idx = 2;
     optional uint64 time_ms = 3;
-}
\ No newline at end of file
+}
+
+/*
+ * Pulls free disk space, for data, system partition and temporary directory.
+ */
+message DiskSpace {
+    // available bytes in data partition
+    optional uint64 data_available_bytes = 1;
+    // available bytes in system partition
+    optional uint64 system_available_bytes = 2;
+    // available bytes in download cache or temp directories
+    optional uint64 temp_available_bytes = 3;
+}
+
+/*
+ * Pulls system up time.
+ */
+message SystemUptime {
+    // Milliseconds since the system was booted.
+    // This clock stops when the system enters deep sleep (CPU off, display dark, device waiting
+    // for external input).
+    // It is not affected by clock scaling, idle, or other power saving mechanisms.
+    optional uint64 uptime_ms = 1;
+}
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 79f1a5d..e06ae48 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -66,6 +66,15 @@
     mPullers.insert({android::util::CPU_TIME_PER_UID_FREQ, make_shared<CpuTimePerUidFreqPuller>()});
     mPullers.insert({android::util::CPU_SUSPEND_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_SUSPEND_TIME)});
     mPullers.insert({android::util::CPU_IDLE_TIME, make_shared<StatsCompanionServicePuller>(android::util::CPU_IDLE_TIME)});
+    mPullers.insert({android::util::DISK_SPACE,
+                     make_shared<StatsCompanionServicePuller>(android::util::DISK_SPACE)});
+    mPullers.insert({android::util::SYSTEM_UPTIME,
+                     make_shared<StatsCompanionServicePuller>(android::util::SYSTEM_UPTIME)});
+    mPullers.insert(
+            {android::util::WIFI_ACTIVITY_ENERGY_INFO,
+             make_shared<StatsCompanionServicePuller>(android::util::WIFI_ACTIVITY_ENERGY_INFO)});
+    mPullers.insert({android::util::MODEM_ACTIVITY_INFO,
+                     make_shared<StatsCompanionServicePuller>(android::util::MODEM_ACTIVITY_INFO)});
 
     mStatsCompanionService = StatsService::getStatsCompanionService();
 }
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
index 7636268..5d43ef3 100644
--- a/cmds/statsd/src/logd/LogReader.cpp
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -98,7 +98,10 @@
 
             // Read a message
             err = android_logger_list_read(loggers, &msg);
-            if (err < 0) {
+            // err = 0 - no content, unexpected connection drop or EOF.
+            // err = +ive number - size of retrieved data from logger
+            // err = -ive number, OS supplied error _except_ for -EAGAIN
+            if (err <= 0) {
                 fprintf(stderr, "logcat read failure: %s\n", strerror(err));
                 break;
             }
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index bb2193f..f73c4a5 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -188,78 +188,78 @@
 }
 
 message StatsdStatsReport {
-    optional int32 stats_begin_time_sec = 1;
+  optional int32 stats_begin_time_sec = 1;
 
-    optional int32 stats_end_time_sec = 2;
+  optional int32 stats_end_time_sec = 2;
 
-    message MatcherStats {
-        optional int64 id = 1;
-        optional int32 matched_times = 2;
-    }
+  message MatcherStats {
+    optional int64 id = 1;
+    optional int32 matched_times = 2;
+  }
 
-    message ConditionStats {
-        optional int64 id = 1;
-        optional int32 max_tuple_counts = 2;
-    }
+  message ConditionStats {
+    optional int64 id = 1;
+    optional int32 max_tuple_counts = 2;
+  }
 
-    message MetricStats {
-        optional int64 id = 1;
-        optional int32 max_tuple_counts = 2;
-    }
+  message MetricStats {
+    optional int64 id = 1;
+    optional int32 max_tuple_counts = 2;
+  }
 
-    message AlertStats {
-        optional int64 id = 1;
-        optional int32 alerted_times = 2;
-    }
+  message AlertStats {
+    optional int64 id = 1;
+    optional int32 alerted_times = 2;
+  }
 
-    message ConfigStats {
-        optional int32 uid = 1;
-        optional int64 id = 2;
-        optional int32 creation_time_sec = 3;
-        optional int32 deletion_time_sec = 4;
-        optional int32 metric_count = 5;
-        optional int32 condition_count = 6;
-        optional int32 matcher_count = 7;
-        optional int32 alert_count = 8;
-        optional bool is_valid = 9;
+  message ConfigStats {
+    optional int32 uid = 1;
+    optional int64 id = 2;
+    optional int32 creation_time_sec = 3;
+    optional int32 deletion_time_sec = 4;
+    optional int32 metric_count = 5;
+    optional int32 condition_count = 6;
+    optional int32 matcher_count = 7;
+    optional int32 alert_count = 8;
+    optional bool is_valid = 9;
 
-        repeated int32 broadcast_sent_time_sec = 10;
-        repeated int32 data_drop_time_sec = 11;
-        repeated int32 dump_report_time_sec = 12;
-        repeated MatcherStats matcher_stats = 13;
-        repeated ConditionStats condition_stats = 14;
-        repeated MetricStats metric_stats = 15;
-        repeated AlertStats alert_stats = 16;
-    }
+    repeated int32 broadcast_sent_time_sec = 10;
+    repeated int32 data_drop_time_sec = 11;
+    repeated int32 dump_report_time_sec = 12;
+    repeated MatcherStats matcher_stats = 13;
+    repeated ConditionStats condition_stats = 14;
+    repeated MetricStats metric_stats = 15;
+    repeated AlertStats alert_stats = 16;
+  }
 
-    repeated ConfigStats config_stats = 3;
+  repeated ConfigStats config_stats = 3;
 
-    message AtomStats {
-        optional int32 tag = 1;
-        optional int32 count = 2;
-    }
+  message AtomStats {
+    optional int32 tag = 1;
+    optional int32 count = 2;
+  }
 
-    repeated AtomStats atom_stats = 7;
+  repeated AtomStats atom_stats = 7;
 
-    message UidMapStats {
-        optional int32 snapshots = 1;
-        optional int32 changes = 2;
-        optional int32 bytes_used = 3;
-        optional int32 dropped_snapshots = 4;
-        optional int32 dropped_changes = 5;
-    }
-    optional UidMapStats uidmap_stats = 8;
+  message UidMapStats {
+    optional int32 snapshots = 1;
+    optional int32 changes = 2;
+    optional int32 bytes_used = 3;
+    optional int32 dropped_snapshots = 4;
+    optional int32 dropped_changes = 5;
+  }
+  optional UidMapStats uidmap_stats = 8;
 
-    message AnomalyAlarmStats {
-        optional int32 alarms_registered = 1;
-    }
-    optional AnomalyAlarmStats anomaly_alarm_stats = 9;
+  message AnomalyAlarmStats {
+    optional int32 alarms_registered = 1;
+  }
+  optional AnomalyAlarmStats anomaly_alarm_stats = 9;
 
-    message PulledAtomStats {
-        optional int32 atom_id = 1;
-        optional int64 total_pull = 2;
-        optional int64 total_pull_from_cache = 3;
-        optional int64 min_pull_interval_sec = 4;
-    }
-    repeated PulledAtomStats pulled_atom_stats = 10;
+  message PulledAtomStats {
+    optional int32 atom_id = 1;
+    optional int64 total_pull = 2;
+    optional int64 total_pull_from_cache = 3;
+    optional int64 min_pull_interval_sec = 4;
+  }
+  repeated PulledAtomStats pulled_atom_stats = 10;
 }
\ No newline at end of file
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index cd60ee7..07bbcb2 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -25,300 +25,303 @@
 import "frameworks/base/cmds/statsd/src/perfetto/perfetto_config.proto";
 
 enum Position {
-    POSITION_UNKNOWN = 0;
-    FIRST = 1;
-    LAST = 2;
-    ANY = 3;
+  POSITION_UNKNOWN = 0;
+
+  FIRST = 1;
+
+  LAST = 2;
+
+  ANY = 3;
 }
 
 enum TimeUnit {
-    TIME_UNIT_UNSPECIFIED = 0;
-    ONE_MINUTE = 1;
-    FIVE_MINUTES = 2;
-    TEN_MINUTES = 3;
-    THIRTY_MINUTES = 4;
-    ONE_HOUR = 5;
-    THREE_HOURS = 6;
-    SIX_HOURS = 7;
-    TWELVE_HOURS = 8;
-    ONE_DAY = 9;
-    CTS = 1000;
+  TIME_UNIT_UNSPECIFIED = 0;
+  ONE_MINUTE = 1;
+  FIVE_MINUTES = 2;
+  TEN_MINUTES = 3;
+  THIRTY_MINUTES = 4;
+  ONE_HOUR = 5;
+  THREE_HOURS = 6;
+  SIX_HOURS = 7;
+  TWELVE_HOURS = 8;
+  ONE_DAY = 9;
+  CTS = 1000;
 }
 
 message FieldMatcher {
-    optional int32 field = 1;
+  optional int32 field = 1;
 
-    optional Position position = 2;
+  optional Position position = 2;
 
-    repeated FieldMatcher child = 3;
+  repeated FieldMatcher child = 3;
 }
 
 message FieldValueMatcher {
-    // Field id, as specified in the atom proto message.
-    optional int32 field = 1;
+  optional int32 field = 1;
 
-    // For repeated fields, specifies the position in the array.
-    // FIRST and LAST mean that if the values are found at the first
-    // or last position, it's a match. ANY means that if the values are found
-    // anywhere in the array, then it's a match.
-    optional Position position = 2;
+  optional Position position = 2;
 
-    oneof value_matcher {
-        bool eq_bool = 3;
-        string eq_string = 4;
-        int32 eq_int = 5;
+  oneof value_matcher {
+    bool eq_bool = 3;
+    string eq_string = 4;
+    int32 eq_int = 5;
 
-        int64 lt_int = 6;
-        int64 gt_int = 7;
-        float lt_float = 8;
-        float gt_float = 9;
+    int64 lt_int = 6;
+    int64 gt_int = 7;
+    float lt_float = 8;
+    float gt_float = 9;
 
-        int64 lte_int = 10;
-        int64 gte_int = 11;
+    int64 lte_int = 10;
+    int64 gte_int = 11;
 
-        MessageMatcher matches_tuple = 12;
-    }
+    MessageMatcher matches_tuple = 12;
+  }
 }
 
 message MessageMatcher {
-    repeated FieldValueMatcher field_value_matcher = 1;
+  repeated FieldValueMatcher field_value_matcher = 1;
 }
 
 enum LogicalOperation {
-    LOGICAL_OPERATION_UNSPECIFIED = 0;
-    AND = 1;
-    OR = 2;
-    NOT = 3;
-    NAND = 4;
-    NOR = 5;
+  LOGICAL_OPERATION_UNSPECIFIED = 0;
+  AND = 1;
+  OR = 2;
+  NOT = 3;
+  NAND = 4;
+  NOR = 5;
 }
 
 message SimpleAtomMatcher {
-    optional int32 atom_id = 1;
+  optional int32 atom_id = 1;
 
-    repeated FieldValueMatcher field_value_matcher = 2;
+  repeated FieldValueMatcher field_value_matcher = 2;
 }
 
 message AtomMatcher {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    message Combination {
-        optional LogicalOperation operation = 1;
+  message Combination {
+    optional LogicalOperation operation = 1;
 
-        repeated int64 matcher = 2;
-    }
-    oneof contents {
-        SimpleAtomMatcher simple_atom_matcher = 2;
-        Combination combination = 3;
-    }
+    repeated int64 matcher = 2;
+  }
+  oneof contents {
+    SimpleAtomMatcher simple_atom_matcher = 2;
+    Combination combination = 3;
+  }
 }
 
 message SimplePredicate {
-    optional int64 start = 1;
+  optional int64 start = 1;
 
-    optional int64 stop = 2;
+  optional int64 stop = 2;
 
-    optional bool count_nesting = 3 [default = true];
+  optional bool count_nesting = 3 [default = true];
 
-    optional int64 stop_all = 4;
+  optional int64 stop_all = 4;
 
-    enum InitialValue {
-        UNKNOWN = 0;
-        FALSE = 1;
-    }
-    optional InitialValue initial_value = 5 [default = FALSE];
+  enum InitialValue {
+    UNKNOWN = 0;
+    FALSE = 1;
+  }
+  optional InitialValue initial_value = 5 [default = FALSE];
 
-    optional FieldMatcher dimensions = 6;
+  optional FieldMatcher dimensions = 6;
 }
 
 message Predicate {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    message Combination {
-        optional LogicalOperation operation = 1;
+  message Combination {
+    optional LogicalOperation operation = 1;
 
-        repeated int64 predicate = 2;
-    }
+    repeated int64 predicate = 2;
+  }
 
-    oneof contents {
-        SimplePredicate simple_predicate = 2;
-        Combination combination = 3;
-    }
-}
-
-message Bucket {
-    optional int64 bucket_size_millis = 1;
+  oneof contents {
+    SimplePredicate simple_predicate = 2;
+    Combination combination = 3;
+  }
 }
 
 message MetricConditionLink {
-    optional int64 condition = 1;
+  optional int64 condition = 1;
 
-    optional FieldMatcher fields_in_what = 2;
+  optional FieldMatcher fields_in_what = 2;
 
-    optional FieldMatcher fields_in_condition = 3;
+  optional FieldMatcher fields_in_condition = 3;
 }
 
 message FieldFilter {
-    optional bool include_all = 1 [default = false];
-    optional FieldMatcher fields = 2;
+  optional bool include_all = 1 [default = false];
+  optional FieldMatcher fields = 2;
 }
 
 message EventMetric {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 what = 2;
+  optional int64 what = 2;
 
-    optional int64 condition = 3;
+  optional int64 condition = 3;
 
-    repeated MetricConditionLink links = 4;
+  repeated MetricConditionLink links = 4;
 }
 
 message CountMetric {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 what = 2;
+  optional int64 what = 2;
 
-    optional int64 condition = 3;
+  optional int64 condition = 3;
 
-    optional FieldMatcher dimensions_in_what = 4;
+  optional FieldMatcher dimensions_in_what = 4;
 
-    optional FieldMatcher dimensions_in_condition = 7;
+  optional FieldMatcher dimensions_in_condition = 7;
 
-    optional TimeUnit bucket = 5;
+  optional TimeUnit bucket = 5;
 
-    repeated MetricConditionLink links = 6;
+  repeated MetricConditionLink links = 6;
 }
 
 message DurationMetric {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 what = 2;
+  optional int64 what = 2;
 
-    optional int64 condition = 3;
+  optional int64 condition = 3;
 
-    repeated MetricConditionLink links = 4;
+  repeated MetricConditionLink links = 4;
 
-    enum AggregationType {
-        SUM = 1;
+  enum AggregationType {
+    SUM = 1;
 
-        MAX_SPARSE = 2;
-    }
-    optional AggregationType aggregation_type = 5 [default = SUM];
+    MAX_SPARSE = 2;
+  }
+  optional AggregationType aggregation_type = 5 [default = SUM];
 
-    optional FieldMatcher dimensions_in_what = 6;
+  optional FieldMatcher dimensions_in_what = 6;
 
-    optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8;
 
-    optional TimeUnit bucket = 7;
+  optional TimeUnit bucket = 7;
 }
 
 message GaugeMetric {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 what = 2;
+  optional int64 what = 2;
 
-    optional FieldFilter gauge_fields_filter = 3;
+  optional FieldFilter gauge_fields_filter = 3;
 
-    optional int64 condition = 4;
+  optional int64 condition = 4;
 
-    optional FieldMatcher dimensions_in_what = 5;
+  optional FieldMatcher dimensions_in_what = 5;
 
-    optional FieldMatcher dimensions_in_condition = 8;
+  optional FieldMatcher dimensions_in_condition = 8;
 
-    optional TimeUnit bucket = 6;
+  optional TimeUnit bucket = 6;
 
-    repeated MetricConditionLink links = 7;
+  repeated MetricConditionLink links = 7;
 }
 
 message ValueMetric {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 what = 2;
+  optional int64 what = 2;
 
-    optional FieldMatcher value_field = 3;
+  optional FieldMatcher value_field = 3;
 
-    optional int64 condition = 4;
+  optional int64 condition = 4;
 
-    optional FieldMatcher dimensions_in_what = 5;
+  optional FieldMatcher dimensions_in_what = 5;
 
-    optional FieldMatcher dimensions_in_condition = 9;
+  optional FieldMatcher dimensions_in_condition = 9;
 
-    optional TimeUnit bucket = 6;
+  optional TimeUnit bucket = 6;
 
-    repeated MetricConditionLink links = 7;
+  repeated MetricConditionLink links = 7;
 
-    enum AggregationType { SUM = 1; }
-    optional AggregationType aggregation_type = 8 [default = SUM];
+  enum AggregationType {
+    SUM = 1;
+  }
+  optional AggregationType aggregation_type = 8 [default = SUM];
 }
 
 message Alert {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    optional int64 metric_id = 2;
+  optional int64 metric_id = 2;
 
-    optional int32 num_buckets = 3;
+  optional int32 num_buckets = 3;
 
-    optional int32 refractory_period_secs = 4;
+  optional int32 refractory_period_secs = 4;
 
-    optional double trigger_if_sum_gt = 5;
+  optional double trigger_if_sum_gt = 5;
 }
 
 message Alarm {
-    optional int64 id = 1;
-    optional int64 offset_millis = 2;
-    optional int64 period_millis = 3;
+  optional int64 id = 1;
+
+  optional int64 offset_millis = 2;
+
+  optional int64 period_millis = 3;
 }
 
 message IncidentdDetails {
-    repeated int32 section = 1;
+  repeated int32 section = 1;
 }
 
 message PerfettoDetails {
-    optional perfetto.protos.TraceConfig trace_config = 1;
+  optional perfetto.protos.TraceConfig trace_config = 1;
+}
+
+message BroadcastSubscriberDetails {
+  optional int64 subscriber_id = 1;
 }
 
 message Subscription {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    enum RuleType {
-        RULE_TYPE_UNSPECIFIED = 0;
-        ALARM = 1;
-        ALERT = 2;
-    }
-    optional RuleType rule_type = 2;
+  enum RuleType {
+    RULE_TYPE_UNSPECIFIED = 0;
+    ALARM = 1;
+    ALERT = 2;
+  }
+  optional RuleType rule_type = 2;
 
-    optional int64 rule_id = 3;
+  optional int64 rule_id = 3;
 
-    oneof subscriber_information {
-        IncidentdDetails incidentd_details = 4;
-        PerfettoDetails perfetto_details = 5;
-    }
+  oneof subscriber_information {
+    IncidentdDetails incidentd_details = 4;
+    PerfettoDetails perfetto_details = 5;
+    BroadcastSubscriberDetails broadcast_subscriber_details = 6;
+  }
 }
 
 message StatsdConfig {
-    optional int64 id = 1;
+  optional int64 id = 1;
 
-    repeated EventMetric event_metric = 2;
+  repeated EventMetric event_metric = 2;
 
-    repeated CountMetric count_metric = 3;
+  repeated CountMetric count_metric = 3;
 
-    repeated ValueMetric value_metric = 4;
+  repeated ValueMetric value_metric = 4;
 
-    repeated GaugeMetric gauge_metric = 5;
+  repeated GaugeMetric gauge_metric = 5;
 
-    repeated DurationMetric duration_metric = 6;
+  repeated DurationMetric duration_metric = 6;
 
-    repeated AtomMatcher atom_matcher = 7;
+  repeated AtomMatcher atom_matcher = 7;
 
-    repeated Predicate predicate = 8;
+  repeated Predicate predicate = 8;
 
-    repeated Alert alert = 9;
+  repeated Alert alert = 9;
 
-    repeated Alarm alarm = 10;
+  repeated Alarm alarm = 10;
 
-    repeated Subscription subscription = 11;
+  repeated Subscription subscription = 11;
 
-    repeated string allowed_log_source = 12;
+  repeated string allowed_log_source = 12;
 
-    repeated int64 no_report_metric = 13;
+  repeated int64 no_report_metric = 13;
 }
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index 843b1e5..2e0161b 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -19,7 +19,6 @@
 import android.content.res.Resources;
 import android.util.Log;
 
-import com.android.internal.os.StatsdConfigProto.Bucket;
 import com.android.internal.os.StatsdConfigProto.Predicate;
 import com.android.internal.os.StatsdConfigProto.CountMetric;
 import com.android.internal.os.StatsdConfigProto.DurationMetric;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 4048e65..cc68c05 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -222,9 +222,18 @@
 
     @Override
     public Intent getLeanbackLaunchIntentForPackage(String packageName) {
-        // Try to find a main leanback_launcher activity.
+        return getLaunchIntentForPackageAndCategory(packageName, Intent.CATEGORY_LEANBACK_LAUNCHER);
+    }
+
+    @Override
+    public Intent getCarLaunchIntentForPackage(String packageName) {
+        return getLaunchIntentForPackageAndCategory(packageName, Intent.CATEGORY_CAR_LAUNCHER);
+    }
+
+    private Intent getLaunchIntentForPackageAndCategory(String packageName, String category) {
+        // Try to find a main launcher activity for the given categories.
         Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
-        intentToResolve.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER);
+        intentToResolve.addCategory(category);
         intentToResolve.setPackage(packageName);
         List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
 
@@ -690,6 +699,26 @@
     }
 
     @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        try {
+            return mPM.hasSigningCertificate(packageName, certificate, type);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        try {
+            return mPM.hasUidSigningCertificate(uid, certificate, type);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public String[] getPackagesForUid(int uid) {
         try {
             return mPM.getPackagesForUid(uid);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index d1aacad..d378f22 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -75,6 +75,7 @@
     NotificationChannelGroup getNotificationChannelGroup(String pkg, String channelGroupId);
     ParceledListSlice getNotificationChannelGroups(String pkg);
     boolean onlyHasDefaultChannel(String pkg, int uid);
+    ParceledListSlice getRecentNotifyingAppsForUser(int userId);
 
     // TODO: Remove this when callers have been migrated to the equivalent
     // INotificationListener method.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 75dc571..2e3b8af 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6469,8 +6469,11 @@
                     ? super.mBigContentTitle
                     : mConversationTitle;
             boolean isOneToOne = TextUtils.isEmpty(conversationTitle);
+            CharSequence nameReplacement = null;
             if (hasOnlyWhiteSpaceSenders()) {
                 isOneToOne = true;
+                nameReplacement = conversationTitle;
+                conversationTitle = null;
             }
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
@@ -6489,6 +6492,8 @@
                     mBuilder.resolveContrastColor());
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
+            contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
+                    nameReplacement);
             contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsOneToOne",
                     isOneToOne);
             contentView.setBundle(R.id.status_bar_latest_event_content, "setData",
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 8b76cc7..d6429ae 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -867,19 +867,30 @@
             @Nullable OnFinished onFinished, @Nullable Handler handler,
             @Nullable String requiredPermission, @Nullable Bundle options)
             throws CanceledException {
+        if (sendAndReturnResult(context, code, intent, onFinished, handler, requiredPermission,
+                options) < 0) {
+            throw new CanceledException();
+        }
+    }
+
+    /**
+     * Like {@link #send}, but returns the result
+     * @hide
+     */
+    public int sendAndReturnResult(Context context, int code, @Nullable Intent intent,
+            @Nullable OnFinished onFinished, @Nullable Handler handler,
+            @Nullable String requiredPermission, @Nullable Bundle options)
+            throws CanceledException {
         try {
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
-            int res = ActivityManager.getService().sendIntentSender(
+            return ActivityManager.getService().sendIntentSender(
                     mTarget, mWhitelistToken, code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
                             : null,
                     requiredPermission, options);
-            if (res < 0) {
-                throw new CanceledException();
-            }
         } catch (RemoteException e) {
             throw new CanceledException(e);
         }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7fccda8..bad19e3 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -8228,6 +8228,47 @@
     }
 
     /**
+     * Called by a device or profile owner to restrict packages from accessing metered data.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @param packageNames the list of package names to be restricted.
+     * @return a list of package names which could not be restricted.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     */
+    public @NonNull List<String> setMeteredDataDisabled(@NonNull ComponentName admin,
+            @NonNull List<String> packageNames) {
+        throwIfParentInstance("setMeteredDataDisabled");
+        if (mService != null) {
+            try {
+                return mService.setMeteredDataDisabled(admin, packageNames);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return packageNames;
+    }
+
+    /**
+     * Called by a device or profile owner to retrieve the list of packages which are restricted
+     * by the admin from accessing metered data.
+     *
+     * @param admin which {@link DeviceAdminReceiver} this request is associated with.
+     * @return the list of restricted package names.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
+     */
+    public @NonNull List<String> getMeteredDataDisabled(@NonNull ComponentName admin) {
+        throwIfParentInstance("getMeteredDataDisabled");
+        if (mService != null) {
+            try {
+                return mService.getMeteredDataDisabled(admin);
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Called by device owners to retrieve device logs from before the device's last reboot.
      * <p>
      * <strong> This API is not supported on all devices. Calling this API on unsupported devices
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d2a2be7..514dca9 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -400,4 +400,7 @@
     void setPrintingEnabled(in ComponentName admin, boolean enabled);
     boolean isPrintingEnabled();
     CharSequence getPrintingDisabledReason();
+
+    List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames);
+    List<String> getMeteredDataDisabled(in ComponentName admin);
 }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 5bd3440..5808f8b 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -156,10 +156,11 @@
      */
     public static final String HINT_SEE_MORE = "see_more";
     /**
-     * A hint to tell the system that this slice cares about the return value of
-     * {@link SliceProvider#getBindingPackage} and should not cache the result
-     * for multiple apps.
-     * @hide
+     * A hint used when implementing app-specific slice permissions.
+     * Tells the system that for this slice the return value of
+     * {@link SliceProvider#onBindSlice(Uri, List)} may be different depending on
+     * {@link SliceProvider#getBindingPackage} and should not be cached for multiple
+     * apps.
      */
     public static final String HINT_CALLER_NEEDED = "caller_needed";
     /**
@@ -429,28 +430,6 @@
          * Add a color to the slice being constructed
          * @param subType Optional template-specific type information
          * @see {@link SliceItem#getSubType()}
-         * @deprecated will be removed once supportlib updates
-         */
-        public Builder addColor(int color, @Nullable String subType, @SliceHint String... hints) {
-            mItems.add(new SliceItem(color, SliceItem.FORMAT_INT, subType, hints));
-            return this;
-        }
-
-        /**
-         * Add a color to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
-         * @deprecated will be removed once supportlib updates
-         */
-        public Builder addColor(int color, @Nullable String subType,
-                @SliceHint List<String> hints) {
-            return addColor(color, subType, hints.toArray(new String[hints.size()]));
-        }
-
-        /**
-         * Add a color to the slice being constructed
-         * @param subType Optional template-specific type information
-         * @see {@link SliceItem#getSubType()}
          */
         public Builder addInt(int value, @Nullable String subType, @SliceHint String... hints) {
             mItems.add(new SliceItem(value, SliceItem.FORMAT_INT, subType, hints));
diff --git a/core/java/android/app/slice/SliceItem.java b/core/java/android/app/slice/SliceItem.java
index bcfd413..9eb2bb8 100644
--- a/core/java/android/app/slice/SliceItem.java
+++ b/core/java/android/app/slice/SliceItem.java
@@ -98,11 +98,6 @@
      */
     public static final String FORMAT_INT = "int";
     /**
-     * A {@link SliceItem} that contains an int.
-     * @deprecated to be removed
-     */
-    public static final String FORMAT_COLOR = "color";
-    /**
      * A {@link SliceItem} that contains a timestamp.
      */
     public static final String FORMAT_TIMESTAMP = "timestamp";
@@ -231,13 +226,6 @@
     }
 
     /**
-     * @deprecated to be removed.
-     */
-    public int getColor() {
-        return (Integer) mObj;
-    }
-
-    /**
      * @return The slice held by this {@link #FORMAT_ACTION} or {@link #FORMAT_SLICE} SliceItem
      */
     public Slice getSlice() {
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 09c420c..2fa9d8e 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -16,6 +16,7 @@
 
 package android.app.slice;
 
+import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
@@ -90,66 +91,56 @@
     }
 
     /**
-     * Adds a callback to a specific slice uri.
-     * <p>
-     * This is a convenience that performs a few slice actions at once. It will put
-     * the slice in a pinned state since there is a callback attached. It will also
-     * listen for content changes, when a content change observes, the android system
-     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
-     *
-     * @param uri The uri of the slice being listened to.
-     * @param callback The listener that should receive the callbacks.
-     * @param specs The list of supported {@link SliceSpec}s of the callback.
-     * @see SliceProvider#onSlicePinned(Uri)
+     * @deprecated TO BE REMOVED.
      */
+    @Deprecated
     public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
             @NonNull List<SliceSpec> specs) {
-        registerSliceCallback(uri, callback, specs, Handler.getMain());
+        registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
     }
 
     /**
-     * Adds a callback to a specific slice uri.
-     * <p>
-     * This is a convenience that performs a few slice actions at once. It will put
-     * the slice in a pinned state since there is a callback attached. It will also
-     * listen for content changes, when a content change observes, the android system
-     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
-     *
-     * @param uri The uri of the slice being listened to.
-     * @param callback The listener that should receive the callbacks.
-     * @param specs The list of supported {@link SliceSpec}s of the callback.
-     * @see SliceProvider#onSlicePinned(Uri)
+     * @deprecated TO BE REMOVED.
      */
-    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
-            @NonNull List<SliceSpec> specs, Handler handler) {
-        try {
-            mService.addSliceListener(uri, mContext.getPackageName(),
-                    getListener(uri, callback, new ISliceListener.Stub() {
-                        @Override
-                        public void onSliceUpdated(Slice s) throws RemoteException {
-                            handler.post(() -> callback.onSliceUpdated(s));
-                        }
-                    }), specs.toArray(new SliceSpec[specs.size()]));
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Adds a callback to a specific slice uri.
-     * <p>
-     * This is a convenience that performs a few slice actions at once. It will put
-     * the slice in a pinned state since there is a callback attached. It will also
-     * listen for content changes, when a content change observes, the android system
-     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
-     *
-     * @param uri The uri of the slice being listened to.
-     * @param callback The listener that should receive the callbacks.
-     * @param specs The list of supported {@link SliceSpec}s of the callback.
-     * @see SliceProvider#onSlicePinned(Uri)
-     */
+    @Deprecated
     public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
             @NonNull List<SliceSpec> specs, Executor executor) {
+        registerSliceCallback(uri, specs, executor, callback);
+    }
+
+    /**
+     * Adds a callback to a specific slice uri.
+     * <p>
+     * This is a convenience that performs a few slice actions at once. It will put
+     * the slice in a pinned state since there is a callback attached. It will also
+     * listen for content changes, when a content change observes, the android system
+     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+     *
+     * @param uri The uri of the slice being listened to.
+     * @param callback The listener that should receive the callbacks.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
+     */
+    public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
+            @NonNull SliceCallback callback) {
+        registerSliceCallback(uri, specs, mContext.getMainExecutor(), callback);
+    }
+
+    /**
+     * Adds a callback to a specific slice uri.
+     * <p>
+     * This is a convenience that performs a few slice actions at once. It will put
+     * the slice in a pinned state since there is a callback attached. It will also
+     * listen for content changes, when a content change observes, the android system
+     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+     *
+     * @param uri The uri of the slice being listened to.
+     * @param callback The listener that should receive the callbacks.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
+     */
+    public void registerSliceCallback(@NonNull Uri uri, @NonNull List<SliceSpec> specs,
+            @NonNull @CallbackExecutor Executor executor, @NonNull SliceCallback callback) {
         try {
             mService.addSliceListener(uri, mContext.getPackageName(),
                     getListener(uri, callback, new ISliceListener.Stub() {
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index c4316a0..00e8cca 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -89,7 +89,7 @@
  */
 public abstract class SliceProvider extends ContentProvider {
     /**
-     * This is the Android platform's MIME type for a slice: URI
+     * This is the Android platform's MIME type for a URI
      * containing a slice implemented through {@link SliceProvider}.
      */
     public static final String SLICE_TYPE = "vnd.android.slice";
@@ -158,7 +158,6 @@
      * currently happening. The returned package will have been
      * verified to belong to the calling UID. Returns {@code null} if not
      * currently performing an {@link #onBindSlice(Uri, List)}.
-     * @hide
      */
     public final @Nullable String getBindingPackage() {
         return mBindingPkg;
diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java
index 16309fa..e86d348 100644
--- a/core/java/android/app/timezone/RulesState.java
+++ b/core/java/android/app/timezone/RulesState.java
@@ -126,9 +126,6 @@
                 mStagedOperationType == STAGED_OPERATION_INSTALL /* requireNotNull */,
                 "stagedDistroRulesVersion", stagedDistroRulesVersion);
 
-        if (operationInProgress && distroStatus != DISTRO_STATUS_UNKNOWN) {
-            throw new IllegalArgumentException("distroInstalled != DISTRO_STATUS_UNKNOWN");
-        }
         this.mDistroStatus = validateDistroStatus(distroStatus);
         this.mInstalledDistroRulesVersion = validateConditionalNull(
                 mDistroStatus == DISTRO_STATUS_INSTALLED/* requireNotNull */,
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index f04e907..edb992b 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -106,6 +106,12 @@
          */
         public static final int NOTIFICATION_SEEN = 10;
 
+        /**
+         * An event type denoting a change in App Standby Bucket.
+         * @hide
+         */
+        public static final int STANDBY_BUCKET_CHANGED = 11;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
@@ -170,6 +176,13 @@
          */
         public String[] mContentAnnotations;
 
+        /**
+         * The app standby bucket assigned.
+         * Only present for {@link #STANDBY_BUCKET_CHANGED} event types
+         * {@hide}
+         */
+        public int mBucket;
+
         /** @hide */
         @EventFlags
         public int mFlags;
@@ -189,6 +202,7 @@
             mContentType = orig.mContentType;
             mContentAnnotations = orig.mContentAnnotations;
             mFlags = orig.mFlags;
+            mBucket = orig.mBucket;
         }
 
         /**
@@ -399,6 +413,9 @@
                 p.writeString(event.mContentType);
                 p.writeStringArray(event.mContentAnnotations);
                 break;
+            case Event.STANDBY_BUCKET_CHANGED:
+                p.writeInt(event.mBucket);
+                break;
         }
     }
 
@@ -442,6 +459,9 @@
                 eventOut.mContentType = p.readString();
                 eventOut.mContentAnnotations = p.createStringArray();
                 break;
+            case Event.STANDBY_BUCKET_CHANGED:
+                eventOut.mBucket = p.readInt();
+                break;
         }
     }
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index f05a4d1..acbdf14 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3525,74 +3525,6 @@
     public static final String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
 
     /**
-     * Broadcast Action: The sim card state has changed.
-     * The intent will have the following extra values:</p>
-     * <dl>
-     *   <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt>
-     *   <dd>The sim card state. One of:
-     *     <dl>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_ABSENT}</dt>
-     *       <dd>SIM card not found</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_IO_ERROR}</dt>
-     *       <dd>SIM card IO error</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_CARD_RESTRICTED}</dt>
-     *       <dd>SIM card is restricted</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PRESENT}</dt>
-     *       <dd>SIM card is present</dd>
-     *     </dl>
-     *   </dd>
-     * </dl>
-     *
-     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
-     *
-     * <p class="note">The current state can also be queried using
-     * {@link android.telephony.TelephonyManager.getSimCardState()}
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SIM_CARD_STATE_CHANGED =
-            "android.intent.action.SIM_CARD_STATE_CHANGED";
-
-    /**
-     * Broadcast Action: The sim application state has changed.
-     * The intent will have the following extra values:</p>
-     * <dl>
-     *   <dt>{@link android.telephony.TelephonyManager.EXTRA_SIM_STATE}</dt>
-     *   <dd>The sim application state. One of:
-     *     <dl>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NOT_READY}</dt>
-     *       <dd>SIM card applications not ready</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PIN_REQUIRED}</dt>
-     *       <dd>SIM card PIN locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PUK_REQUIRED}</dt>
-     *       <dd>SIM card PUK locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_NETWORK_LOCKED}</dt>
-     *       <dd>SIM card network locked</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_PERM_DISABLED}</dt>
-     *       <dd>SIM card permanently disabled due to PUK failures</dd>
-     *       <dt>{@link android.telephony.TelephonyManager.SIM_STATE_LOADED}</dt>
-     *       <dd>SIM card data loaded</dd>
-     *     </dl>
-     *   </dd>
-     * </dl>
-     *
-     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
-     *
-     * <p class="note">The current state can also be queried using
-     * {@link android.telephony.TelephonyManager.getSimApplicationState()}
-     *
-     * <p class="note">This is a protected intent that can only be sent by the system.
-     * @hide
-     */
-    @SystemApi
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
-            "android.intent.action.SIM_APPLICATION_STATE_CHANGED";
-
-    /**
      * Broadcast Action: indicate that the phone service state has changed.
      * The intent will have the following extra values:</p>
      * <p>
@@ -3998,6 +3930,14 @@
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String CATEGORY_LEANBACK_LAUNCHER = "android.intent.category.LEANBACK_LAUNCHER";
     /**
+     * Indicates the preferred entry-point activity when an application is launched from a Car
+     * launcher. If not present, Car launcher can optionally use {@link #CATEGORY_LAUNCHER} as a
+     * fallback, or exclude the application entirely.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+    public static final String CATEGORY_CAR_LAUNCHER = "android.intent.category.CAR_LAUNCHER";
+    /**
      * Indicates a Leanback settings activity to be displayed in the Leanback launcher.
      * @hide
      */
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index cce6b84..379bff4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -656,4 +656,8 @@
     void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
 
     CharSequence getHarmfulAppWarning(String packageName, int userId);
+
+    boolean hasSigningCertificate(String packageName, in byte[] signingCertificate, int flags);
+
+    boolean hasUidSigningCertificate(int uid, in byte[] signingCertificate, int flags);
 }
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 5a91e94..13ec4fd 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -246,9 +246,44 @@
      * equivalent to being signed with certificates B and A. This means that
      * in case multiple signatures are reported you cannot assume the one at
      * the first position to be the same across updates.
+     *
+     * <strong>Deprecated</strong> This has been replaced by the
+     * {@link PackageInfo#signingCertificateHistory} field, which takes into
+     * account signing certificate rotation.  For backwards compatibility in
+     * the event of signing certificate rotation, this will return the oldest
+     * reported signing certificate, so that an application will appear to
+     * callers as though no rotation occurred.
+     *
+     * @deprecated use {@code signingCertificateHistory} instead
      */
+    @Deprecated
     public Signature[] signatures;
-    
+
+    /**
+     * Array of all signatures arrays read from the package file, potentially
+     * including past signing certificates no longer used after signing
+     * certificate rotation.  Though signing certificate rotation is only
+     * available for apps with a single signing certificate, this provides an
+     * array of arrays so that packages signed with multiple signing
+     * certificates can still return all signers.  This is only filled in if
+     * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
+     *
+     * A package must be singed with at least one certificate, which is at
+     * position zero in the array.  An application may be signed by multiple
+     * certificates, which would be in the array at position zero in an
+     * indeterminate order.  A package may also have a history of certificates
+     * due to signing certificate rotation.  In this case, the array will be
+     * populated by a series of single-entry arrays corresponding to a signing
+     * certificate of the package.
+     *
+     * <strong>Note:</strong> Signature ordering is not guaranteed to be
+     * stable which means that a package signed with certificates A and B is
+     * equivalent to being signed with certificates B and A. This means that
+     * in case multiple signatures are reported you cannot assume the one at
+     * the first position will be the same across updates.
+     */
+    public Signature[][] signingCertificateHistory;
+
     /**
      * Application specified preferred configuration
      * {@link android.R.styleable#AndroidManifestUsesConfiguration
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bcf80ee..67c9584 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -133,6 +133,7 @@
             GET_SERVICES,
             GET_SHARED_LIBRARY_FILES,
             GET_SIGNATURES,
+            GET_SIGNING_CERTIFICATES,
             GET_URI_PERMISSION_PATTERNS,
             MATCH_UNINSTALLED_PACKAGES,
             MATCH_DISABLED_COMPONENTS,
@@ -272,7 +273,10 @@
     /**
      * {@link PackageInfo} flag: return information about the
      * signatures included in the package.
+     *
+     * @deprecated use {@code GET_SIGNING_CERTIFICATES} instead
      */
+    @Deprecated
     public static final int GET_SIGNATURES          = 0x00000040;
 
     /**
@@ -488,6 +492,14 @@
     public static final int MATCH_STATIC_SHARED_LIBRARIES = 0x04000000;
 
     /**
+     * {@link PackageInfo} flag: return the signing certificates associated with
+     * this package.  Each entry is a signing certificate that the package
+     * has proven it is authorized to use, usually a past signing certificate from
+     * which it has rotated.
+     */
+    public static final int GET_SIGNING_CERTIFICATES = 0x08000000;
+
+    /**
      * Internal flag used to indicate that a system component has done their
      * homework and verified that they correctly handle packages and components
      * that come and go over time. In particular:
@@ -3044,6 +3056,21 @@
     public abstract @Nullable Intent getLeanbackLaunchIntentForPackage(@NonNull String packageName);
 
     /**
+     * Return a "good" intent to launch a front-door Car activity in a
+     * package, for use for example to implement an "open" button when browsing
+     * through packages. The current implementation will look for a main
+     * activity in the category {@link Intent#CATEGORY_CAR_LAUNCHER}, or
+     * return null if no main car activities are found.
+     *
+     * @param packageName The name of the package to inspect.
+     * @return Returns either a fully-qualified Intent that can be used to launch
+     *         the main Car activity in the package, or null if the package
+     *         does not contain such an activity.
+     * @hide
+     */
+    public abstract @Nullable Intent getCarLaunchIntentForPackage(@NonNull String packageName);
+
+    /**
      * Return an array of all of the POSIX secondary group IDs that have been
      * assigned to the given package.
      * <p>
@@ -3766,7 +3793,7 @@
     public abstract int getInstantAppCookieMaxBytes();
 
     /**
-     * @deprecated
+     * deprecated
      * @hide
      */
     public abstract int getInstantAppCookieMaxSize();
@@ -5899,4 +5926,60 @@
     public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
         throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
     }
+
+    /** @hide */
+    @IntDef(prefix = { "CERT_INPUT_" }, value = {
+            CERT_INPUT_RAW_X509,
+            CERT_INPUT_SHA256
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface CertificateInputType {}
+
+    /**
+     * Certificate input bytes: the input bytes represent an encoded X.509 Certificate which could
+     * be generated using an {@code CertificateFactory}
+     */
+    public static final int CERT_INPUT_RAW_X509 = 0;
+
+    /**
+     * Certificate input bytes: the input bytes represent the SHA256 output of an encoded X.509
+     * Certificate.
+     */
+    public static final int CERT_INPUT_SHA256 = 1;
+
+    /**
+     * Searches the set of signing certificates by which the given package has proven to have been
+     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported.
+     *
+     * @param packageName package whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
+
+    /**
+     * Searches the set of signing certificates by which the given uid has proven to have been
+     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * since it takes into account the possibility of signing certificate rotation, except in the
+     * case of packages that are signed by multiple certificates, for which signing certificate
+     * rotation is not supported.
+     *
+     * @param uid package whose signing certificates to check
+     * @param certificate signing certificate for which to search
+     * @param type representation of the {@code certificate}
+     * @return true if this package was or is signed by exactly the certificate {@code certificate}
+     */
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @CertificateInputType int type) {
+        throw new UnsupportedOperationException(
+                "hasSigningCertificate not implemented in subclass");
+    }
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4a71467..5b5ccf5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -801,13 +801,40 @@
                 }
             }
         }
+        // deprecated method of getting signing certificates
         if ((flags&PackageManager.GET_SIGNATURES) != 0) {
-            if (p.mSigningDetails.hasSignatures()) {
+            if (p.mSigningDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Return the oldest
+                // cert so that programmatic checks keep working even if unaware of key rotation.
+                pi.signatures = new Signature[1];
+                pi.signatures[0] = p.mSigningDetails.pastSigningCertificates[0];
+            } else if (p.mSigningDetails.hasSignatures()) {
+                // otherwise keep old behavior
                 int numberOfSigs = p.mSigningDetails.signatures.length;
                 pi.signatures = new Signature[numberOfSigs];
                 System.arraycopy(p.mSigningDetails.signatures, 0, pi.signatures, 0, numberOfSigs);
             }
         }
+
+        // replacement for GET_SIGNATURES
+        if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
+            if (p.mSigningDetails.hasPastSigningCertificates()) {
+                // Package has included signing certificate rotation information.  Convert each
+                // entry to an array
+                int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
+                pi.signingCertificateHistory = new Signature[numberOfSigs][];
+                for (int i = 0; i < numberOfSigs; i++) {
+                    pi.signingCertificateHistory[i] =
+                            new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
+                }
+            } else if (p.mSigningDetails.hasSignatures()) {
+                // otherwise keep old behavior
+                int numberOfSigs = p.mSigningDetails.signatures.length;
+                pi.signingCertificateHistory = new Signature[1][numberOfSigs];
+                System.arraycopy(p.mSigningDetails.signatures, 0,
+                        pi.signingCertificateHistory[0], 0, numberOfSigs);
+            }
+        }
         return pi;
     }
 
@@ -5684,23 +5711,74 @@
         @Nullable
         public final ArraySet<PublicKey> publicKeys;
 
+        /**
+         * Collection of {@code Signature} objects, each of which is formed from a former signing
+         * certificate of this APK before it was changed by signing certificate rotation.
+         */
+        @Nullable
+        public final Signature[] pastSigningCertificates;
+
+        /**
+         * Flags for the {@code pastSigningCertificates} collection, which indicate the capabilities
+         * the including APK wishes to grant to its past signing certificates.
+         */
+        @Nullable
+        public final int[] pastSigningCertificatesFlags;
+
         /** A representation of unknown signing details. Use instead of null. */
         public static final SigningDetails UNKNOWN =
-                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null);
+                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
 
         @VisibleForTesting
         public SigningDetails(Signature[] signatures,
                 @SignatureSchemeVersion int signatureSchemeVersion,
-                ArraySet<PublicKey> keys) {
+                ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
+                int[] pastSigningCertificatesFlags) {
             this.signatures = signatures;
             this.signatureSchemeVersion = signatureSchemeVersion;
             this.publicKeys = keys;
+            this.pastSigningCertificates = pastSigningCertificates;
+            this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
+        }
+
+        public SigningDetails(Signature[] signatures,
+                @SignatureSchemeVersion int signatureSchemeVersion,
+                Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+                throws CertificateException {
+            this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
+                    pastSigningCertificates, pastSigningCertificatesFlags);
         }
 
         public SigningDetails(Signature[] signatures,
                 @SignatureSchemeVersion int signatureSchemeVersion)
                 throws CertificateException {
-            this(signatures, signatureSchemeVersion, toSigningKeys(signatures));
+            this(signatures, signatureSchemeVersion,
+                    null, null);
+        }
+
+        public SigningDetails(SigningDetails orig) {
+            if (orig != null) {
+                if (orig.signatures != null) {
+                    this.signatures = orig.signatures.clone();
+                } else {
+                    this.signatures = null;
+                }
+                this.signatureSchemeVersion = orig.signatureSchemeVersion;
+                this.publicKeys = new ArraySet<>(orig.publicKeys);
+                if (orig.pastSigningCertificates != null) {
+                    this.pastSigningCertificates = orig.pastSigningCertificates.clone();
+                    this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
+                } else {
+                    this.pastSigningCertificates = null;
+                    this.pastSigningCertificatesFlags = null;
+                }
+            } else {
+                this.signatures = null;
+                this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+                this.publicKeys = null;
+                this.pastSigningCertificates = null;
+                this.pastSigningCertificatesFlags = null;
+            }
         }
 
         /** Returns true if the signing details have one or more signatures. */
@@ -5708,6 +5786,11 @@
             return signatures != null && signatures.length > 0;
         }
 
+        /** Returns true if the signing details have past signing certificates. */
+        public boolean hasPastSigningCertificates() {
+            return pastSigningCertificates != null && pastSigningCertificates.length > 0;
+        }
+
         /** Returns true if the signatures in this and other match exactly. */
         public boolean signaturesMatchExactly(SigningDetails other) {
             return Signature.areExactMatch(this.signatures, other.signatures);
@@ -5728,6 +5811,8 @@
             dest.writeTypedArray(this.signatures, flags);
             dest.writeInt(this.signatureSchemeVersion);
             dest.writeArraySet(this.publicKeys);
+            dest.writeTypedArray(this.pastSigningCertificates, flags);
+            dest.writeIntArray(this.pastSigningCertificatesFlags);
         }
 
         protected SigningDetails(Parcel in) {
@@ -5735,6 +5820,8 @@
             this.signatures = in.createTypedArray(Signature.CREATOR);
             this.signatureSchemeVersion = in.readInt();
             this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
+            this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
+            this.pastSigningCertificatesFlags = in.createIntArray();
         }
 
         public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -5761,8 +5848,23 @@
 
             if (signatureSchemeVersion != that.signatureSchemeVersion) return false;
             if (!Signature.areExactMatch(signatures, that.signatures)) return false;
-            return publicKeys != null ? publicKeys.equals(that.publicKeys)
-                    : that.publicKeys == null;
+            if (publicKeys != null) {
+                if (!publicKeys.equals((that.publicKeys))) {
+                    return false;
+                }
+            } else if (that.publicKeys != null) {
+                return false;
+            }
+
+            // can't use Signature.areExactMatch() because order matters with the past signing certs
+            if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
+                return false;
+            }
+            if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
+                return false;
+            }
+
+            return true;
         }
 
         @Override
@@ -5770,8 +5872,77 @@
             int result = +Arrays.hashCode(signatures);
             result = 31 * result + signatureSchemeVersion;
             result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
+            result = 31 * result + Arrays.hashCode(pastSigningCertificates);
+            result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
             return result;
         }
+
+        /**
+         * Builder of {@code SigningDetails} instances.
+         */
+        public static class Builder {
+            private Signature[] mSignatures;
+            private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+            private Signature[] mPastSigningCertificates;
+            private int[] mPastSigningCertificatesFlags;
+
+            public Builder() {
+            }
+
+            /** get signing certificates used to sign the current APK */
+            public Builder setSignatures(Signature[] signatures) {
+                mSignatures = signatures;
+                return this;
+            }
+
+            /** set the signature scheme version used to sign the APK */
+            public Builder setSignatureSchemeVersion(int signatureSchemeVersion) {
+                mSignatureSchemeVersion = signatureSchemeVersion;
+                return this;
+            }
+
+            /** set the signing certificates by which the APK proved it can be authenticated */
+            public Builder setPastSigningCertificates(Signature[] pastSigningCertificates) {
+                mPastSigningCertificates = pastSigningCertificates;
+                return this;
+            }
+
+            /** set the flags for the {@code pastSigningCertificates} */
+            public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
+                mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
+                return this;
+            }
+
+            private void checkInvariants() {
+                // must have signatures and scheme version set
+                if (mSignatures == null) {
+                    throw new IllegalStateException("SigningDetails requires the current signing"
+                            + " certificates.");
+                }
+
+                // pastSigningCerts and flags must match up
+                boolean pastMismatch = false;
+                if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
+                    if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
+                        pastMismatch = true;
+                    }
+                } else if (!(mPastSigningCertificates == null
+                        && mPastSigningCertificatesFlags == null)) {
+                    pastMismatch = true;
+                }
+                if (pastMismatch) {
+                    throw new IllegalStateException("SigningDetails must have a one to one mapping "
+                            + "between pastSigningCertificates and pastSigningCertificatesFlags");
+                }
+            }
+            /** build a {@code SigningDetails} object */
+            public SigningDetails build()
+                    throws CertificateException {
+                checkInvariants();
+                return new SigningDetails(mSignatures, mSignatureSchemeVersion,
+                        mPastSigningCertificates, mPastSigningCertificatesFlags);
+            }
+        }
     }
 
     /**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 11d338d..166342d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3763,4 +3763,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * The network watchlist is a list of domains and IP addresses that are associated with
+     * potentially harmful apps. This method returns the hash of the watchlist currently
+     * used by the system.
+     *
+     * @return Hash of network watchlist config file. Null if config does not exist.
+     */
+    public byte[] getNetworkWatchlistConfigHash() {
+        try {
+            return mService.getNetworkWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index a6fe738..ce95b60 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -180,4 +180,6 @@
     void stopKeepalive(in Network network, int slot);
 
     String getCaptivePortalServerUrl();
+
+    byte[] getNetworkWatchlistConfigHash();
 }
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index f04f03f6..6125394 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -748,7 +748,7 @@
      * @hide
      */
     @SystemApi
-    void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
+    public void applyTunnelModeTransform(IpSecTunnelInterface tunnel, int direction,
             IpSecTransform transform) throws IOException {
         // TODO: call IpSecService
     }
diff --git a/core/java/android/net/NetworkWatchlistManager.java b/core/java/android/net/NetworkWatchlistManager.java
index 5425bf5..49047d3 100644
--- a/core/java/android/net/NetworkWatchlistManager.java
+++ b/core/java/android/net/NetworkWatchlistManager.java
@@ -86,4 +86,16 @@
             e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get Network Watchlist config file hash.
+     */
+    public byte[] getWatchlistConfigHash() {
+        try {
+            return mNetworkWatchlistManager.getWatchlistConfigHash();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to get watchlist config hash");
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index 94a44ec..dda0ed8 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -82,6 +82,14 @@
     public static final String ACTION_UPDATE_SMART_SELECTION
             = "android.intent.action.UPDATE_SMART_SELECTION";
 
+    /**
+     * Update network watchlist config file.
+     * @hide
+     */
+    @SystemApi
+    public static final String ACTION_UPDATE_NETWORK_WATCHLIST
+            = "android.intent.action.UPDATE_NETWORK_WATCHLIST";
+
     private ConfigUpdate() {
     }
 }
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 5c5e351..fc88e90 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -202,7 +202,8 @@
         mLooper = Looper.myLooper();
         if (mLooper == null) {
             throw new RuntimeException(
-                "Can't create handler inside thread that has not called Looper.prepare()");
+                "Can't create handler inside thread " + Thread.currentThread()
+                        + " that has not called Looper.prepare()");
         }
         mQueue = mLooper.mQueue;
         mCallback = callback;
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7654e9b..7683880 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -143,7 +143,7 @@
      * Defines the UID/GID for the WebView zygote process.
      * @hide
      */
-    public static final int WEBVIEW_ZYGOTE_UID = 1051;
+    public static final int WEBVIEW_ZYGOTE_UID = 1053;
 
     /**
      * Defines the UID used for resource tracking for OTA updates.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4228fbb..1c41979 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -41,7 +41,6 @@
 import android.app.AppOpsManager;
 import android.app.Application;
 import android.app.NotificationChannel;
-import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.app.SearchManager;
 import android.app.WallpaperManager;
@@ -75,7 +74,6 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.provider.SettingsValidators;
 import android.provider.SettingsValidators.Validator;
 import android.speech.tts.TextToSpeech;
 import android.telephony.SubscriptionManager;
@@ -1345,18 +1343,6 @@
             = "android.settings.CHANNEL_NOTIFICATION_SETTINGS";
 
     /**
-     * Activity Action: Show notification settings for a single {@link NotificationChannelGroup}.
-     * <p>
-     *     Input: {@link #EXTRA_APP_PACKAGE}, the package containing the channel group to display.
-     *     Input: {@link #EXTRA_CHANNEL_GROUP_ID}, the id of the channel group to display.
-     * <p>
-     * Output: Nothing.
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS =
-            "android.settings.CHANNEL_GROUP_NOTIFICATION_SETTINGS";
-
-    /**
      * Activity Extra: The package owner of the notification channel settings to display.
      * <p>
      * This must be passed as an extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}.
@@ -1372,15 +1358,6 @@
     public static final String EXTRA_CHANNEL_ID = "android.provider.extra.CHANNEL_ID";
 
     /**
-     * Activity Extra: The {@link NotificationChannelGroup#getId()} of the notification channel
-     * group settings to display.
-     * <p>
-     * This must be passed as an extra field to the
-     * {@link #ACTION_CHANNEL_GROUP_NOTIFICATION_SETTINGS}.
-     */
-    public static final String EXTRA_CHANNEL_GROUP_ID = "android.provider.extra.CHANNEL_GROUP_ID";
-
-    /**
      * Activity Action: Show notification redaction settings.
      *
      * @hide
@@ -5509,37 +5486,54 @@
          * Note: do not rely on this value being present in settings.db or on ContentObserver
          * notifications for the corresponding Uri. Use {@link LocationManager#MODE_CHANGED_ACTION}
          * to receive changes in this value.
+         *
+         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+         *             get the status of a location provider, use
+         *             {@link LocationManager#isProviderEnabled(String)}.
          */
+        @Deprecated
         public static final String LOCATION_MODE = "location_mode";
-        /**
-         * Stores the previous location mode when {@link #LOCATION_MODE} is set to
-         * {@link #LOCATION_MODE_OFF}
-         * @hide
-         */
-        public static final String LOCATION_PREVIOUS_MODE = "location_previous_mode";
 
         /**
-         * Sets all location providers to the previous states before location was turned off.
-         * @hide
-         */
-        public static final int LOCATION_MODE_PREVIOUS = -1;
-        /**
          * Location access disabled.
+         *
+         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+         *             get the status of a location provider, use
+         *             {@link LocationManager#isProviderEnabled(String)}.
          */
+        @Deprecated
         public static final int LOCATION_MODE_OFF = 0;
+
         /**
          * Network Location Provider disabled, but GPS and other sensors enabled.
+         *
+         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+         *             get the status of a location provider, use
+         *             {@link LocationManager#isProviderEnabled(String)}.
          */
+        @Deprecated
         public static final int LOCATION_MODE_SENSORS_ONLY = 1;
+
         /**
          * Reduced power usage, such as limiting the number of GPS updates per hour. Requests
          * with {@link android.location.Criteria#POWER_HIGH} may be downgraded to
          * {@link android.location.Criteria#POWER_MEDIUM}.
+         *
+         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+         *             get the status of a location provider, use
+         *             {@link LocationManager#isProviderEnabled(String)}.
          */
+        @Deprecated
         public static final int LOCATION_MODE_BATTERY_SAVING = 2;
+
         /**
          * Best-effort location computation allowed.
+         *
+         * @deprecated To check location status, use {@link LocationManager#isLocationEnabled()}. To
+         *             get the status of a location provider, use
+         *             {@link LocationManager#isProviderEnabled(String)}.
          */
+        @Deprecated
         public static final int LOCATION_MODE_HIGH_ACCURACY = 3;
 
         /**
@@ -7866,7 +7860,6 @@
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
             CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_MODE);
-            CLONE_TO_MANAGED_PROFILE.add(LOCATION_PREVIOUS_MODE);
             CLONE_TO_MANAGED_PROFILE.add(LOCATION_PROVIDERS_ALLOWED);
             CLONE_TO_MANAGED_PROFILE.add(SELECTED_INPUT_METHOD_SUBTYPE);
         }
@@ -7917,8 +7910,7 @@
          * @param provider the location provider to query
          * @return true if the provider is enabled
          *
-         * @deprecated use {@link #LOCATION_MODE} or
-         *             {@link LocationManager#isProviderEnabled(String)}
+         * @deprecated use {@link LocationManager#isProviderEnabled(String)}
          */
         @Deprecated
         public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) {
@@ -7931,12 +7923,13 @@
          * @param provider the location provider to query
          * @param userId the userId to query
          * @return true if the provider is enabled
-         * @deprecated use {@link #LOCATION_MODE} or
-         *             {@link LocationManager#isProviderEnabled(String)}
+         *
+         * @deprecated use {@link LocationManager#isProviderEnabled(String)}
          * @hide
          */
         @Deprecated
-        public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+        public static final boolean isLocationProviderEnabledForUser(
+                ContentResolver cr, String provider, int userId) {
             String allowedProviders = Settings.Secure.getStringForUser(cr,
                     LOCATION_PROVIDERS_ALLOWED, userId);
             return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
@@ -7947,7 +7940,8 @@
          * @param cr the content resolver to use
          * @param provider the location provider to enable or disable
          * @param enabled true if the provider should be enabled
-         * @deprecated use {@link #putInt(ContentResolver, String, int)} and {@link #LOCATION_MODE}
+         * @deprecated This API is deprecated. It requires WRITE_SECURE_SETTINGS permission to
+         *             change location settings.
          */
         @Deprecated
         public static final void setLocationProviderEnabled(ContentResolver cr,
@@ -7963,8 +7957,8 @@
          * @param enabled true if the provider should be enabled
          * @param userId the userId for which to enable/disable providers
          * @return true if the value was set, false on database errors
-         * @deprecated use {@link #putIntForUser(ContentResolver, String, int, int)} and
-         *             {@link #LOCATION_MODE}
+         *
+         * @deprecated use {@link LocationManager#setProviderEnabledForUser(String, boolean, int)}
          * @hide
          */
         @Deprecated
@@ -7985,28 +7979,6 @@
         }
 
         /**
-         * Saves the current location mode into {@link #LOCATION_PREVIOUS_MODE}.
-         */
-        private static final boolean saveLocationModeForUser(ContentResolver cr, int userId) {
-            final int mode = getLocationModeForUser(cr, userId);
-            return putIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE, mode, userId);
-        }
-
-        /**
-         * Restores the current location mode from {@link #LOCATION_PREVIOUS_MODE}.
-         */
-        private static final boolean restoreLocationModeForUser(ContentResolver cr, int userId) {
-            int mode = getIntForUser(cr, Settings.Secure.LOCATION_PREVIOUS_MODE,
-                    LOCATION_MODE_HIGH_ACCURACY, userId);
-            // Make sure that the previous mode is never "off". Otherwise the user won't be able to
-            // turn on location any longer.
-            if (mode == LOCATION_MODE_OFF) {
-                mode = LOCATION_MODE_HIGH_ACCURACY;
-            }
-            return setLocationModeForUser(cr, mode, userId);
-        }
-
-        /**
          * Thread-safe method for setting the location mode to one of
          * {@link #LOCATION_MODE_HIGH_ACCURACY}, {@link #LOCATION_MODE_SENSORS_ONLY},
          * {@link #LOCATION_MODE_BATTERY_SAVING}, or {@link #LOCATION_MODE_OFF}.
@@ -8019,18 +7991,20 @@
          * @return true if the value was set, false on database errors
          *
          * @throws IllegalArgumentException if mode is not one of the supported values
+         *
+         * @deprecated To enable/disable location, use
+         *             {@link LocationManager#setLocationEnabledForUser(boolean, int)}.
+         *             To enable/disable a specific location provider, use
+         *             {@link LocationManager#setProviderEnabledForUser(String, boolean, int)}.
          */
-        private static final boolean setLocationModeForUser(ContentResolver cr, int mode,
-                int userId) {
+        @Deprecated
+        private static boolean setLocationModeForUser(
+                ContentResolver cr, int mode, int userId) {
             synchronized (mLocationSettingsLock) {
                 boolean gps = false;
                 boolean network = false;
                 switch (mode) {
-                    case LOCATION_MODE_PREVIOUS:
-                        // Retrieve the actual mode and set to that mode.
-                        return restoreLocationModeForUser(cr, userId);
                     case LOCATION_MODE_OFF:
-                        saveLocationModeForUser(cr, userId);
                         break;
                     case LOCATION_MODE_SENSORS_ONLY:
                         gps = true;
@@ -8045,15 +8019,7 @@
                     default:
                         throw new IllegalArgumentException("Invalid location mode: " + mode);
                 }
-                // Note it's important that we set the NLP mode first. The Google implementation
-                // of NLP clears its NLP consent setting any time it receives a
-                // LocationManager.PROVIDERS_CHANGED_ACTION broadcast and NLP is disabled. Also,
-                // it shows an NLP consent dialog any time it receives the broadcast, NLP is
-                // enabled, and the NLP consent is not set. If 1) we were to enable GPS first,
-                // 2) a setup wizard has its own NLP consent UI that sets the NLP consent setting,
-                // and 3) the receiver happened to complete before we enabled NLP, then the Google
-                // NLP would detect the attempt to enable NLP and show a redundant NLP consent
-                // dialog. Then the people who wrote the setup wizard would be sad.
+
                 boolean nlpSuccess = Settings.Secure.setLocationProviderEnabledForUser(
                         cr, LocationManager.NETWORK_PROVIDER, network, userId);
                 boolean gpsSuccess = Settings.Secure.setLocationProviderEnabledForUser(
@@ -9424,6 +9390,14 @@
        public static final String WIFI_VERBOSE_LOGGING_ENABLED =
                "wifi_verbose_logging_enabled";
 
+        /**
+         * Setting to enable connected MAC randomization in Wi-Fi; disabled by default, and
+         * setting to 1 will enable it. In the future, additional values may be supported.
+         * @hide
+         */
+        public static final String WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED =
+                "wifi_connected_mac_randomization_enabled";
+
        /**
         * The maximum number of times we will retry a connection to an access
         * point for which we have failed in acquiring an IP address from DHCP.
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 72afbb8..3464370 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -102,6 +102,7 @@
     public static final int KM_ALGORITHM_RSA = 1;
     public static final int KM_ALGORITHM_EC = 3;
     public static final int KM_ALGORITHM_AES = 32;
+    public static final int KM_ALGORITHM_3DES = 33;
     public static final int KM_ALGORITHM_HMAC = 128;
 
     // Block modes.
@@ -131,6 +132,7 @@
     public static final int KM_ORIGIN_GENERATED = 0;
     public static final int KM_ORIGIN_IMPORTED = 2;
     public static final int KM_ORIGIN_UNKNOWN = 3;
+    public static final int KM_ORIGIN_SECURELY_IMPORTED = 4;
 
     // Key usability requirements.
     public static final int KM_BLOB_STANDALONE = 0;
@@ -141,6 +143,7 @@
     public static final int KM_PURPOSE_DECRYPT = 1;
     public static final int KM_PURPOSE_SIGN = 2;
     public static final int KM_PURPOSE_VERIFY = 3;
+    public static final int KM_PURPOSE_WRAP = 5;
 
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = 0;
diff --git a/core/java/android/security/keystore/BackwardsCompat.java b/core/java/android/security/keystore/BackwardsCompat.java
new file mode 100644
index 0000000..69558c4
--- /dev/null
+++ b/core/java/android/security/keystore/BackwardsCompat.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.security.keystore;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Function;
+
+/**
+ * Helpers for converting classes between old and new API, so we can preserve backwards
+ * compatibility while teamfooding. This will be removed soon.
+ *
+ * @hide
+ */
+class BackwardsCompat {
+
+
+    static KeychainProtectionParams toLegacyKeychainProtectionParams(
+            android.security.keystore.recovery.KeyChainProtectionParams keychainProtectionParams
+    ) {
+        return new KeychainProtectionParams.Builder()
+                .setUserSecretType(keychainProtectionParams.getUserSecretType())
+                .setSecret(keychainProtectionParams.getSecret())
+                .setLockScreenUiFormat(keychainProtectionParams.getLockScreenUiFormat())
+                .setKeyDerivationParams(
+                        toLegacyKeyDerivationParams(
+                                keychainProtectionParams.getKeyDerivationParams()))
+                .build();
+    }
+
+    static KeyDerivationParams toLegacyKeyDerivationParams(
+            android.security.keystore.recovery.KeyDerivationParams keyDerivationParams
+    ) {
+        return new KeyDerivationParams(
+                keyDerivationParams.getAlgorithm(), keyDerivationParams.getSalt());
+    }
+
+    static WrappedApplicationKey toLegacyWrappedApplicationKey(
+            android.security.keystore.recovery.WrappedApplicationKey wrappedApplicationKey
+    ) {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(wrappedApplicationKey.getAlias())
+                .setEncryptedKeyMaterial(wrappedApplicationKey.getEncryptedKeyMaterial())
+                .build();
+    }
+
+    static android.security.keystore.recovery.KeyDerivationParams fromLegacyKeyDerivationParams(
+            KeyDerivationParams keyDerivationParams
+    ) {
+        return new android.security.keystore.recovery.KeyDerivationParams(
+                keyDerivationParams.getAlgorithm(), keyDerivationParams.getSalt());
+    }
+
+    static android.security.keystore.recovery.WrappedApplicationKey fromLegacyWrappedApplicationKey(
+            WrappedApplicationKey wrappedApplicationKey
+    ) {
+        return new android.security.keystore.recovery.WrappedApplicationKey.Builder()
+                .setAlias(wrappedApplicationKey.getAlias())
+                .setEncryptedKeyMaterial(wrappedApplicationKey.getEncryptedKeyMaterial())
+                .build();
+    }
+
+    static List<android.security.keystore.recovery.WrappedApplicationKey>
+            fromLegacyWrappedApplicationKeys(List<WrappedApplicationKey> wrappedApplicationKeys
+    ) {
+        return map(wrappedApplicationKeys, BackwardsCompat::fromLegacyWrappedApplicationKey);
+    }
+
+    static List<android.security.keystore.recovery.KeyChainProtectionParams>
+            fromLegacyKeychainProtectionParams(
+                    List<KeychainProtectionParams> keychainProtectionParams) {
+        return map(keychainProtectionParams, BackwardsCompat::fromLegacyKeychainProtectionParam);
+    }
+
+    static android.security.keystore.recovery.KeyChainProtectionParams
+            fromLegacyKeychainProtectionParam(KeychainProtectionParams keychainProtectionParams) {
+        return new android.security.keystore.recovery.KeyChainProtectionParams.Builder()
+                .setUserSecretType(keychainProtectionParams.getUserSecretType())
+                .setSecret(keychainProtectionParams.getSecret())
+                .setLockScreenUiFormat(keychainProtectionParams.getLockScreenUiFormat())
+                .setKeyDerivationParams(
+                        fromLegacyKeyDerivationParams(
+                                keychainProtectionParams.getKeyDerivationParams()))
+                .build();
+    }
+
+    static KeychainSnapshot toLegacyKeychainSnapshot(
+            android.security.keystore.recovery.KeyChainSnapshot keychainSnapshot
+    ) {
+        return new KeychainSnapshot.Builder()
+                .setCounterId(keychainSnapshot.getCounterId())
+                .setEncryptedRecoveryKeyBlob(keychainSnapshot.getEncryptedRecoveryKeyBlob())
+                .setTrustedHardwarePublicKey(keychainSnapshot.getTrustedHardwarePublicKey())
+                .setSnapshotVersion(keychainSnapshot.getSnapshotVersion())
+                .setMaxAttempts(keychainSnapshot.getMaxAttempts())
+                .setServerParams(keychainSnapshot.getServerParams())
+                .setKeychainProtectionParams(
+                        map(keychainSnapshot.getKeyChainProtectionParams(),
+                                BackwardsCompat::toLegacyKeychainProtectionParams))
+                .setWrappedApplicationKeys(
+                        map(keychainSnapshot.getWrappedApplicationKeys(),
+                                BackwardsCompat::toLegacyWrappedApplicationKey))
+                .build();
+    }
+
+    static <A, B> List<B> map(List<A> as, Function<A, B> f) {
+        ArrayList<B> bs = new ArrayList<>(as.size());
+        for (A a : as) {
+            bs.add(f.apply(a));
+        }
+        return bs;
+    }
+}
diff --git a/core/java/android/security/keystore/KeyDerivationParams.java b/core/java/android/security/keystore/KeyDerivationParams.java
index b702acc..b19cee2 100644
--- a/core/java/android/security/keystore/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/KeyDerivationParams.java
@@ -61,7 +61,7 @@
         return new KeyDerivationParams(ALGORITHM_SHA256, salt);
     }
 
-    private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+    KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
         mAlgorithm = algorithm;
         mSalt = Preconditions.checkNotNull(salt);
     }
diff --git a/core/java/android/security/keystore/KeychainProtectionParams.aidl b/core/java/android/security/keystore/KeychainProtectionParams.aidl
deleted file mode 100644
index 0341223..0000000
--- a/core/java/android/security/keystore/KeychainProtectionParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (C) 2017 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.security.keystore;
-
-/* @hide */
-parcelable KeychainProtectionParams;
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index 87283cb..8be6d52 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -167,7 +167,7 @@
     public @NonNull KeychainSnapshot getRecoveryData(@NonNull byte[] account)
             throws InternalRecoveryServiceException {
         try {
-            return mBinder.getRecoveryData(account);
+            return BackwardsCompat.toLegacyKeychainSnapshot(mBinder.getRecoveryData(account));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
@@ -360,28 +360,6 @@
     }
 
     /**
-     * Method notifies KeyStore that a user-generated secret is available. This method generates a
-     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
-     * should use {@link KeychainProtectionParams#clearSecret} to override the secret value in
-     * memory.
-     *
-     * @param recoverySecret user generated secret together with parameters necessary to regenerate
-     *     it on a new device.
-     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
-     *     service.
-     */
-    public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret)
-            throws InternalRecoveryServiceException {
-        try {
-            mBinder.recoverySecretAvailable(recoverySecret);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
-    }
-
-    /**
      * Initializes recovery session and returns a blob with proof of recovery secrets possession.
      * The method generates symmetric key for a session, which trusted remote device can use to
      * return recovery key.
@@ -417,7 +395,7 @@
                             verifierPublicKey,
                             vaultParams,
                             vaultChallenge,
-                            secrets);
+                            BackwardsCompat.fromLegacyKeychainProtectionParams(secrets));
             return new RecoveryClaim(recoverySession, recoveryClaim);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -451,7 +429,9 @@
             InternalRecoveryServiceException {
         try {
             return (Map<String, byte[]>) mBinder.recoverKeys(
-                    session.getSessionId(), recoveryKeyBlob, applicationKeys);
+                    session.getSessionId(),
+                    recoveryKeyBlob,
+                    BackwardsCompat.fromLegacyWrappedApplicationKeys(applicationKeys));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (ServiceSpecificException e) {
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/core/java/android/security/keystore/recovery/BadCertificateFormatException.java
similarity index 60%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to core/java/android/security/keystore/recovery/BadCertificateFormatException.java
index b35713f..fda3387 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/core/java/android/security/keystore/recovery/BadCertificateFormatException.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,15 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.security.keystore.recovery;
 
-/* @hide */
-parcelable KeychainSnapshot;
+/**
+ * Error thrown when the recovery agent supplies an invalid X509 certificate.
+ *
+ * @hide
+ */
+public class BadCertificateFormatException extends RecoveryControllerException {
+    public BadCertificateFormatException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/DecryptionFailedException.java b/core/java/android/security/keystore/recovery/DecryptionFailedException.java
new file mode 100644
index 0000000..93f033f
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/DecryptionFailedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * Error thrown when decryption failed, due to an agent error. i.e., using the incorrect key,
+ * trying to decrypt garbage data, trying to decrypt data that has somehow been corrupted, etc.
+ *
+ * @hide
+ */
+public class DecryptionFailedException extends GeneralSecurityException {
+
+    public DecryptionFailedException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/InternalRecoveryServiceException.java b/core/java/android/security/keystore/recovery/InternalRecoveryServiceException.java
new file mode 100644
index 0000000..9a03226
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/InternalRecoveryServiceException.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * An error thrown when something went wrong internally in the recovery service.
+ *
+ * <p>This is an unexpected error, and indicates a problem with the service itself, rather than the
+ * caller having performed some kind of illegal action.
+ *
+ * @hide
+ */
+public class InternalRecoveryServiceException extends GeneralSecurityException {
+    public InternalRecoveryServiceException(String msg) {
+        super(msg);
+    }
+
+    public InternalRecoveryServiceException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.aidl
similarity index 88%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to core/java/android/security/keystore/recovery/KeyChainProtectionParams.aidl
index b35713f..58edc84 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.security.keystore.recovery;
 
 /* @hide */
-parcelable KeychainSnapshot;
+parcelable KeyChainProtectionParams;
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
new file mode 100644
index 0000000..7ccb909
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2017 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.security.keystore.recovery;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * A {@link KeyChainSnapshot} is protected with a key derived from the user's lock screen. This
+ * class wraps all the data necessary to derive the same key on a recovering device:
+ *
+ * <ul>
+ *     <li>UI parameters for the user's lock screen - so that if e.g., the user was using a pattern,
+ *         the recovering device can display the pattern UI to the user when asking them to enter
+ *         the lock screen from their previous device.
+ *     <li>The algorithm used to derive a key from the user's lock screen, e.g. SHA-256 with a salt.
+ * </ul>
+ *
+ * <p>As such, this data is sent along with the {@link KeyChainSnapshot} when syncing the current
+ * version of the keychain.
+ *
+ * <p>For now, the recoverable keychain only supports a single layer of protection, which is the
+ * user's lock screen. In the future, the keychain will support multiple layers of protection
+ * (e.g. an additional keychain password, along with the lock screen).
+ *
+ * @hide
+ */
+public final class KeyChainProtectionParams implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+    public @interface UserSecretType {
+    }
+
+    /**
+     * Lockscreen secret is required to recover KeyStore.
+     */
+    public static final int TYPE_LOCKSCREEN = 100;
+
+    /**
+     * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
+     */
+    public static final int TYPE_CUSTOM_PASSWORD = 101;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"UI_FORMAT_"}, value = {UI_FORMAT_PIN, UI_FORMAT_PASSWORD, UI_FORMAT_PATTERN})
+    public @interface LockScreenUiFormat {
+    }
+
+    /**
+     * Pin with digits only.
+     */
+    public static final int UI_FORMAT_PIN = 1;
+
+    /**
+     * Password. String with latin-1 characters only.
+     */
+    public static final int UI_FORMAT_PASSWORD = 2;
+
+    /**
+     * Pattern with 3 by 3 grid.
+     */
+    public static final int UI_FORMAT_PATTERN = 3;
+
+    @UserSecretType
+    private Integer mUserSecretType;
+
+    @LockScreenUiFormat
+    private Integer mLockScreenUiFormat;
+
+    /**
+     * Parameters of the key derivation function, including algorithm, difficulty, salt.
+     */
+    private KeyDerivationParams mKeyDerivationParams;
+    private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
+
+    /**
+     * @param secret Constructor creates a reference to the secret. Caller must use
+     * @link {#clearSecret} to overwrite its value in memory.
+     * @hide
+     */
+    public KeyChainProtectionParams(@UserSecretType int userSecretType,
+            @LockScreenUiFormat int lockScreenUiFormat,
+            @NonNull KeyDerivationParams keyDerivationParams,
+            @NonNull byte[] secret) {
+        mUserSecretType = userSecretType;
+        mLockScreenUiFormat = lockScreenUiFormat;
+        mKeyDerivationParams = Preconditions.checkNotNull(keyDerivationParams);
+        mSecret = Preconditions.checkNotNull(secret);
+    }
+
+    private KeyChainProtectionParams() {
+
+    }
+
+    /**
+     * @see TYPE_LOCKSCREEN
+     * @see TYPE_CUSTOM_PASSWORD
+     */
+    public @UserSecretType int getUserSecretType() {
+        return mUserSecretType;
+    }
+
+    /**
+     * Specifies UX shown to user during recovery.
+     * Default value is {@code UI_FORMAT_LOCKSCREEN}
+     *
+     * @see UI_FORMAT_PIN
+     * @see UI_FORMAT_PASSWORD
+     * @see UI_FORMAT_PATTERN
+     */
+    public @LockScreenUiFormat int getLockScreenUiFormat() {
+        return mLockScreenUiFormat;
+    }
+
+    /**
+     * Specifies function used to derive symmetric key from user input
+     * Format is defined in separate util class.
+     */
+    public @NonNull KeyDerivationParams getKeyDerivationParams() {
+        return mKeyDerivationParams;
+    }
+
+    /**
+     * Secret derived from user input.
+     * Default value is empty array
+     *
+     * @return secret or empty array
+     */
+    public @NonNull byte[] getSecret() {
+        return mSecret;
+    }
+
+    /**
+     * Builder for creating {@link KeyChainProtectionParams}.
+     */
+    public static class Builder {
+        private KeyChainProtectionParams mInstance = new KeyChainProtectionParams();
+
+        /**
+         * Sets user secret type.
+         *
+         * @see TYPE_LOCKSCREEN
+         * @see TYPE_CUSTOM_PASSWORD
+         * @param userSecretType The secret type
+         * @return This builder.
+         */
+        public Builder setUserSecretType(@UserSecretType int userSecretType) {
+            mInstance.mUserSecretType = userSecretType;
+            return this;
+        }
+
+        /**
+         * Sets UI format.
+         *
+         * @see UI_FORMAT_PIN
+         * @see UI_FORMAT_PASSWORD
+         * @see UI_FORMAT_PATTERN
+         * @param lockScreenUiFormat The UI format
+         * @return This builder.
+         */
+        public Builder setLockScreenUiFormat(@LockScreenUiFormat int lockScreenUiFormat) {
+            mInstance.mLockScreenUiFormat = lockScreenUiFormat;
+            return this;
+        }
+
+        /**
+         * Sets parameters of the key derivation function.
+         *
+         * @param keyDerivationParams Key derivation Params
+         * @return This builder.
+         */
+        public Builder setKeyDerivationParams(@NonNull KeyDerivationParams
+                keyDerivationParams) {
+            mInstance.mKeyDerivationParams = keyDerivationParams;
+            return this;
+        }
+
+        /**
+         * Secret derived from user input, or empty array.
+         *
+         * @param secret The secret.
+         * @return This builder.
+         */
+        public Builder setSecret(@NonNull byte[] secret) {
+            mInstance.mSecret = secret;
+            return this;
+        }
+
+
+        /**
+         * Creates a new {@link KeyChainProtectionParams} instance.
+         * The instance will include default values, if {@link setSecret}
+         * or {@link setUserSecretType} were not called.
+         *
+         * @return new instance
+         * @throws NullPointerException if some required fields were not set.
+         */
+        @NonNull public KeyChainProtectionParams build() {
+            if (mInstance.mUserSecretType == null) {
+                mInstance.mUserSecretType = TYPE_LOCKSCREEN;
+            }
+            Preconditions.checkNotNull(mInstance.mLockScreenUiFormat);
+            Preconditions.checkNotNull(mInstance.mKeyDerivationParams);
+            if (mInstance.mSecret == null) {
+                mInstance.mSecret = new byte[]{};
+            }
+            return mInstance;
+        }
+    }
+
+    /**
+     * Removes secret from memory than object is no longer used.
+     * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        clearSecret();
+        super.finalize();
+    }
+
+    /**
+     * Fills mSecret with zeroes.
+     */
+    public void clearSecret() {
+        Arrays.fill(mSecret, (byte) 0);
+    }
+
+    public static final Parcelable.Creator<KeyChainProtectionParams> CREATOR =
+            new Parcelable.Creator<KeyChainProtectionParams>() {
+        public KeyChainProtectionParams createFromParcel(Parcel in) {
+            return new KeyChainProtectionParams(in);
+        }
+
+        public KeyChainProtectionParams[] newArray(int length) {
+            return new KeyChainProtectionParams[length];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mUserSecretType);
+        out.writeInt(mLockScreenUiFormat);
+        out.writeTypedObject(mKeyDerivationParams, flags);
+        out.writeByteArray(mSecret);
+    }
+
+    /**
+     * @hide
+     */
+    protected KeyChainProtectionParams(Parcel in) {
+        mUserSecretType = in.readInt();
+        mLockScreenUiFormat = in.readInt();
+        mKeyDerivationParams = in.readTypedObject(KeyDerivationParams.CREATOR);
+        mSecret = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/core/java/android/security/keystore/recovery/KeyChainSnapshot.aidl
similarity index 89%
rename from core/java/android/security/keystore/KeychainSnapshot.aidl
rename to core/java/android/security/keystore/recovery/KeyChainSnapshot.aidl
index b35713f..d02a2ea 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.security.keystore.recovery;
 
 /* @hide */
-parcelable KeychainSnapshot;
+parcelable KeyChainSnapshot;
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
new file mode 100644
index 0000000..9639bb5e
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2017 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.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * A snapshot of a version of the keystore. Two events can trigger the generation of a new snapshot:
+ *
+ * <ul>
+ *     <li>The user's lock screen changes. (A key derived from the user's lock screen is used to
+ *         protected the keychain, which is why this forces a new snapshot.)
+ *     <li>A key is added to or removed from the recoverable keychain.
+ * </ul>
+ *
+ * <p>The snapshot data is also encrypted with the remote trusted hardware's public key, so even
+ * the recovery agent itself should not be able to decipher the data. The recovery agent sends an
+ * instance of this to the remote trusted hardware whenever a new snapshot is generated. During a
+ * recovery flow, the recovery agent retrieves a snapshot from the remote trusted hardware. It then
+ * sends it to the framework, where it is decrypted using the user's lock screen from their previous
+ * device.
+ *
+ * @hide
+ */
+public final class KeyChainSnapshot implements Parcelable {
+    private static final int DEFAULT_MAX_ATTEMPTS = 10;
+    private static final long DEFAULT_COUNTER_ID = 1L;
+
+    private int mSnapshotVersion;
+    private int mMaxAttempts = DEFAULT_MAX_ATTEMPTS;
+    private long mCounterId = DEFAULT_COUNTER_ID;
+    private byte[] mServerParams;
+    private byte[] mPublicKey;
+    private List<KeyChainProtectionParams> mKeyChainProtectionParams;
+    private List<WrappedApplicationKey> mEntryRecoveryData;
+    private byte[] mEncryptedRecoveryKeyBlob;
+
+    /**
+     * @hide
+     * Deprecated, consider using builder.
+     */
+    public KeyChainSnapshot(
+            int snapshotVersion,
+            @NonNull List<KeyChainProtectionParams> keyChainProtectionParams,
+            @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
+            @NonNull byte[] encryptedRecoveryKeyBlob) {
+        mSnapshotVersion = snapshotVersion;
+        mKeyChainProtectionParams =
+                Preconditions.checkCollectionElementsNotNull(keyChainProtectionParams,
+                        "KeyChainProtectionParams");
+        mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
+                "wrappedApplicationKeys");
+        mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
+    }
+
+    private KeyChainSnapshot() {
+
+    }
+
+    /**
+     * Snapshot version for given account. It is incremented when user secret or list of application
+     * keys changes.
+     */
+    public int getSnapshotVersion() {
+        return mSnapshotVersion;
+    }
+
+    /**
+     * Number of user secret guesses allowed during Keychain recovery.
+     */
+    public int getMaxAttempts() {
+        return mMaxAttempts;
+    }
+
+    /**
+     * CounterId which is rotated together with user secret.
+     */
+    public long getCounterId() {
+        return mCounterId;
+    }
+
+    /**
+     * Server parameters.
+     */
+    public @NonNull byte[] getServerParams() {
+        return mServerParams;
+    }
+
+    /**
+     * Public key used to encrypt {@code encryptedRecoveryKeyBlob}.
+     *
+     * See implementation for binary key format
+     */
+    // TODO: document key format.
+    public @NonNull byte[] getTrustedHardwarePublicKey() {
+        return mPublicKey;
+    }
+
+    /**
+     * UI and key derivation parameters. Note that combination of secrets may be used.
+     */
+    public @NonNull List<KeyChainProtectionParams> getKeyChainProtectionParams() {
+        return mKeyChainProtectionParams;
+    }
+
+    /**
+     * List of application keys, with key material encrypted by
+     * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
+     */
+    public @NonNull List<WrappedApplicationKey> getWrappedApplicationKeys() {
+        return mEntryRecoveryData;
+    }
+
+    /**
+     * Recovery key blob, encrypted by user secret and recovery service public key.
+     */
+    public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
+        return mEncryptedRecoveryKeyBlob;
+    }
+
+    public static final Creator<KeyChainSnapshot> CREATOR =
+            new Creator<KeyChainSnapshot>() {
+        public KeyChainSnapshot createFromParcel(Parcel in) {
+            return new KeyChainSnapshot(in);
+        }
+
+        public KeyChainSnapshot[] newArray(int length) {
+            return new KeyChainSnapshot[length];
+        }
+    };
+
+    /**
+     * Builder for creating {@link KeyChainSnapshot}.
+     */
+    public static class Builder {
+        private KeyChainSnapshot
+                mInstance = new KeyChainSnapshot();
+
+        /**
+         * Snapshot version for given account.
+         *
+         * @param snapshotVersion The snapshot version
+         * @return This builder.
+         */
+        public Builder setSnapshotVersion(int snapshotVersion) {
+            mInstance.mSnapshotVersion = snapshotVersion;
+            return this;
+        }
+
+        /**
+         * Sets the number of user secret guesses allowed during Keychain recovery.
+         *
+         * @param maxAttempts The maximum number of guesses.
+         * @return This builder.
+         */
+        public Builder setMaxAttempts(int maxAttempts) {
+            mInstance.mMaxAttempts = maxAttempts;
+            return this;
+        }
+
+        /**
+         * Sets counter id.
+         *
+         * @param counterId The counter id.
+         * @return This builder.
+         */
+        public Builder setCounterId(long counterId) {
+            mInstance.mCounterId = counterId;
+            return this;
+        }
+
+        /**
+         * Sets server parameters.
+         *
+         * @param serverParams The server parameters
+         * @return This builder.
+         */
+        public Builder setServerParams(byte[] serverParams) {
+            mInstance.mServerParams = serverParams;
+            return this;
+        }
+
+        /**
+         * Sets public key used to encrypt recovery blob.
+         *
+         * @param publicKey The public key
+         * @return This builder.
+         */
+        public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
+            mInstance.mPublicKey = publicKey;
+            return this;
+        }
+
+        /**
+         * Sets UI and key derivation parameters
+         *
+         * @param recoveryMetadata The UI and key derivation parameters
+         * @return This builder.
+         */
+        public Builder setKeyChainProtectionParams(
+                @NonNull List<KeyChainProtectionParams> recoveryMetadata) {
+            mInstance.mKeyChainProtectionParams = recoveryMetadata;
+            return this;
+        }
+
+        /**
+         * List of application keys.
+         *
+         * @param entryRecoveryData List of application keys
+         * @return This builder.
+         */
+        public Builder setWrappedApplicationKeys(List<WrappedApplicationKey> entryRecoveryData) {
+            mInstance.mEntryRecoveryData = entryRecoveryData;
+            return this;
+        }
+
+        /**
+         * Sets recovery key blob
+         *
+         * @param encryptedRecoveryKeyBlob The recovery key blob.
+         * @return This builder.
+         */
+        public Builder setEncryptedRecoveryKeyBlob(@NonNull byte[] encryptedRecoveryKeyBlob) {
+            mInstance.mEncryptedRecoveryKeyBlob = encryptedRecoveryKeyBlob;
+            return this;
+        }
+
+
+        /**
+         * Creates a new {@link KeyChainSnapshot} instance.
+         *
+         * @return new instance
+         * @throws NullPointerException if some required fields were not set.
+         */
+        @NonNull public KeyChainSnapshot build() {
+            Preconditions.checkCollectionElementsNotNull(mInstance.mKeyChainProtectionParams,
+                    "recoveryMetadata");
+            Preconditions.checkCollectionElementsNotNull(mInstance.mEntryRecoveryData,
+                    "entryRecoveryData");
+            Preconditions.checkNotNull(mInstance.mEncryptedRecoveryKeyBlob);
+            Preconditions.checkNotNull(mInstance.mServerParams);
+            Preconditions.checkNotNull(mInstance.mPublicKey);
+            return mInstance;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mSnapshotVersion);
+        out.writeTypedList(mKeyChainProtectionParams);
+        out.writeByteArray(mEncryptedRecoveryKeyBlob);
+        out.writeTypedList(mEntryRecoveryData);
+        out.writeInt(mMaxAttempts);
+        out.writeLong(mCounterId);
+        out.writeByteArray(mServerParams);
+        out.writeByteArray(mPublicKey);
+    }
+
+    /**
+     * @hide
+     */
+    protected KeyChainSnapshot(Parcel in) {
+        mSnapshotVersion = in.readInt();
+        mKeyChainProtectionParams = in.createTypedArrayList(KeyChainProtectionParams.CREATOR);
+        mEncryptedRecoveryKeyBlob = in.createByteArray();
+        mEntryRecoveryData = in.createTypedArrayList(WrappedApplicationKey.CREATOR);
+        mMaxAttempts = in.readInt();
+        mCounterId = in.readLong();
+        mServerParams = in.createByteArray();
+        mPublicKey = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/keystore/KeyDerivationParams.aidl b/core/java/android/security/keystore/recovery/KeyDerivationParams.aidl
similarity index 93%
rename from core/java/android/security/keystore/KeyDerivationParams.aidl
rename to core/java/android/security/keystore/recovery/KeyDerivationParams.aidl
index f39aa04..2b1bbbe 100644
--- a/core/java/android/security/keystore/KeyDerivationParams.aidl
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.security.keystore.recovery;
 
 /* @hide */
 parcelable KeyDerivationParams;
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
new file mode 100644
index 0000000..20631b0
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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.security.keystore.recovery;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Collection of parameters which define a key derivation function.
+ * Currently only supports salted SHA-256
+ *
+ * @hide
+ */
+public final class KeyDerivationParams implements Parcelable {
+    private final int mAlgorithm;
+    private byte[] mSalt;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ALGORITHM_"}, value = {ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+    public @interface KeyDerivationAlgorithm {
+    }
+
+    /**
+     * Salted SHA256
+     */
+    public static final int ALGORITHM_SHA256 = 1;
+
+    /**
+     * Argon2ID
+     * @hide
+     */
+    // TODO: add Argon2ID support.
+    public static final int ALGORITHM_ARGON2ID = 2;
+
+    /**
+     * Creates instance of the class to to derive key using salted SHA256 hash.
+     */
+    public static KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
+        return new KeyDerivationParams(ALGORITHM_SHA256, salt);
+    }
+
+    // TODO: Make private once legacy API is removed
+    public KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+        mAlgorithm = algorithm;
+        mSalt = Preconditions.checkNotNull(salt);
+    }
+
+    /**
+     * Gets algorithm.
+     */
+    public @KeyDerivationAlgorithm int getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets salt.
+     */
+    public @NonNull byte[] getSalt() {
+        return mSalt;
+    }
+
+    public static final Parcelable.Creator<KeyDerivationParams> CREATOR =
+            new Parcelable.Creator<KeyDerivationParams>() {
+        public KeyDerivationParams createFromParcel(Parcel in) {
+                return new KeyDerivationParams(in);
+        }
+
+        public KeyDerivationParams[] newArray(int length) {
+            return new KeyDerivationParams[length];
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAlgorithm);
+        out.writeByteArray(mSalt);
+    }
+
+    /**
+     * @hide
+     */
+    protected KeyDerivationParams(Parcel in) {
+        mAlgorithm = in.readInt();
+        mSalt = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/LockScreenRequiredException.java b/core/java/android/security/keystore/recovery/LockScreenRequiredException.java
new file mode 100644
index 0000000..acf893b
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/LockScreenRequiredException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * Error thrown when trying to generate keys for a profile that has no lock screen set.
+ *
+ * <p>A lock screen must be set, as the lock screen is used to encrypt the snapshot.
+ *
+ * @hide
+ */
+public class LockScreenRequiredException extends GeneralSecurityException {
+    public LockScreenRequiredException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/RecoveryClaim.java b/core/java/android/security/keystore/recovery/RecoveryClaim.java
new file mode 100644
index 0000000..11385d8
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryClaim.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+/**
+ * An attempt to recover a keychain protected by remote secure hardware.
+ *
+ * @hide
+ */
+public class RecoveryClaim {
+
+    private final RecoverySession mRecoverySession;
+    private final byte[] mClaimBytes;
+
+    RecoveryClaim(RecoverySession recoverySession, byte[] claimBytes) {
+        mRecoverySession = recoverySession;
+        mClaimBytes = claimBytes;
+    }
+
+    /**
+     * Returns the session associated with the recovery attempt. This is used to match the symmetric
+     * key, which remains internal to the framework, for decrypting the claim response.
+     *
+     * @return The session data.
+     */
+    public RecoverySession getRecoverySession() {
+        return mRecoverySession;
+    }
+
+    /**
+     * Returns the encrypted claim's bytes.
+     *
+     * <p>This should be sent by the recovery agent to the remote secure hardware, which will use
+     * it to decrypt the keychain, before sending it re-encrypted with the session's symmetric key
+     * to the device.
+     */
+    public byte[] getClaimBytes() {
+        return mClaimBytes;
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
new file mode 100644
index 0000000..2087317
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2017 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.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+
+import com.android.internal.widget.ILockSettings;
+
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
+ * other Android devices belonging to the user. The exported keychain is protected by the user's
+ * lock screen.
+ *
+ * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
+ * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
+ * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
+ * After  that number of incorrect guesses, the trusted hardware no longer allows access to the
+ * key chain.
+ *
+ * <p>For now only the recovery agent itself is able to create keys, so it is expected that the
+ * recovery agent is itself the system app.
+ *
+ * <p>A recovery agent requires the privileged permission
+ * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
+ *
+ * @hide
+ */
+public class RecoveryController {
+    private static final String TAG = "RecoveryController";
+
+    /** Key has been successfully synced. */
+    public static final int RECOVERY_STATUS_SYNCED = 0;
+    /** Waiting for recovery agent to sync the key. */
+    public static final int RECOVERY_STATUS_SYNC_IN_PROGRESS = 1;
+    /** Recovery account is not available. */
+    public static final int RECOVERY_STATUS_MISSING_ACCOUNT = 2;
+    /** Key cannot be synced. */
+    public static final int RECOVERY_STATUS_PERMANENT_FAILURE = 3;
+
+    /**
+     * Failed because no snapshot is yet pending to be synced for the user.
+     *
+     * @hide
+     */
+    public static final int ERROR_NO_SNAPSHOT_PENDING = 21;
+
+    /**
+     * Failed due to an error internal to the recovery service. This is unexpected and indicates
+     * either a problem with the logic in the service, or a problem with a dependency of the
+     * service (such as AndroidKeyStore).
+     *
+     * @hide
+     */
+    public static final int ERROR_SERVICE_INTERNAL_ERROR = 22;
+
+    /**
+     * Failed because the user does not have a lock screen set.
+     *
+     * @hide
+     */
+    public static final int ERROR_INSECURE_USER = 23;
+
+    /**
+     * Error thrown when attempting to use a recovery session that has since been closed.
+     *
+     * @hide
+     */
+    public static final int ERROR_SESSION_EXPIRED = 24;
+
+    /**
+     * Failed because the provided certificate was not a valid X509 certificate.
+     *
+     * @hide
+     */
+    public static final int ERROR_BAD_CERTIFICATE_FORMAT = 25;
+
+    /**
+     * Error thrown if decryption failed. This might be because the tag is wrong, the key is wrong,
+     * the data has become corrupted, the data has been tampered with, etc.
+     *
+     * @hide
+     */
+    public static final int ERROR_DECRYPTION_FAILED = 26;
+
+
+    private final ILockSettings mBinder;
+
+    private RecoveryController(ILockSettings binder) {
+        mBinder = binder;
+    }
+
+    /**
+     * Internal method used by {@code RecoverySession}.
+     *
+     * @hide
+     */
+    ILockSettings getBinder() {
+        return mBinder;
+    }
+
+    /**
+     * Gets a new instance of the class.
+     */
+    public static RecoveryController getInstance(Context context) {
+        ILockSettings lockSettings =
+                ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
+        return new RecoveryController(lockSettings);
+    }
+
+    /**
+     * Initializes key recovery service for the calling application. RecoveryController
+     * randomly chooses one of the keys from the list and keeps it to use for future key export
+     * operations. Collection of all keys in the list must be signed by the provided {@code
+     * rootCertificateAlias}, which must also be present in the list of root certificates
+     * preinstalled on the device. The random selection allows RecoveryController to select
+     * which of a set of remote recovery service devices will be used.
+     *
+     * <p>In addition, RecoveryController enforces a delay of three months between
+     * consecutive initialization attempts, to limit the ability of an attacker to often switch
+     * remote recovery devices and significantly increase number of recovery attempts.
+     *
+     * @param rootCertificateAlias alias of a root certificate preinstalled on the device
+     * @param signedPublicKeyList binary blob a list of X509 certificates and signature
+     * @throws CertificateException if the {@code signedPublicKeyList} is in a bad format.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void initRecoveryService(
+            @NonNull String rootCertificateAlias, @NonNull byte[] signedPublicKeyList)
+            throws CertificateException, InternalRecoveryServiceException {
+        try {
+            mBinder.initRecoveryService(rootCertificateAlias, signedPublicKeyList);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_BAD_CERTIFICATE_FORMAT) {
+                throw new CertificateException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Returns data necessary to store all recoverable keys. Key material is
+     * encrypted with user secret and recovery public key.
+     *
+     * @return Data necessary to recover keystore.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public @NonNull KeyChainSnapshot getRecoveryData()
+            throws InternalRecoveryServiceException {
+        try {
+            return mBinder.getRecoveryData(/*account=*/ new byte[]{});
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_NO_SNAPSHOT_PENDING) {
+                return null;
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Sets a listener which notifies recovery agent that new recovery snapshot is available. {@link
+     * #getRecoveryData} can be used to get the snapshot. Note that every recovery agent can have at
+     * most one registered listener at any time.
+     *
+     * @param intent triggered when new snapshot is available. Unregisters listener if the value is
+     *     {@code null}.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void setSnapshotCreatedPendingIntent(@Nullable PendingIntent intent)
+            throws InternalRecoveryServiceException {
+        try {
+            mBinder.setSnapshotCreatedPendingIntent(intent);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Server parameters used to generate new recovery key blobs. This value will be included in
+     * {@code KeyChainSnapshot.getEncryptedRecoveryKeyBlob()}. The same value must be included
+     * in vaultParams {@link #startRecoverySession}
+     *
+     * @param serverParams included in recovery key blob.
+     * @see #getRecoveryData
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void setServerParams(byte[] serverParams) throws InternalRecoveryServiceException {
+        try {
+            mBinder.setServerParams(serverParams);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Gets aliases of recoverable keys for the application.
+     * @param packageName which recoverable keys' aliases will be returned.
+     *
+     * @return {@code List} of all aliases.
+     */
+    public List<String> getAliases(@Nullable String packageName)
+            throws RemoteException, InternalRecoveryServiceException {
+        try {
+            // TODO: update aidl
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            return new ArrayList<>(allStatuses.keySet());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Updates recovery status for given key. It is used to notify keystore that key was
+     * successfully stored on the server or there were an error. Application can check this value
+     * using {@code getRecoveyStatus}.
+     *
+     * @param packageName Application whose recoverable key's status are to be updated.
+     * @param alias Application-specific key alias.
+     * @param status Status specific to recovery agent.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public void setRecoveryStatus(
+            @NonNull String packageName, String alias, int status)
+            throws NameNotFoundException, InternalRecoveryServiceException {
+        try {
+            // TODO: update aidl
+            String[] aliases = alias == null ? null : new String[]{alias};
+            mBinder.setRecoveryStatus(packageName, aliases, status);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Returns recovery status for Application's KeyStore key.
+     * Negative status values are reserved for recovery agent specific codes. List of common codes:
+     *
+     * <ul>
+     *   <li>{@link #RECOVERY_STATUS_SYNCED}
+     *   <li>{@link #RECOVERY_STATUS_SYNC_IN_PROGRESS}
+     *   <li>{@link #RECOVERY_STATUS_MISSING_ACCOUNT}
+     *   <li>{@link #RECOVERY_STATUS_PERMANENT_FAILURE}
+     * </ul>
+     *
+     * @param packageName Application whose recoverable key status is returned.
+     * @param alias Application-specific key alias.
+     * @return Recovery status.
+     * @see #setRecoveryStatus
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    public int getRecoveryStatus(String packageName, String alias)
+            throws InternalRecoveryServiceException {
+        try {
+            // TODO: update aidl
+            Map<String, Integer> allStatuses = mBinder.getRecoveryStatus(packageName);
+            Integer status = allStatuses.get(alias);
+            if (status == null) {
+                return RecoveryController.RECOVERY_STATUS_PERMANENT_FAILURE;
+            } else {
+                return status;
+            }
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Specifies a set of secret types used for end-to-end keystore encryption. Knowing all of them
+     * is necessary to recover data.
+     *
+     * @param secretTypes {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} or {@link
+     *     KeyChainProtectionParams#TYPE_CUSTOM_PASSWORD}
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    public void setRecoverySecretTypes(
+            @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
+            throws InternalRecoveryServiceException {
+        try {
+            mBinder.setRecoverySecretTypes(secretTypes);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Defines a set of secret types used for end-to-end keystore encryption. Knowing all of them is
+     * necessary to generate KeyChainSnapshot.
+     *
+     * @return list of recovery secret types
+     * @see KeyChainSnapshot
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    public @NonNull @KeyChainProtectionParams.UserSecretType int[] getRecoverySecretTypes()
+            throws InternalRecoveryServiceException {
+        try {
+            return mBinder.getRecoverySecretTypes();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
+     * When user enters a secret of a pending type {@link #recoverySecretAvailable} should be
+     * called.
+     *
+     * @return list of recovery secret types
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @NonNull
+    public @KeyChainProtectionParams.UserSecretType int[] getPendingRecoverySecretTypes()
+            throws InternalRecoveryServiceException {
+        try {
+            return mBinder.getPendingRecoverySecretTypes();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Method notifies KeyStore that a user-generated secret is available. This method generates a
+     * symmetric session key which a trusted remote device can use to return a recovery key. Caller
+     * should use {@link KeyChainProtectionParams#clearSecret} to override the secret value in
+     * memory.
+     *
+     * @param recoverySecret user generated secret together with parameters necessary to regenerate
+     *     it on a new device.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
+            throws InternalRecoveryServiceException {
+        try {
+            mBinder.recoverySecretAvailable(recoverySecret);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Generates a key called {@code alias} and loads it into the recoverable key store. Returns the
+     * raw material of the key.
+     *
+     * @param alias The key alias.
+     * @param account The account associated with the key
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     * @throws LockScreenRequiredException if the user has not set a lock screen. This is required
+     *     to generate recoverable keys, as the snapshots are encrypted using a key derived from the
+     *     lock screen.
+     */
+    public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        try {
+            // TODO: add account
+            return mBinder.generateAndStoreKey(alias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == ERROR_INSECURE_USER) {
+                throw new LockScreenRequiredException(e.getMessage());
+            }
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Removes a key called {@code alias} from the recoverable key store.
+     *
+     * @param alias The key alias.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    public void removeKey(@NonNull String alias) throws InternalRecoveryServiceException {
+        try {
+            mBinder.removeKey(alias);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            throw wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    InternalRecoveryServiceException wrapUnexpectedServiceSpecificException(
+            ServiceSpecificException e) {
+        if (e.errorCode == ERROR_SERVICE_INTERNAL_ERROR) {
+            return new InternalRecoveryServiceException(e.getMessage());
+        }
+
+        // Should never happen. If it does, it's a bug, and we need to update how the method that
+        // called this throws its exceptions.
+        return new InternalRecoveryServiceException("Unexpected error code for method: "
+                + e.errorCode, e);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/RecoveryControllerException.java b/core/java/android/security/keystore/recovery/RecoveryControllerException.java
new file mode 100644
index 0000000..0fb7c07
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoveryControllerException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import java.security.GeneralSecurityException;
+
+/**
+ * Base exception for errors thrown by {@link RecoveryController}.
+ *
+ * @hide
+ */
+public abstract class RecoveryControllerException extends GeneralSecurityException {
+    RecoveryControllerException() { }
+
+    RecoveryControllerException(String msg) {
+        super(msg);
+    }
+
+    public RecoveryControllerException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
new file mode 100644
index 0000000..11bea96
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.util.Log;
+
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Session to recover a {@link KeyChainSnapshot} from the remote trusted hardware, initiated by a
+ * recovery agent.
+ *
+ * @hide
+ */
+public class RecoverySession implements AutoCloseable {
+    private static final String TAG = "RecoverySession";
+
+    private static final int SESSION_ID_LENGTH_BYTES = 16;
+
+    private final String mSessionId;
+    private final RecoveryController mRecoveryController;
+
+    private RecoverySession(RecoveryController recoveryController, String sessionId) {
+        mRecoveryController = recoveryController;
+        mSessionId = sessionId;
+    }
+
+    /**
+     * A new session, started by {@code recoveryManager}.
+     */
+    static RecoverySession newInstance(RecoveryController recoveryController) {
+        return new RecoverySession(recoveryController, newSessionId());
+    }
+
+    /**
+     * Returns a new random session ID.
+     */
+    private static String newSessionId() {
+        SecureRandom secureRandom = new SecureRandom();
+        byte[] sessionId = new byte[SESSION_ID_LENGTH_BYTES];
+        secureRandom.nextBytes(sessionId);
+        StringBuilder sb = new StringBuilder();
+        for (byte b : sessionId) {
+            sb.append(Byte.toHexString(b, /*upperCase=*/ false));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Starts a recovery session and returns a blob with proof of recovery secret possession.
+     * The method generates a symmetric key for a session, which trusted remote device can use to
+     * return recovery key.
+     *
+     * @param verifierPublicKey Encoded {@code java.security.cert.X509Certificate} with Public key
+     *     used to create the recovery blob on the source device.
+     *     Keystore will verify the certificate using root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     *     Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     *     replay attacks
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
+     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+     *     key and parameters necessary to identify the counter with the number of failed recovery
+     *     attempts.
+     * @throws CertificateException if the {@code verifierPublicKey} is in an incorrect
+     *     format.
+     * @throws InternalRecoveryServiceException if an unexpected error occurred in the recovery
+     *     service.
+     */
+    @NonNull public byte[] start(
+            @NonNull byte[] verifierPublicKey,
+            @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge,
+            @NonNull List<KeyChainProtectionParams> secrets)
+            throws CertificateException, InternalRecoveryServiceException {
+        try {
+            byte[] recoveryClaim =
+                    mRecoveryController.getBinder().startRecoverySession(
+                            mSessionId,
+                            verifierPublicKey,
+                            vaultParams,
+                            vaultChallenge,
+                            secrets);
+            return recoveryClaim;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_BAD_CERTIFICATE_FORMAT) {
+                throw new CertificateException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * Imports keys.
+     *
+     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+     *     and session. KeyStore only uses package names from the application info in {@link
+     *     WrappedApplicationKey}. Caller is responsibility to perform certificates check.
+     * @return Map from alias to raw key material.
+     * @throws SessionExpiredException if {@code session} has since been closed.
+     * @throws DecryptionFailedException if unable to decrypt the snapshot.
+     * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
+     */
+    public Map<String, byte[]> recoverKeys(
+            @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<WrappedApplicationKey> applicationKeys)
+            throws SessionExpiredException, DecryptionFailedException,
+            InternalRecoveryServiceException {
+        try {
+            return (Map<String, byte[]>) mRecoveryController.getBinder().recoverKeys(
+                    mSessionId, recoveryKeyBlob, applicationKeys);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        } catch (ServiceSpecificException e) {
+            if (e.errorCode == RecoveryController.ERROR_DECRYPTION_FAILED) {
+                throw new DecryptionFailedException(e.getMessage());
+            }
+            if (e.errorCode == RecoveryController.ERROR_SESSION_EXPIRED) {
+                throw new SessionExpiredException(e.getMessage());
+            }
+            throw mRecoveryController.wrapUnexpectedServiceSpecificException(e);
+        }
+    }
+
+    /**
+     * An internal session ID, used by the framework to match recovery claims to snapshot responses.
+     *
+     * @hide
+     */
+    String getSessionId() {
+        return mSessionId;
+    }
+
+    /**
+     * Deletes all data associated with {@code session}. Should not be invoked directly but via
+     * {@link RecoverySession#close()}.
+     *
+     * @hide
+     */
+    @Override
+    public void close() {
+        try {
+            mRecoveryController.getBinder().closeSession(mSessionId);
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.e(TAG, "Unexpected error trying to close session", e);
+        }
+    }
+}
diff --git a/core/java/android/security/keystore/recovery/SessionExpiredException.java b/core/java/android/security/keystore/recovery/SessionExpiredException.java
new file mode 100644
index 0000000..abee62e
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/SessionExpiredException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.security.keystore.recovery;
+
+import java.security.GeneralSecurityException;
+
+
+/**
+ * Error thrown when attempting to use a {@link RecoverySession} that has since expired.
+ *
+ * @hide
+ */
+public class SessionExpiredException extends GeneralSecurityException {
+    public SessionExpiredException(String msg) {
+        super(msg);
+    }
+}
diff --git a/core/java/android/security/keystore/WrappedApplicationKey.aidl b/core/java/android/security/keystore/recovery/WrappedApplicationKey.aidl
similarity index 93%
rename from core/java/android/security/keystore/WrappedApplicationKey.aidl
rename to core/java/android/security/keystore/recovery/WrappedApplicationKey.aidl
index a6294fe..b2d1ae4 100644
--- a/core/java/android/security/keystore/WrappedApplicationKey.aidl
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.aidl
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.security.keystore.recovery;
 
 /* @hide */
 parcelable WrappedApplicationKey;
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
new file mode 100644
index 0000000..2719137
--- /dev/null
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2017 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.security.keystore.recovery;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Helper class with data necessary recover a single application key, given a recovery key.
+ *
+ * <ul>
+ *   <li>Alias - Keystore alias of the key.
+ *   <li>Account Recovery Agent specific account associated with the key.
+ *   <li>Encrypted key material.
+ * </ul>
+ *
+ * Note that Application info is not included. Recovery Agent can only make its own keys
+ * recoverable.
+ *
+ * @hide
+ */
+public final class WrappedApplicationKey implements Parcelable {
+    private String mAlias;
+    // The only supported format is AES-256 symmetric key.
+    private byte[] mEncryptedKeyMaterial;
+    private byte[] mAccount;
+
+    /**
+     * Builder for creating {@link WrappedApplicationKey}.
+     */
+    public static class Builder {
+        private WrappedApplicationKey mInstance = new WrappedApplicationKey();
+
+        /**
+         * Sets Application-specific alias of the key.
+         *
+         * @param alias The alias.
+         * @return This builder.
+         */
+        public Builder setAlias(@NonNull String alias) {
+            mInstance.mAlias = alias;
+            return this;
+        }
+
+        /**
+         * Sets Recovery agent specific account.
+         *
+         * @param account The account.
+         * @return This builder.
+         */
+        public Builder setAccount(@NonNull byte[] account) {
+            mInstance.mAccount = account;
+            return this;
+        }
+
+        /**
+         * Sets key material encrypted by recovery key.
+         *
+         * @param encryptedKeyMaterial The key material
+         * @return This builder
+         */
+
+        public Builder setEncryptedKeyMaterial(@NonNull byte[] encryptedKeyMaterial) {
+            mInstance.mEncryptedKeyMaterial = encryptedKeyMaterial;
+            return this;
+        }
+
+        /**
+         * Creates a new {@link WrappedApplicationKey} instance.
+         *
+         * @return new instance
+         * @throws NullPointerException if some required fields were not set.
+         */
+        @NonNull public WrappedApplicationKey build() {
+            Preconditions.checkNotNull(mInstance.mAlias);
+            Preconditions.checkNotNull(mInstance.mEncryptedKeyMaterial);
+            if (mInstance.mAccount == null) {
+                mInstance.mAccount = new byte[]{};
+            }
+            return mInstance;
+        }
+    }
+
+    private WrappedApplicationKey() {
+    }
+
+    /**
+     * Deprecated - consider using Builder.
+     * @hide
+     */
+    public WrappedApplicationKey(@NonNull String alias, @NonNull byte[] encryptedKeyMaterial) {
+        mAlias = Preconditions.checkNotNull(alias);
+        mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
+    }
+
+    /**
+     * Application-specific alias of the key.
+     *
+     * @see java.security.KeyStore.aliases
+     */
+    public @NonNull String getAlias() {
+        return mAlias;
+    }
+
+    /** Key material encrypted by recovery key. */
+    public @NonNull byte[] getEncryptedKeyMaterial() {
+        return mEncryptedKeyMaterial;
+    }
+
+    /** Account, default value is empty array */
+    public @NonNull byte[] getAccount() {
+        if (mAccount == null) {
+            return new byte[]{};
+        }
+        return mAccount;
+    }
+
+    public static final Parcelable.Creator<WrappedApplicationKey> CREATOR =
+            new Parcelable.Creator<WrappedApplicationKey>() {
+                public WrappedApplicationKey createFromParcel(Parcel in) {
+                    return new WrappedApplicationKey(in);
+                }
+
+                public WrappedApplicationKey[] newArray(int length) {
+                    return new WrappedApplicationKey[length];
+                }
+            };
+
+    /**
+     * @hide
+     */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mAlias);
+        out.writeByteArray(mEncryptedKeyMaterial);
+        out.writeByteArray(mAccount);
+    }
+
+    /**
+     * @hide
+     */
+    protected WrappedApplicationKey(Parcel in) {
+        mAlias = in.readString();
+        mEncryptedKeyMaterial = in.createByteArray();
+        mAccount = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index df63a91..1ef6100 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -172,6 +172,7 @@
      * {@hide}
      */
     public static final class Scores implements Parcelable {
+        @NonNull
         public final float[][] scores;
 
         private Scores(Parcel parcel) {
@@ -185,11 +186,23 @@
             }
         }
 
-        private Scores(float[][] scores) {
+        private Scores(@NonNull float[][] scores) {
             this.scores = scores;
         }
 
         @Override
+        public String toString() {
+            final int size1 = scores.length;
+            final int size2 = size1 > 0 ? scores[0].length : 0;
+            final StringBuilder builder = new StringBuilder("Scores [")
+                    .append(size1).append("x").append(size2).append("] ");
+            for (int i = 0; i < size1; i++) {
+                builder.append(i).append(": ").append(Arrays.toString(scores[i])).append(' ');
+            }
+            return builder.toString();
+        }
+
+        @Override
         public int describeContents() {
             return 0;
         }
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/core/java/android/service/notification/NotifyingApp.aidl
similarity index 73%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to core/java/android/service/notification/NotifyingApp.aidl
index b35713f..5358c2f 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/core/java/android/service/notification/NotifyingApp.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
+/**
+ * Copyright (c) 2018, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.service.notification;
 
-/* @hide */
-parcelable KeychainSnapshot;
+parcelable NotifyingApp;
\ No newline at end of file
diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java
new file mode 100644
index 0000000..38f18c6
--- /dev/null
+++ b/core/java/android/service/notification/NotifyingApp.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> {
+
+    private int mUid;
+    private String mPkg;
+    private long mLastNotified;
+
+    public NotifyingApp() {}
+
+    protected NotifyingApp(Parcel in) {
+        mUid = in.readInt();
+        mPkg = in.readString();
+        mLastNotified = in.readLong();
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Sets the uid of the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setUid(int mUid) {
+        this.mUid = mUid;
+        return this;
+    }
+
+    public String getPackage() {
+        return mPkg;
+    }
+
+    /**
+     * Sets the package that sent the notification. Returns self.
+     */
+    public NotifyingApp setPackage(@NonNull String mPkg) {
+        this.mPkg = mPkg;
+        return this;
+    }
+
+    public long getLastNotified() {
+        return mLastNotified;
+    }
+
+    /**
+     * Sets the time the notification was originally sent. Returns self.
+     */
+    public NotifyingApp setLastNotified(long mLastNotified) {
+        this.mLastNotified = mLastNotified;
+        return this;
+    }
+
+    public static final Creator<NotifyingApp> CREATOR = new Creator<NotifyingApp>() {
+        @Override
+        public NotifyingApp createFromParcel(Parcel in) {
+            return new NotifyingApp(in);
+        }
+
+        @Override
+        public NotifyingApp[] newArray(int size) {
+            return new NotifyingApp[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mUid);
+        dest.writeString(mPkg);
+        dest.writeLong(mLastNotified);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        NotifyingApp that = (NotifyingApp) o;
+        return getUid() == that.getUid()
+                && getLastNotified() == that.getLastNotified()
+                && Objects.equals(mPkg, that.mPkg);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(getUid(), mPkg, getLastNotified());
+    }
+
+    /**
+     * Sorts notifying apps from newest last notified date to oldest.
+     */
+    @Override
+    public int compareTo(NotifyingApp o) {
+        if (getLastNotified() == o.getLastNotified()) {
+            if (getUid() == o.getUid()) {
+                return getPackage().compareTo(o.getPackage());
+            }
+            return Integer.compare(getUid(), o.getUid());
+        }
+
+        return -Long.compare(getLastNotified(), o.getLastNotified());
+    }
+
+    @Override
+    public String toString() {
+        return "NotifyingApp{"
+                + "mUid=" + mUid
+                + ", mPkg='" + mPkg + '\''
+                + ", mLastNotified=" + mLastNotified
+                + '}';
+    }
+}
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 43dd0ff..70175c8 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -16,6 +16,11 @@
 
 package android.text.style;
 
+import android.annotation.ColorInt;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.Px;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Path;
@@ -26,38 +31,108 @@
 import android.text.Spanned;
 import android.text.TextUtils;
 
+/**
+ * A span which styles paragraphs as bullet points (respecting layout direction).
+ * <p>
+ * BulletSpans must be attached from the first character to the last character of a single
+ * paragraph, otherwise the bullet point will not be displayed but the first paragraph encountered
+ * will have a leading margin.
+ * <p>
+ * BulletSpans allow configuring the following elements:
+ * <ul>
+ * <li><b>gap width</b> - the distance, in pixels, between the bullet point and the paragraph.
+ * Default value is 2px.</li>
+ * <li><b>color</b> - the bullet point color. By default, the bullet point color is 0 - no color,
+ * so it uses the TextView's text color.</li>
+ * <li><b>bullet radius</b> - the radius, in pixels, of the bullet point. Default value is
+ * 4px.</li>
+ * </ul>
+ * For example, a BulletSpan using the default values can be constructed like this:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/defaultbulletspan.png" />
+ * <figcaption>BulletSpan constructed with default values.</figcaption>
+ * <p>
+ * <p>
+ * To construct a BulletSpan with a gap width of 40px, green bullet point and bullet radius of
+ * 20px:
+ * <pre>{@code
+ *  SpannableString string = new SpannableString("Text with\nBullet point");
+ *string.setSpan(new BulletSpan(40, color, 20), 10, 22, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);}</pre>
+ * <img src="{@docRoot}reference/android/images/text/style/custombulletspan.png" />
+ * <figcaption>Customized BulletSpan.</figcaption>
+ */
 public class BulletSpan implements LeadingMarginSpan, ParcelableSpan {
-    private final int mGapWidth;
-    private final boolean mWantColor;
-    private final int mColor;
-
     // Bullet is slightly bigger to avoid aliasing artifacts on mdpi devices.
-    private static final float BULLET_RADIUS = 3 * 1.2f;
-    private static Path sBulletPath = null;
+    private static final int STANDARD_BULLET_RADIUS = 4;
     public static final int STANDARD_GAP_WIDTH = 2;
+    private static final int STANDARD_COLOR = 0;
 
+    @Px
+    private final int mGapWidth;
+    @Px
+    private final int mBulletRadius;
+    private Path mBulletPath = null;
+    @ColorInt
+    private final int mColor;
+    private final boolean mWantColor;
+
+    /**
+     * Creates a {@link BulletSpan} with the default values.
+     */
     public BulletSpan() {
-        mGapWidth = STANDARD_GAP_WIDTH;
-        mWantColor = false;
-        mColor = 0;
+        this(STANDARD_GAP_WIDTH, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
+    /**
+     * Creates a {@link BulletSpan} based on a gap width
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     */
     public BulletSpan(int gapWidth) {
-        mGapWidth = gapWidth;
-        mWantColor = false;
-        mColor = 0;
+        this(gapWidth, STANDARD_COLOR, false, STANDARD_BULLET_RADIUS);
     }
 
-    public BulletSpan(int gapWidth, int color) {
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth the distance, in pixels, between the bullet point and the paragraph.
+     * @param color    the bullet point color, as a color integer
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color) {
+        this(gapWidth, color, true, STANDARD_BULLET_RADIUS);
+    }
+
+    /**
+     * Creates a {@link BulletSpan} based on a gap width and a color integer.
+     *
+     * @param gapWidth     the distance, in pixels, between the bullet point and the paragraph.
+     * @param color        the bullet point color, as a color integer.
+     * @param bulletRadius the radius of the bullet point, in pixels.
+     * @see android.content.res.Resources#getColor(int, Resources.Theme)
+     */
+    public BulletSpan(int gapWidth, @ColorInt int color, @IntRange(from = 0) int bulletRadius) {
+        this(gapWidth, color, true, bulletRadius);
+    }
+
+    private BulletSpan(int gapWidth, @ColorInt int color, boolean wantColor,
+            @IntRange(from = 0) int bulletRadius) {
         mGapWidth = gapWidth;
-        mWantColor = true;
+        mBulletRadius = bulletRadius;
         mColor = color;
+        mWantColor = wantColor;
     }
 
-    public BulletSpan(Parcel src) {
+    /**
+     * Creates a {@link BulletSpan} from a parcel.
+     */
+    public BulletSpan(@NonNull Parcel src) {
         mGapWidth = src.readInt();
         mWantColor = src.readInt() != 0;
         mColor = src.readInt();
+        mBulletRadius = src.readInt();
     }
 
     @Override
@@ -77,68 +152,97 @@
     }
 
     @Override
-    public void writeToParcel(Parcel dest, int flags) {
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
         writeToParcelInternal(dest, flags);
     }
 
     /** @hide */
     @Override
-    public void writeToParcelInternal(Parcel dest, int flags) {
+    public void writeToParcelInternal(@NonNull Parcel dest, int flags) {
         dest.writeInt(mGapWidth);
         dest.writeInt(mWantColor ? 1 : 0);
         dest.writeInt(mColor);
+        dest.writeInt(mBulletRadius);
     }
 
     @Override
     public int getLeadingMargin(boolean first) {
-        return (int) (2 * BULLET_RADIUS + mGapWidth);
+        return 2 * mBulletRadius + mGapWidth;
+    }
+
+    /**
+     * Get the distance, in pixels, between the bullet point and the paragraph.
+     *
+     * @return the distance, in pixels, between the bullet point and the paragraph.
+     */
+    public int getGapWidth() {
+        return mGapWidth;
+    }
+
+    /**
+     * Get the radius, in pixels, of the bullet point.
+     *
+     * @return the radius, in pixels, of the bullet point.
+     */
+    public int getBulletRadius() {
+        return mBulletRadius;
+    }
+
+    /**
+     * Get the bullet point color.
+     *
+     * @return the bullet point color
+     */
+    public int getColor() {
+        return mColor;
     }
 
     @Override
-    public void drawLeadingMargin(Canvas c, Paint p, int x, int dir,
-                                  int top, int baseline, int bottom,
-                                  CharSequence text, int start, int end,
-                                  boolean first, Layout l) {
+    public void drawLeadingMargin(@NonNull Canvas canvas, @NonNull Paint paint, int x, int dir,
+            int top, int baseline, int bottom,
+            @NonNull CharSequence text, int start, int end,
+            boolean first, @Nullable Layout layout) {
         if (((Spanned) text).getSpanStart(this) == start) {
-            Paint.Style style = p.getStyle();
+            Paint.Style style = paint.getStyle();
             int oldcolor = 0;
 
             if (mWantColor) {
-                oldcolor = p.getColor();
-                p.setColor(mColor);
+                oldcolor = paint.getColor();
+                paint.setColor(mColor);
             }
 
-            p.setStyle(Paint.Style.FILL);
+            paint.setStyle(Paint.Style.FILL);
 
-            if (l != null) {
+            if (layout != null) {
                 // "bottom" position might include extra space as a result of line spacing
                 // configuration. Subtract extra space in order to show bullet in the vertical
                 // center of characters.
-                final int line = l.getLineForOffset(start);
-                bottom = bottom - l.getLineExtra(line);
+                final int line = layout.getLineForOffset(start);
+                bottom = bottom - layout.getLineExtra(line);
             }
 
-            final float y = (top + bottom) / 2f;
+            final float yPosition = (top + bottom) / 2f;
+            final float xPosition = x + dir * mBulletRadius;
 
-            if (c.isHardwareAccelerated()) {
-                if (sBulletPath == null) {
-                    sBulletPath = new Path();
-                    sBulletPath.addCircle(0.0f, 0.0f, BULLET_RADIUS, Direction.CW);
+            if (canvas.isHardwareAccelerated()) {
+                if (mBulletPath == null) {
+                    mBulletPath = new Path();
+                    mBulletPath.addCircle(0.0f, 0.0f, mBulletRadius, Direction.CW);
                 }
 
-                c.save();
-                c.translate(x + dir * BULLET_RADIUS, y);
-                c.drawPath(sBulletPath, p);
-                c.restore();
+                canvas.save();
+                canvas.translate(xPosition, yPosition);
+                canvas.drawPath(mBulletPath, paint);
+                canvas.restore();
             } else {
-                c.drawCircle(x + dir * BULLET_RADIUS, y, BULLET_RADIUS, p);
+                canvas.drawCircle(xPosition, yPosition, mBulletRadius, paint);
             }
 
             if (mWantColor) {
-                p.setColor(oldcolor);
+                paint.setColor(oldcolor);
             }
 
-            p.setStyle(style);
+            paint.setStyle(style);
         }
     }
 }
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 4e98d9b..b313514 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -40,11 +40,12 @@
         DEFAULT_FLAGS.put("device_info_v2", "true");
         DEFAULT_FLAGS.put("settings_app_info_v2", "true");
         DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
-        DEFAULT_FLAGS.put("settings_battery_v2", "false");
+        DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_security_settings_v2", "true");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_suggestion_ui_v2", "false");
+        DEFAULT_FLAGS.put("settings_about_phone_v2", "false");
     }
 
     /**
diff --git a/core/java/android/util/PackageUtils.java b/core/java/android/util/PackageUtils.java
index e2e9d53..a5e3818 100644
--- a/core/java/android/util/PackageUtils.java
+++ b/core/java/android/util/PackageUtils.java
@@ -105,7 +105,7 @@
      * @param data The data.
      * @return The digest or null if an error occurs.
      */
-    public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+    public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) {
         MessageDigest messageDigest;
         try {
             messageDigest = MessageDigest.getInstance("SHA256");
@@ -116,6 +116,15 @@
 
         messageDigest.update(data);
 
-        return ByteStringUtils.toHexString(messageDigest.digest());
+        return messageDigest.digest();
+    }
+
+    /**
+     * Computes the SHA256 digest of some data.
+     * @param data The data.
+     * @return The digest or null if an error occurs.
+     */
+    public static @Nullable String computeSha256Digest(@NonNull byte[] data) {
+        return ByteStringUtils.toHexString(computeSha256DigestBytes(data));
     }
 }
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index a2a7616..8794372 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -80,10 +80,22 @@
                     ApkSignatureSchemeV3Verifier.verify(apkPath);
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3);
+            Signature[] pastSignerSigs = null;
+            int[] pastSignerSigsFlags = null;
+            if (vSigner.por != null) {
+                // populate proof-of-rotation information
+                pastSignerSigs = new Signature[vSigner.por.certs.size()];
+                pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+                for (int i = 0; i < pastSignerSigs.length; i++) {
+                    pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+                    pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+                }
+            }
+            return new PackageParser.SigningDetails(
+                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                    pastSignerSigs, pastSignerSigsFlags);
         } catch (SignatureNotFoundException e) {
-            // not signed with v2, try older if allowed
+            // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
@@ -92,7 +104,7 @@
             // APK Signature Scheme v2 signature found but did not verify
             throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v2", e);
+                            + " using APK Signature Scheme v3", e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
@@ -304,25 +316,37 @@
         }
 
         // first try v3
-        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifyV3");
+        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "certsOnlyV3");
         try {
             ApkSignatureSchemeV3Verifier.VerifiedSigner vSigner =
                     ApkSignatureSchemeV3Verifier.plsCertsNoVerifyOnlyCerts(apkPath);
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
-            return new PackageParser.SigningDetails(signerSigs,
-                    SignatureSchemeVersion.SIGNING_BLOCK_V3);
+            Signature[] pastSignerSigs = null;
+            int[] pastSignerSigsFlags = null;
+            if (vSigner.por != null) {
+                // populate proof-of-rotation information
+                pastSignerSigs = new Signature[vSigner.por.certs.size()];
+                pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
+                for (int i = 0; i < pastSignerSigs.length; i++) {
+                    pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
+                    pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+                }
+            }
+            return new PackageParser.SigningDetails(
+                    signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                    pastSignerSigs, pastSignerSigsFlags);
         } catch (SignatureNotFoundException e) {
-            // not signed with v2, try older if allowed
+            // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
                 throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                         "No APK Signature Scheme v3 signature in package " + apkPath, e);
             }
         } catch (Exception e) {
-            // APK Signature Scheme v2 signature found but did not verify
+            // APK Signature Scheme v3 signature found but did not verify
             throw new  PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                     "Failed to collect certificates from " + apkPath
-                            + " using APK Signature Scheme v2", e);
+                            + " using APK Signature Scheme v3", e);
         } finally {
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index ed2b8b6..a597405 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -806,8 +806,10 @@
     public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283;
     /** Key code constant: Show all apps */
     public static final int KEYCODE_ALL_APPS = 284;
+    /** Key code constant: Refresh key. */
+    public static final int KEYCODE_REFRESH = 285;
 
-    private static final int LAST_KEYCODE = KEYCODE_ALL_APPS;
+    private static final int LAST_KEYCODE = KEYCODE_REFRESH;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index e0864bd..4631261 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -57,6 +57,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.LayoutAnimationController;
 import android.view.animation.Transformation;
+import android.view.autofill.Helper;
 
 import com.android.internal.R;
 
@@ -3474,8 +3475,10 @@
         }
 
         if (!isLaidOut()) {
-            Log.v(VIEW_LOG_TAG, "dispatchProvideStructure(): not laid out, ignoring "
-                    + childrenCount + " children of " + getAccessibilityViewId());
+            if (Helper.sVerbose) {
+                Log.v(VIEW_LOG_TAG, "dispatchProvideStructure(): not laid out, ignoring "
+                        + childrenCount + " children of " + getAccessibilityViewId());
+            }
             return;
         }
 
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 176927f..95abea1 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2244,9 +2244,36 @@
      * <p>
      * The transitionName for the view background will be "android:navigation:background".
      * </p>
+     * @attr ref android.R.styleable#Window_navigationBarColor
      */
     public abstract void setNavigationBarColor(@ColorInt int color);
 
+    /**
+     * Shows a thin line of the specified color between the navigation bar and the app
+     * content.
+     * <p>
+     * For this to take effect,
+     * the window must be drawing the system bar backgrounds with
+     * {@link android.view.WindowManager.LayoutParams#FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS} and
+     * {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_NAVIGATION} must not be set.
+     *
+     * @param dividerColor The color of the thin line.
+     * @attr ref android.R.styleable#Window_navigationBarDividerColor
+     */
+    public void setNavigationBarDividerColor(@ColorInt int dividerColor) {
+    }
+
+    /**
+     * Retrieves the color of the navigation bar divider.
+     *
+     * @return The color of the navigation bar divider color.
+     * @see #setNavigationBarColor(int)
+     * @attr ref android.R.styleable#Window_navigationBarDividerColor
+     */
+    public @ColorInt int getNavigationBarDividerColor() {
+        return 0;
+    }
+
     /** @hide */
     public void setTheme(int resId) {
     }
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index f5c3613..990fbdb 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -156,6 +156,8 @@
                 anim = new RotateAnimation(c, attrs);
             }  else if (name.equals("translate")) {
                 anim = new TranslateAnimation(c, attrs);
+            } else if (name.equals("cliprect")) {
+                anim = new ClipRectAnimation(c, attrs);
             } else {
                 throw new RuntimeException("Unknown animation name: " + parser.getName());
             }
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
index e194927..21509d3 100644
--- a/core/java/android/view/animation/ClipRectAnimation.java
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -16,7 +16,11 @@
 
 package android.view.animation;
 
+import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
 
 /**
  * An animation that controls the clip of an object. See the
@@ -26,8 +30,84 @@
  * @hide
  */
 public class ClipRectAnimation extends Animation {
-    protected Rect mFromRect = new Rect();
-    protected Rect mToRect = new Rect();
+    protected final Rect mFromRect = new Rect();
+    protected final Rect mToRect = new Rect();
+
+    private int mFromLeftType = ABSOLUTE;
+    private int mFromTopType = ABSOLUTE;
+    private int mFromRightType = ABSOLUTE;
+    private int mFromBottomType = ABSOLUTE;
+
+    private int mToLeftType = ABSOLUTE;
+    private int mToTopType = ABSOLUTE;
+    private int mToRightType = ABSOLUTE;
+    private int mToBottomType = ABSOLUTE;
+
+    private float mFromLeftValue;
+    private float mFromTopValue;
+    private float mFromRightValue;
+    private float mFromBottomValue;
+
+    private float mToLeftValue;
+    private float mToTopValue;
+    private float mToRightValue;
+    private float mToBottomValue;
+
+    /**
+     * Constructor used when a ClipRectAnimation is loaded from a resource.
+     *
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public ClipRectAnimation(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.ClipRectAnimation);
+
+        Description d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_fromLeft));
+        mFromLeftType = d.type;
+        mFromLeftValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_fromTop));
+        mFromTopType = d.type;
+        mFromTopValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_fromRight));
+        mFromRightType = d.type;
+        mFromRightValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_fromBottom));
+        mFromBottomType = d.type;
+        mFromBottomValue = d.value;
+
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_toLeft));
+        mToLeftType = d.type;
+        mToLeftValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_toTop));
+        mToTopType = d.type;
+        mToTopValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_toRight));
+        mToRightType = d.type;
+        mToRightValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ClipRectAnimation_toBottom));
+        mToBottomType = d.type;
+        mToBottomValue = d.value;
+
+        a.recycle();
+    }
 
     /**
      * Constructor to use when building a ClipRectAnimation from code
@@ -39,8 +119,15 @@
         if (fromClip == null || toClip == null) {
             throw new RuntimeException("Expected non-null animation clip rects");
         }
-        mFromRect.set(fromClip);
-        mToRect.set(toClip);
+        mFromLeftValue = fromClip.left;
+        mFromTopValue = fromClip.top;
+        mFromRightValue= fromClip.right;
+        mFromBottomValue = fromClip.bottom;
+
+        mToLeftValue = toClip.left;
+        mToTopValue = toClip.top;
+        mToRightValue= toClip.right;
+        mToBottomValue = toClip.bottom;
     }
 
     /**
@@ -48,8 +135,7 @@
      */
     public ClipRectAnimation(int fromL, int fromT, int fromR, int fromB,
             int toL, int toT, int toR, int toB) {
-        mFromRect.set(fromL, fromT, fromR, fromB);
-        mToRect.set(toL, toT, toR, toB);
+        this(new Rect(fromL, fromT, fromR, fromB), new Rect(toL, toT, toR, toB));
     }
 
     @Override
@@ -65,4 +151,17 @@
     public boolean willChangeTransformationMatrix() {
         return false;
     }
+
+    @Override
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        super.initialize(width, height, parentWidth, parentHeight);
+        mFromRect.set((int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth),
+                (int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight),
+                (int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth),
+                (int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight));
+        mToRect.set((int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth),
+                (int) resolveSize(mToTopType, mToTopValue, height, parentHeight),
+                (int) resolveSize(mToRightType, mToRightValue, width, parentWidth),
+                (int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight));
+    }
 }
diff --git a/core/java/android/widget/MediaControlView2.java b/core/java/android/widget/MediaControlView2.java
index 6e85ece..0aa2b64 100644
--- a/core/java/android/widget/MediaControlView2.java
+++ b/core/java/android/widget/MediaControlView2.java
@@ -158,6 +158,15 @@
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -194,6 +203,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            MediaControlView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            MediaControlView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return MediaControlView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/android/widget/VideoView2.java b/core/java/android/widget/VideoView2.java
index 955f053..56f3dbd 100644
--- a/core/java/android/widget/VideoView2.java
+++ b/core/java/android/widget/VideoView2.java
@@ -34,46 +34,95 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.Map;
 
+// TODO: Use @link tag to refer MediaPlayer2 in docs once MediaPlayer2.java is submitted. Same to
+// MediaSession2.
+// TODO: change the reference from MediaPlayer to MediaPlayer2.
 /**
- * TODO PUBLIC API
+ * Displays a video file.  VideoView2 class is a View class which is wrapping MediaPlayer2 so that
+ * developers can easily implement a video rendering application.
+ *
+ * <p>
+ * <em> Data sources that VideoView2 supports : </em>
+ * VideoView2 can play video files and audio-only fiels as
+ * well. It can load from various sources such as resources or content providers. The supported
+ * media file formats are the same as MediaPlayer2.
+ *
+ * <p>
+ * <em> View type can be selected : </em>
+ * VideoView2 can render videos on top of TextureView as well as
+ * SurfaceView selectively. The default is SurfaceView and it can be changed using
+ * {@link #setViewType(int)} method. Using SurfaceView is recommended in most cases for saving
+ * battery. TextureView might be preferred for supporting various UIs such as animation and
+ * translucency.
+ *
+ * <p>
+ * <em> Differences between {@link VideoView} class : </em>
+ * VideoView2 covers and inherits the most of
+ * VideoView's functionalities. The main differences are
+ * <ul>
+ * <li> VideoView2 inherits FrameLayout and renders videos using SurfaceView and TextureView
+ * selectively while VideoView inherits SurfaceView class.
+ * <li> VideoView2 is integrated with MediaControlView2 and a default MediaControlView2 instance is
+ * attached to VideoView2 by default. If a developer does not want to use the default
+ * MediaControlView2, needs to set enableControlView attribute to false. For instance,
+ * <pre>
+ * &lt;VideoView2
+ *     android:id="@+id/video_view"
+ *     xmlns:widget="http://schemas.android.com/apk/com.android.media.update"
+ *     widget:enableControlView="false" /&gt;
+ * </pre>
+ * If a developer wants to attach a customed MediaControlView2, then set enableControlView attribute
+ * to false and assign the customed media control widget using {@link #setMediaControlView2}.
+ * <li> VideoView2 is integrated with MediaPlayer2 while VideoView is integrated with MediaPlayer.
+ * <li> VideoView2 is integrated with MediaSession2 and so it responses with media key events.
+ * A VideoView2 keeps a MediaSession2 instance internally and connects it to a corresponding
+ * MediaControlView2 instance.
+ * </p>
+ * </ul>
+ *
+ * <p>
+ * <em> Audio focus and audio attributes : </em>
+ * By default, VideoView2 requests audio focus with
+ * {@link AudioManager#AUDIOFOCUS_GAIN}. Use {@link #setAudioFocusRequest(int)} to change this
+ * behavior. The default {@link AudioAttributes} used during playback have a usage of
+ * {@link AudioAttributes#USAGE_MEDIA} and a content type of
+ * {@link AudioAttributes#CONTENT_TYPE_MOVIE}, use {@link #setAudioAttributes(AudioAttributes)} to
+ * modify them.
+ *
+ * <p>
+ * Note: VideoView2 does not retain its full state when going into the background. In particular, it
+ * does not restore the current play state, play position, selected tracks. Applications should save
+ * and restore these on their own in {@link android.app.Activity#onSaveInstanceState} and
+ * {@link android.app.Activity#onRestoreInstanceState}.
+ *
  * @hide
  */
 public class VideoView2 extends FrameLayout {
+    /** @hide */
     @IntDef({
             VIEW_TYPE_TEXTUREVIEW,
             VIEW_TYPE_SURFACEVIEW
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ViewType {}
+
     public static final int VIEW_TYPE_SURFACEVIEW = 1;
     public static final int VIEW_TYPE_TEXTUREVIEW = 2;
 
     private final VideoView2Provider mProvider;
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context) {
         this(context, null);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs) {
         this(context, attrs, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    /**
-     * @hide
-     */
     public VideoView2(
             @NonNull Context context, @Nullable AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -91,89 +140,102 @@
     }
 
     /**
-     * @hide
+     * Sets MediaControlView2 instance. It will replace the previously assigned MediaControlView2
+     * instance if any.
+     *
+     * @param mediaControlView a media control view2 instance.
      */
     public void setMediaControlView2(MediaControlView2 mediaControlView) {
         mProvider.setMediaControlView2_impl(mediaControlView);
     }
 
     /**
-     * @hide
+     * Returns MediaControlView2 instance which is currently attached to VideoView2 by default or by
+     * {@link #setMediaControlView2} method.
      */
     public MediaControlView2 getMediaControlView2() {
         return mProvider.getMediaControlView2_impl();
     }
 
     /**
-     * @hide
+     * Starts playback with the media contents specified by {@link #setVideoURI} and
+     * {@link #setVideoPath}.
+     * If it has been paused, this method will resume playback from the current position.
      */
     public void start() {
         mProvider.start_impl();
     }
 
     /**
-     * @hide
+     * Pauses playback.
      */
     public void pause() {
         mProvider.pause_impl();
     }
 
     /**
-     * @hide
+     * Gets the duration of the media content specified by #setVideoURI and #setVideoPath
+     * in milliseconds.
      */
     public int getDuration() {
         return mProvider.getDuration_impl();
     }
 
     /**
-     * @hide
+     * Gets current playback position in milliseconds.
      */
     public int getCurrentPosition() {
         return mProvider.getCurrentPosition_impl();
     }
 
+    // TODO: mention about key-frame related behavior.
     /**
-     * @hide
+     * Moves the media by specified time position.
+     * @param msec the offset in milliseconds from the start to seek to.
      */
     public void seekTo(int msec) {
         mProvider.seekTo_impl(msec);
     }
 
     /**
-     * @hide
+     * Says if the media is currently playing.
+     * @return true if the media is playing, false if it is not (eg. paused or stopped).
      */
     public boolean isPlaying() {
         return mProvider.isPlaying_impl();
     }
 
+    // TODO: check what will return if it is a local media.
     /**
-     * @hide
+     * Gets the percentage (0-100) of the content that has been buffered or played so far.
      */
     public int getBufferPercentage() {
         return mProvider.getBufferPercentage_impl();
     }
 
     /**
-     * @hide
+     * Returns the audio session ID.
      */
     public int getAudioSessionId() {
         return mProvider.getAudioSessionId_impl();
     }
 
     /**
-     * @hide
+     * Starts rendering closed caption or subtitles if there is any. The first subtitle track will
+     * be chosen by default if there multiple subtitle tracks exist.
      */
     public void showSubtitle() {
         mProvider.showSubtitle_impl();
     }
 
     /**
-     * @hide
+     * Stops showing closed captions or subtitles.
      */
     public void hideSubtitle() {
         mProvider.hideSubtitle_impl();
     }
 
+    // TODO: This should be revised after integration with MediaPlayer2.
     /**
      * Sets playback speed.
      *
@@ -181,9 +243,7 @@
      * or equal to zero, it will be just ignored and nothing will be changed. If it exceeds the
      * maximum speed that internal engine supports, system will determine best handling or it will
      * be reset to the normal speed 1.0f.
-     * TODO: This should be revised after integration with MediaPlayer2.
      * @param speed the playback speed. It should be positive.
-     * @hide
      */
     public void setSpeed(float speed) {
         mProvider.setSpeed_impl(speed);
@@ -194,7 +254,6 @@
      *
      * If setSpeed() has never been called, returns the default value 1.0f.
      * @return current speed setting
-     * @hide
      */
     public float getSpeed() {
         return mProvider.getSpeed_impl();
@@ -213,8 +272,6 @@
      *
      * @param focusGain the type of audio focus gain that will be requested, or
      *     {@link AudioManager#AUDIOFOCUS_NONE} to disable the use audio focus during playback.
-     *
-     * @hide
      */
     public void setAudioFocusRequest(int focusGain) {
         mProvider.setAudioFocusRequest_impl(focusGain);
@@ -224,8 +281,6 @@
      * Sets the {@link AudioAttributes} to be used during the playback of the video.
      *
      * @param attributes non-null <code>AudioAttributes</code>.
-     *
-     * @hide
      */
     public void setAudioAttributes(@NonNull AudioAttributes attributes) {
         mProvider.setAudioAttributes_impl(attributes);
@@ -235,35 +290,51 @@
      * Sets video path.
      *
      * @param path the path of the video.
-     * @hide
      */
     public void setVideoPath(String path) {
         mProvider.setVideoPath_impl(path);
     }
 
     /**
-     * @hide
+     * Sets video URI.
+     *
+     * @param uri the URI of the video.
      */
     public void setVideoURI(Uri uri) {
         mProvider.setVideoURI_impl(uri);
     }
 
     /**
-     * @hide
+     * Sets video URI using specific headers.
+     *
+     * @param uri     the URI of the video.
+     * @param headers the headers for the URI request.
+     *                Note that the cross domain redirection is allowed by default, but that can be
+     *                changed with key/value pairs through the headers parameter with
+     *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
+     *                to disallow or allow cross domain redirection.
      */
     public void setVideoURI(Uri uri, Map<String, String> headers) {
         mProvider.setVideoURI_impl(uri, headers);
     }
 
     /**
-     * @hide
+     * Selects which view will be used to render video between SurfacView and TextureView.
+     *
+     * @param viewType the view type to render video
+     * <ul>
+     * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+     * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+     * </ul>
      */
     public void setViewType(@ViewType int viewType) {
         mProvider.setViewType_impl(viewType);
     }
 
     /**
-     * @hide
+     * Returns view type.
+     *
+     * @return view type. See {@see setViewType}.
      */
     @ViewType
     public int getViewType() {
@@ -271,42 +342,57 @@
     }
 
     /**
-     * @hide
+     * Stops playback and release all the resources. This should be called whenever a VideoView2
+     * instance is no longer to be used.
      */
     public void stopPlayback() {
         mProvider.stopPlayback_impl();
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the media file is loaded and ready to go.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnPreparedListener(OnPreparedListener l) {
         mProvider.setOnPreparedListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when the end of a media file has been reached during
+     * playback.
+     *
+     * @param l the callback that will be run.
      */
     public void setOnCompletionListener(OnCompletionListener l) {
         mProvider.setOnCompletionListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an error occurs during playback or setup.  If no
+     * listener is specified, or if the listener returned false, VideoView2 will inform the user of
+     * any errors.
+     *
+     * @param l The callback that will be run
      */
     public void setOnErrorListener(OnErrorListener l) {
         mProvider.setOnErrorListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when an informational event occurs during playback or
+     * setup.
+     *
+     * @param l The callback that will be run
      */
     public void setOnInfoListener(OnInfoListener l) {
         mProvider.setOnInfoListener_impl(l);
     }
 
     /**
-     * @hide
+     * Registers a callback to be invoked when a view type change is done.
+     * {@see #setViewType(int)}
+     * @param l The callback that will be run
      */
     public void setOnViewTypeChangedListener(OnViewTypeChangedListener l) {
         mProvider.setOnViewTypeChangedListener_impl(l);
@@ -314,18 +400,22 @@
 
     /**
      * Interface definition of a callback to be invoked when the viw type has been changed.
-     * @hide
      */
     public interface OnViewTypeChangedListener {
         /**
          * Called when the view type has been changed.
-         * @see VideoView2#setViewType(int)
+         * @see #setViewType(int)
+         * @param viewType
+         * <ul>
+         * <li>{@link #VIEW_TYPE_SURFACEVIEW}
+         * <li>{@link #VIEW_TYPE_TEXTUREVIEW}
+         * </ul>
          */
         void onViewTypeChanged(@ViewType int viewType);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when the media source is ready for playback.
      */
     public interface OnPreparedListener {
         /**
@@ -335,7 +425,8 @@
     }
 
     /**
-     * @hide
+     * Interface definition for a callback to be invoked when playback of a media source has
+     * completed.
      */
     public interface OnCompletionListener {
         /**
@@ -345,30 +436,47 @@
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked when there has been an error during an
+     * asynchronous operation.
      */
     public interface OnErrorListener {
+        // TODO: Redefine error codes.
         /**
          * Called to indicate an error.
+         * @param what the type of error that has occurred
+         * @param extra an extra code, specific to the error.
+         * @return true if the method handled the error, false if it didn't.
+         * @see MediaPlayer#OnErrorListener
          */
         boolean onError(int what, int extra);
     }
 
     /**
-     * @hide
+     * Interface definition of a callback to be invoked to communicate some info and/or warning
+     * about the media or its playback.
      */
     public interface OnInfoListener {
         /**
          * Called to indicate an info or a warning.
-         * @see MediaPlayer#OnInfoListener
-         *
          * @param what the type of info or warning.
          * @param extra an extra code, specific to the info.
+         *
+         * @see MediaPlayer#OnInfoListener
          */
         void onInfo(int what, int extra);
     }
 
     @Override
+    protected void onAttachedToWindow() {
+        mProvider.onAttachedToWindow_impl();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        mProvider.onDetachedFromWindow_impl();
+    }
+
+    @Override
     public CharSequence getAccessibilityClassName() {
         return mProvider.getAccessibilityClassName_impl();
     }
@@ -405,6 +513,16 @@
 
     private class SuperProvider implements ViewProvider {
         @Override
+        public void onAttachedToWindow_impl() {
+            VideoView2.super.onAttachedToWindow();
+        }
+
+        @Override
+        public void onDetachedFromWindow_impl() {
+            VideoView2.super.onDetachedFromWindow();
+        }
+
+        @Override
         public CharSequence getAccessibilityClassName_impl() {
             return VideoView2.super.getAccessibilityClassName();
         }
diff --git a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
index ee01a23..d69c7de 100644
--- a/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
+++ b/core/java/com/android/internal/net/INetworkWatchlistManager.aidl
@@ -24,4 +24,5 @@
     boolean stopWatchlistLogging();
     void reloadWatchlist();
     void reportWatchlistIfNecessary();
+    byte[] getWatchlistConfigHash();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b1c45f7..039b66d 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -6947,6 +6947,8 @@
                             WIFI_MULTICAST_ENABLED, mBsi.mWifiMulticastTimers, mBsi.mOnBatteryTimeBase);
                 }
                 mWifiMulticastTimer.startRunningLocked(elapsedRealtimeMs);
+                StatsLog.write_non_chained(
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 1);
             }
         }
 
@@ -6955,6 +6957,8 @@
             if (mWifiMulticastEnabled) {
                 mWifiMulticastEnabled = false;
                 mWifiMulticastTimer.stopRunningLocked(elapsedRealtimeMs);
+                StatsLog.write_non_chained(
+                        StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, getUid(), null, 0);
             }
         }
 
diff --git a/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
new file mode 100644
index 0000000..245a66e
--- /dev/null
+++ b/core/java/com/android/internal/os/logging/MetricsLoggerWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os.logging;
+
+import android.content.Context;
+import android.util.StatsLog;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+/**
+ * Used to wrap different logging calls in one, so that client side code base is clean and more
+ * readable.
+ */
+public class MetricsLoggerWrapper {
+
+    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
+    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
+
+    public static void logPictureInPictureDismissByTap(Context context) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_TAP);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureDismissByDrag(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
+                METRIC_VALUE_DISMISSED_BY_DRAG);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__DISMISSED);
+    }
+
+    public static void logPictureInPictureMinimize(Context context, boolean isMinimized) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
+                isMinimized);
+        if (isMinimized) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__MINIMIZED);
+        } else {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                    context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+        }
+    }
+
+    public static void logPictureInPictureMenuVisible(Context context, boolean menuStateFull) {
+        MetricsLogger.visibility(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
+                menuStateFull);
+    }
+
+    public static void logPictureInPictureEnter(Context context,
+            boolean supportsEnterPipOnTaskSwitch) {
+        MetricsLogger.action(context, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
+                supportsEnterPipOnTaskSwitch);
+        if (supportsEnterPipOnTaskSwitch) {
+            StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED, context.getUserId(),
+                    context.getApplicationInfo().packageName,
+                    context.getApplicationInfo().className,
+                    StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__ENTERED);
+        }
+    }
+
+    public static void logPictureInPictureFullScreen(Context context) {
+        MetricsLogger.action(context,
+                MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+        StatsLog.write(StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED,
+                context.getUserId(),
+                context.getApplicationInfo().packageName,
+                context.getApplicationInfo().className,
+                StatsLog.PICTURE_IN_PICTURE_STATE_CHANGED__STATE__EXPANDED_TO_FULL_SCREEN);
+    }
+}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index e8ee29d..34b5ec8 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3810,6 +3810,19 @@
         }
     }
 
+    @Override
+    public void setNavigationBarDividerColor(int navigationBarDividerColor) {
+        mNavigationBarDividerColor = navigationBarDividerColor;
+        if (mDecor != null) {
+            mDecor.updateColorViews(null, false /* animate */);
+        }
+    }
+
+    @Override
+    public int getNavigationBarDividerColor() {
+        return mNavigationBarDividerColor;
+    }
+
     public void setIsStartingWindow(boolean isStartingWindow) {
         mIsStartingWindow = isStartingWindow;
     }
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e3f1f47..927d757 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -19,9 +19,9 @@
 import android.app.PendingIntent;
 import android.app.trust.IStrongAuthTracker;
 import android.os.Bundle;
-import android.security.keystore.WrappedApplicationKey;
-import android.security.keystore.KeychainSnapshot;
-import android.security.keystore.KeychainProtectionParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyChainProtectionParams;
 import com.android.internal.widget.ICheckCredentialProgressCallback;
 import com.android.internal.widget.VerifyCredentialResponse;
 
@@ -64,7 +64,7 @@
     // {@code ServiceSpecificException} may be thrown to signal an error, which caller can
     // convert to  {@code RecoveryManagerException}.
     void initRecoveryService(in String rootCertificateAlias, in byte[] signedPublicKeyList);
-    KeychainSnapshot getRecoveryData(in byte[] account);
+    KeyChainSnapshot getRecoveryData(in byte[] account);
     byte[] generateAndStoreKey(String alias);
     void removeKey(String alias);
     void setSnapshotCreatedPendingIntent(in PendingIntent intent);
@@ -75,10 +75,10 @@
     void setRecoverySecretTypes(in int[] secretTypes);
     int[] getRecoverySecretTypes();
     int[] getPendingRecoverySecretTypes();
-    void recoverySecretAvailable(in KeychainProtectionParams recoverySecret);
+    void recoverySecretAvailable(in KeyChainProtectionParams recoverySecret);
     byte[] startRecoverySession(in String sessionId,
             in byte[] verifierPublicKey, in byte[] vaultParams, in byte[] vaultChallenge,
-            in List<KeychainProtectionParams> secrets);
+            in List<KeyChainProtectionParams> secrets);
     Map/*<String, byte[]>*/ recoverKeys(in String sessionId, in byte[] recoveryKeyBlob,
             in List<WrappedApplicationKey> applicationKeys);
     void closeSession(in String sessionId);
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 33977f3..5577d6e 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -89,9 +89,12 @@
         mAvatarView = findViewById(R.id.message_icon);
     }
 
-    public void setSender(Notification.Person sender) {
+    public void setSender(Notification.Person sender, CharSequence nameOverride) {
         mSender = sender;
-        mSenderName.setText(sender.getName());
+        if (nameOverride == null) {
+            nameOverride = sender.getName();
+        }
+        mSenderName.setText(nameOverride);
         mNeedsGeneratedAvatar = sender.getIcon() == null;
         if (!mNeedsGeneratedAvatar) {
             setAvatar(sender.getIcon());
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 7a64cad..d45c086 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -80,6 +80,7 @@
     private boolean mIsOneToOne;
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
     private Notification.Person mUser;
+    private CharSequence mNameReplacement;
 
     public MessagingLayout(@NonNull Context context) {
         super(context);
@@ -121,6 +122,11 @@
     }
 
     @RemotableViewMethod
+    public void setNameReplacement(CharSequence nameReplacement) {
+        mNameReplacement = nameReplacement;
+    }
+
+    @RemotableViewMethod
     public void setData(Bundle extras) {
         Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
         List<Notification.MessagingStyle.Message> newMessages
@@ -326,7 +332,12 @@
                 mAddedGroups.add(newGroup);
             }
             newGroup.setLayoutColor(mLayoutColor);
-            newGroup.setSender(senders.get(groupIndex));
+            Notification.Person sender = senders.get(groupIndex);
+            CharSequence nameOverride = null;
+            if (sender != mUser && mNameReplacement != null) {
+                nameOverride = mNameReplacement;
+            }
+            newGroup.setSender(sender, nameOverride);
             mGroups.add(newGroup);
 
             if (mMessagingLinearLayout.indexOfChild(newGroup) != groupIndex) {
diff --git a/core/jni/android/graphics/AnimatedImageDrawable.cpp b/core/jni/android/graphics/AnimatedImageDrawable.cpp
index 12feaab..ec15cce 100644
--- a/core/jni/android/graphics/AnimatedImageDrawable.cpp
+++ b/core/jni/android/graphics/AnimatedImageDrawable.cpp
@@ -58,8 +58,7 @@
         SkPictureRecorder recorder;
         SkCanvas* skcanvas = recorder.beginRecording(bounds);
         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(skcanvas));
-        postProcessAndRelease(env, jpostProcess, std::move(canvas), bounds.width(),
-                              bounds.height());
+        postProcessAndRelease(env, jpostProcess, std::move(canvas));
         if (env->ExceptionCheck()) {
             return 0;
         }
diff --git a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
index 115edd4..85c9ef3 100644
--- a/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
+++ b/core/jni/android/graphics/ByteBufferStreamAdaptor.cpp
@@ -67,7 +67,7 @@
         }
 
         if (!buffer) {
-            return this->setPosition(mPosition + size);
+            return this->setPosition(mPosition + size) ? size : 0;
         }
 
         auto* env = get_env_or_die(mJvm);
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 1c2528f..743a7ef 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -37,15 +37,13 @@
 using namespace android;
 
 static jclass    gImageDecoder_class;
-static jclass    gPoint_class;
+static jclass    gSize_class;
 static jclass    gIncomplete_class;
-static jclass    gCorrupt_class;
 static jclass    gCanvas_class;
 static jmethodID gImageDecoder_constructorMethodID;
 static jmethodID gImageDecoder_postProcessMethodID;
-static jmethodID gPoint_constructorMethodID;
+static jmethodID gSize_constructorMethodID;
 static jmethodID gIncomplete_constructorMethodID;
-static jmethodID gCorrupt_constructorMethodID;
 static jmethodID gCallback_onPartialImageMethodID;
 static jmethodID gCanvas_constructorMethodID;
 static jmethodID gCanvas_releaseMethodID;
@@ -157,8 +155,7 @@
     return native_create(env, std::move(stream));
 }
 
-jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
-                           int width, int height) {
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
     jobject jcanvas = env->NewObject(gCanvas_class, gCanvas_constructorMethodID,
                                      reinterpret_cast<jlong>(canvas.get()));
     if (!jcanvas) {
@@ -169,8 +166,7 @@
     // jcanvas now owns canvas.
     canvas.release();
 
-    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID,
-                              jcanvas, width, height);
+    return env->CallIntMethod(jimageDecoder, gImageDecoder_postProcessMethodID, jcanvas);
 }
 
 // Note: jpostProcess points to an ImageDecoder object if it has a PostProcess object, and nullptr
@@ -271,39 +267,47 @@
     if (jexception) {
         env->ExceptionClear();
     }
+    int onPartialImageError = jexception ? 1  // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
+                                         : 0; // No error.
     switch (result) {
         case SkCodec::kSuccess:
             // Ignore the exception, since the decode was successful anyway.
             jexception = nullptr;
+            onPartialImageError = 0;
             break;
         case SkCodec::kIncompleteInput:
-            if (jcallback && !jexception) {
-                jexception = (jthrowable) env->NewObject(gIncomplete_class,
-                                                         gIncomplete_constructorMethodID);
+            if (!jexception) {
+                onPartialImageError = 2; // ImageDecoder.java's ERROR_SOURCE_EXCEPTION
             }
             break;
         case SkCodec::kErrorInInput:
-            if (jcallback && !jexception) {
-                jexception = (jthrowable) env->NewObject(gCorrupt_class,
-                                                         gCorrupt_constructorMethodID);
+            if (!jexception) {
+                onPartialImageError = 3; // ImageDecoder.java's ERROR_SOURCE_ERROR
             }
             break;
         default:
             SkString msg;
-            msg.printf("getPixels failed with error %i", result);
+            msg.printf("getPixels failed with error %s", SkCodec::ResultToString(result));
             doThrowIOE(env, msg.c_str());
             return nullptr;
     }
 
-    if (jexception) {
-        bool throwException = !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
-                                                      jexception);
+    if (jexception || onPartialImageError) {
+        bool throwException = !jcallback ||
+            !env->CallBooleanMethod(jcallback, gCallback_onPartialImageMethodID,
+                                    onPartialImageError);
         if (env->ExceptionCheck()) {
             return nullptr;
         }
 
         if (throwException) {
-            env->Throw(jexception);
+            if (jexception) {
+                env->Throw(jexception);
+            } else if (onPartialImageError == 2) {
+                env->ThrowNew(gIncomplete_class, "Incomplete input");
+            } else {
+                doThrowIOE(env, "image has an error!");
+            }
             return nullptr;
         }
     }
@@ -399,8 +403,7 @@
     if (jpostProcess) {
         std::unique_ptr<Canvas> canvas(Canvas::create_canvas(bm));
 
-        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas),
-                                                 bm.width(), bm.height());
+        jint pixelFormat = postProcessAndRelease(env, jpostProcess, std::move(canvas));
         if (env->ExceptionCheck()) {
             return nullptr;
         }
@@ -472,7 +475,7 @@
                                             jint sampleSize) {
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
     SkISize size = decoder->mCodec->getSampledDimensions(sampleSize);
-    return env->NewObject(gPoint_class, gPoint_constructorMethodID, size.width(), size.height());
+    return env->NewObject(gSize_class, gSize_constructorMethodID, size.width(), size.height());
 }
 
 static void ImageDecoder_nGetPadding(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
@@ -496,9 +499,9 @@
     { "nCreate",        "([BII)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
     { "nCreate",        "(Ljava/io/InputStream;[B)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
     { "nCreate",        "(Ljava/io/FileDescriptor;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
-    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder$OnPartialImageListener;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;Landroid/graphics/ImageDecoder;IILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
-    { "nGetSampledSize","(JI)Landroid/graphics/Point;",          (void*) ImageDecoder_nGetSampledSize },
+    { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
     { "nClose",         "(J)V",                                  (void*) ImageDecoder_nClose},
     { "nGetMimeType",   "(J)Ljava/lang/String;",                 (void*) ImageDecoder_nGetMimeType },
@@ -507,19 +510,15 @@
 int register_android_graphics_ImageDecoder(JNIEnv* env) {
     gImageDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder"));
     gImageDecoder_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZ)V");
-    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;II)I");
+    gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
 
-    gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
-    gPoint_constructorMethodID = GetMethodIDOrDie(env, gPoint_class, "<init>", "(II)V");
+    gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
+    gSize_constructorMethodID = GetMethodIDOrDie(env, gSize_class, "<init>", "(II)V");
 
     gIncomplete_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$IncompleteException"));
     gIncomplete_constructorMethodID = GetMethodIDOrDie(env, gIncomplete_class, "<init>", "()V");
 
-    gCorrupt_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ImageDecoder$CorruptException"));
-    gCorrupt_constructorMethodID = GetMethodIDOrDie(env, gCorrupt_class, "<init>", "()V");
-
-    jclass callback_class = FindClassOrDie(env, "android/graphics/ImageDecoder$OnPartialImageListener");
-    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, callback_class, "onPartialImage", "(Ljava/io/IOException;)Z");
+    gCallback_onPartialImageMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "onPartialImage", "(I)Z");
 
     gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
     gCanvas_constructorMethodID = GetMethodIDOrDie(env, gCanvas_class, "<init>", "(J)V");
diff --git a/core/jni/android/graphics/ImageDecoder.h b/core/jni/android/graphics/ImageDecoder.h
index 2df71eb..5d7e676 100644
--- a/core/jni/android/graphics/ImageDecoder.h
+++ b/core/jni/android/graphics/ImageDecoder.h
@@ -51,5 +51,4 @@
 // Creates a Java Canvas object from canvas, calls jimageDecoder's PostProcess on it, and then
 // releases the Canvas.
 // Caller needs to check for exceptions.
-jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas,
-                           int width, int height);
+jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas);
diff --git a/core/jni/android_database_SQLiteCommon.cpp b/core/jni/android_database_SQLiteCommon.cpp
index 34544d3..daa2087 100644
--- a/core/jni/android_database_SQLiteCommon.cpp
+++ b/core/jni/android_database_SQLiteCommon.cpp
@@ -223,8 +223,8 @@
     if (sqlite3Message) {
         String8 fullMessage;
         fullMessage.append(sqlite3Message);
-        const char* errcode_msg = sqlite3_error_code_to_msg(errcode).c_str();
-        fullMessage.appendFormat(" (code %s)", errcode_msg); // print extended error code
+        std::string errcode_msg = sqlite3_error_code_to_msg(errcode);
+        fullMessage.appendFormat(" (code %s)", errcode_msg.c_str()); // print extended error code
         if (message) {
             fullMessage.append(": ");
             fullMessage.append(message);
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 331f80f..ce1d5c9 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -22,8 +22,11 @@
 import "frameworks/base/core/proto/android/app/jobparameters.proto";
 import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/telephony/signalstrength.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message BatteryStatsProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   optional int32 report_version = 1;
   optional int64 parcel_version = 2;
   optional string start_platform_version = 3;
@@ -33,6 +36,8 @@
 }
 
 message ControllerActivityProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   // Time (milliseconds) spent in the idle state.
   optional int64 idle_duration_ms = 1;
   // Time (milliseconds) spent in the receive state.
@@ -45,6 +50,8 @@
   // of power. The levels themselves are controller-specific (and may possibly
   // be device specific...yet to be confirmed).
   message TxLevel {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Transmit level. Higher levels draw more power.
     optional int32 level = 1;
     // Time spent in this specific transmit level state.
@@ -54,7 +61,11 @@
 }
 
 message SystemProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   message Battery {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Wall clock time when the data collection started.
     // In case of device time manually reset by users:
     //   start_clock_time_ms keeps the same value in the current collection
@@ -92,6 +103,8 @@
   optional Battery battery = 1;
 
   message BatteryDischarge {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Discharged battery percentage points since the stats were last reset
     // after charging (lower bound approximation).
     optional int32 lower_bound_since_charge = 1;
@@ -142,6 +155,8 @@
   // the entire duration. Field for which the conditions were not consistent
   // for the entire duration should be marked MIXED.
   message BatteryLevelStep {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // How long the battery was at the current level.
     optional int64 duration_ms = 1;
     // Battery level
@@ -192,6 +207,8 @@
   repeated int64 cpu_frequency = 7;
 
   message DataConnection {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Name {
       NONE = 0;
       GPRS = 1;
@@ -221,6 +238,8 @@
   optional ControllerActivityProto global_wifi_controller = 11;
 
   message GlobalNetwork {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Total Bytes received on mobile connections.
     optional int64 mobile_bytes_rx = 1;
     // Total Bytes transmitted on mobile connections.
@@ -245,6 +264,8 @@
   optional GlobalNetwork global_network = 12;
 
   message GlobalWifi {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // The amount of time that wifi has been on while the device was running on
     // battery.
     optional int64 on_duration_ms = 1;
@@ -257,6 +278,8 @@
   // Kernel wakelock metrics are only recorded when the device is unplugged
   // *and* the screen is off.
   message KernelWakelock {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string name = 1;
     // Kernel wakelock stats aren't apportioned across all kernel wakelocks (as
     // app wakelocks stats are).
@@ -267,6 +290,8 @@
   repeated KernelWakelock kernel_wakelock = 14;
 
   message Misc {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int64 screen_on_duration_ms = 1;
     optional int64 phone_on_duration_ms = 2;
     optional int64 full_wakelock_total_duration_ms = 3;
@@ -312,12 +337,16 @@
   optional Misc misc = 15;
 
   message PhoneSignalStrength {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional android.telephony.SignalStrengthProto.StrengthName name = 1;
     optional TimerProto total = 2;
   };
   repeated PhoneSignalStrength phone_signal_strength = 16;
 
   message PowerUseItem {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Sipper {
       UNKNOWN_SIPPER = 0;
       IDLE = 1;
@@ -352,6 +381,8 @@
   repeated PowerUseItem power_use_item = 17;
 
   message PowerUseSummary {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional double battery_capacity_mah = 1;
     optional double computed_power_mah = 2;
     // Lower bound of actual power drained.
@@ -362,6 +393,8 @@
   optional PowerUseSummary power_use_summary = 18;
 
   message ResourcePowerManager {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Either StateName or StateName.VoterName.
     optional string name = 1;
     optional TimerProto total = 2;
@@ -370,6 +403,8 @@
   repeated ResourcePowerManager resource_power_manager = 19;
 
   message ScreenBrightness {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Name {
       DARK = 0; // Not screen-off.
       DIM = 1;
@@ -386,18 +421,24 @@
   optional TimerProto signal_scanning = 21;
 
   message WakeupReason {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string name = 1;
     optional TimerProto total = 2;
   };
   repeated WakeupReason wakeup_reason = 22;
 
   message WifiMulticastWakelockTotal {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int64 duration_ms = 1;
     optional int32 count = 2;
   }
   optional WifiMulticastWakelockTotal wifi_multicast_wakelock_total = 23;
 
   message WifiSignalStrength {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Name {
       NONE = 0;
       POOR = 1;
@@ -411,6 +452,8 @@
   repeated WifiSignalStrength wifi_signal_strength = 24;
 
   message WifiState {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Name {
       OFF = 0;
       OFF_SCANNING = 1;
@@ -427,6 +470,8 @@
   repeated WifiState wifi_state = 25;
 
   message WifiSupplicantState {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum Name {
       INVALID = 0;
       DISCONNECTED = 1;
@@ -449,6 +494,8 @@
 }
 
 message TimerProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   // This may be an apportioned time.
   optional int64 duration_ms = 1;
   optional int64 count = 2;
@@ -468,14 +515,20 @@
 }
 
 message UidProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   // Combination of app ID and user ID.
   optional int32 uid = 1;
 
   // The statistics associated with a particular package.
   message Package {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string name = 1;
 
     message Service {
+      option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
       optional string name = 1;
       // Time spent started.
       optional int64 start_duration_ms = 2;
@@ -492,6 +545,8 @@
 
   // Bluetooth misc data.
   message BluetoothMisc {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Duration spent BLE scanning blamed on this App (i.e. apportioned to this
     // app amongst all apps doing BLE scanning; see explanation of 'apportioned'
     // in App's comment).
@@ -515,6 +570,8 @@
   optional BluetoothMisc bluetooth_misc = 6;
 
   message Cpu {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Total CPU time with processes executing in userspace. Summed up across
     // multiple cores.
     optional int64 user_duration_ms = 1;
@@ -529,6 +586,8 @@
     // system_duration_millis, which are just approximations. Data is not
     // tracked when device is charging.
     message ByFrequency {
+      option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
       // Index of the frequency in system.cpu_frequency. It starts from 1, to
       // make it easier to analyze.
       optional int32 frequency_index = 1;
@@ -551,6 +610,8 @@
     }
     // CPU times at different process states.
     message ByProcessState {
+      option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
       optional ProcessState process_state = 1;
       repeated ByFrequency by_frequency = 2;
     }
@@ -574,7 +635,11 @@
   optional TimerProto video = 14;
 
   message Job {
-    optional string name = 1;
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string name = 1 [
+        (android.privacy).dest = DEST_EXPLICIT
+    ];
     // Job times aren't apportioned.
     optional TimerProto total = 2;
     optional TimerProto background = 3;
@@ -582,10 +647,16 @@
   repeated Job jobs = 15;
 
   message JobCompletion {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Job name.
-    optional string name = 1;
+    optional string name = 1 [
+        (android.privacy).dest = DEST_EXPLICIT
+    ];
 
     message ReasonCount {
+      option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
       optional android.app.JobParametersProto.CancelReason name = 1;
       optional int32 count = 2;
     }
@@ -594,6 +665,8 @@
   repeated JobCompletion job_completion = 16;
 
   message Network {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Mobile data traffic (total, background + foreground).
     optional int64 mobile_bytes_rx = 1;
     optional int64 mobile_bytes_tx = 2;
@@ -631,6 +704,8 @@
 
   // TODO: combine System and App messages?
   message PowerUseItem {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Estimated power use in mAh.
     optional double computed_power_mah = 1;
     // Starting in Oreo, Battery Settings has two modes to display the battery
@@ -648,6 +723,8 @@
 
   // Durations are not pooled/apportioned.
   message Process {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional string name = 1;
     // Time spent executing in user code.
     optional int64 user_duration_ms = 2;
@@ -665,6 +742,8 @@
   repeated Process process = 19;
 
   message StateTime {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // All of these (non-deprecated) states are mutually exclusive and can be
     // added together to find the total time a uid has had any processes running
     // at all.
@@ -706,6 +785,8 @@
   repeated StateTime states = 20;
 
   message Sensor {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional int32 id = 1;
     optional TimerProto apportioned = 2;
     // Background times aren't apportioned.
@@ -714,7 +795,11 @@
   repeated Sensor sensors = 21;
 
   message Sync {
-    optional string name = 1;
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string name = 1 [
+        (android.privacy).dest = DEST_EXPLICIT
+    ];
     // Sync times aren't apportioned.
     optional TimerProto total = 2;
     optional TimerProto background = 3;
@@ -722,6 +807,8 @@
   repeated Sync syncs = 22;
 
   message UserActivity {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     optional android.os.PowerManagerProto.UserActivityEvent name = 1;
     optional int32 count = 2;
   };
@@ -736,6 +823,8 @@
   // wakelocks. AggregatedWakelock, on the other hand, holds overall per-app
   // wakelock data.
   message AggregatedWakelock {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // The total duration that the app spent holding partial wakelocks.
     // It includes both foreground + background use.
     optional int64 partial_duration_ms = 1;
@@ -747,7 +836,11 @@
   optional AggregatedWakelock aggregated_wakelock = 24;
 
   message Wakelock {
-    optional string name = 1;
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string name = 1 [
+        (android.privacy).dest = DEST_EXPLICIT
+    ];
 
     // Full wakelocks keep the screen on. Based on
     // PowerManager.SCREEN_BRIGHT_WAKE_LOCK (deprecated in API 13) and
@@ -776,14 +869,20 @@
   repeated Wakelock wakelocks = 25;
 
   message WakeupAlarm {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Wakeup alarm name.
-    optional string name = 1;
+    optional string name = 1 [
+        (android.privacy).dest = DEST_EXPLICIT
+    ];
     // Only includes counts when screen-off (& on battery).
     optional int32 count = 2;
   }
   repeated WakeupAlarm wakeup_alarm = 26;
 
   message Wifi {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     // Duration holding Wifi-lock. This time is apportioned.
     optional int64 full_wifi_lock_duration_ms = 1;
     // Duration running Wifi. This time is apportioned.
diff --git a/core/proto/android/os/batterytype.proto b/core/proto/android/os/batterytype.proto
index 75d0dd3..2388c1e 100644
--- a/core/proto/android/os/batterytype.proto
+++ b/core/proto/android/os/batterytype.proto
@@ -20,6 +20,10 @@
 
 option java_multiple_files = true;
 
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
 message BatteryTypeProto {
-   optional string type = 1;
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional string type = 1;
 }
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 8d6df12..95eb889 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -391,8 +391,9 @@
     optional SettingProto zram_enabled = 347;
     optional SettingProto enable_smart_replies_in_notifications = 348;
     optional SettingProto show_first_crash_dialog = 349;
+    optional SettingProto wifi_connected_mac_randomization_enabled = 350;
 
-    // Next tag = 350;
+    // Next tag = 351;
 }
 
 message SecureSettingsProto {
diff --git a/core/proto/android/service/battery.proto b/core/proto/android/service/battery.proto
index 4cb7fd3..42fa72c 100644
--- a/core/proto/android/service/battery.proto
+++ b/core/proto/android/service/battery.proto
@@ -21,8 +21,11 @@
 option java_outer_classname = "BatteryServiceProto";
 
 import "frameworks/base/core/proto/android/os/batterymanager.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message BatteryServiceDumpProto {
+    option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
     enum BatteryStatus {
         BATTERY_STATUS_INVALID = 0;
         BATTERY_STATUS_UNKNOWN = 1;
diff --git a/core/proto/android/service/batterystats.proto b/core/proto/android/service/batterystats.proto
index 54d3f40..e31e7f3 100644
--- a/core/proto/android/service/batterystats.proto
+++ b/core/proto/android/service/batterystats.proto
@@ -21,7 +21,10 @@
 option java_outer_classname = "BatteryStatsServiceProto";
 
 import "frameworks/base/core/proto/android/os/batterystats.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message BatteryStatsServiceDumpProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
   optional android.os.BatteryStatsProto batterystats = 1;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 93d852c..081c92c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -327,6 +327,10 @@
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.CONNECT_TO_NETWORK" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_WIFI_NETWORK" />
     <protected-broadcast android:name="com.android.server.wifi.ConnectToNetworkNotification.PICK_NETWORK_AFTER_FAILURE" />
+    <protected-broadcast android:name="com.android.server.wifi.wakeup.DISMISS_NOTIFICATION" />
+    <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_PREFERENCES" />
+    <protected-broadcast android:name="com.android.server.wifi.wakeup.OPEN_WIFI_SETTINGS" />
+    <protected-broadcast android:name="com.android.server.wifi.wakeup.TURN_OFF_WIFI_WAKE" />
     <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index a67b0ca..371bcfe 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -17,9 +17,17 @@
 */
 -->
 
-<set xmlns:android="http://schemas.android.com/apk/res/android" android:zAdjustment="normal">
-    <alpha android:fromAlpha="0.7" android:toAlpha="1.0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/linear_out_slow_in"
-            android:duration="250"/>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <translate
+        android:fromYDelta="-2%"
+        android:toYDelta="0"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="425"/>
+    <alpha
+        android:fromAlpha="0.9"
+        android:toAlpha="1.0"
+        android:interpolator="@interpolator/activity_close_dim"
+        android:startOffset="0"
+        android:duration="425"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index d8c42ed..143bedb 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -18,15 +18,27 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false" android:zAdjustment="top">
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:interpolator="@interpolator/linear"
-            android:fillEnabled="true"
-            android:fillBefore="false" android:fillAfter="true"
-            android:startOffset="100"
-            android:duration="150"/>
-    <translate android:fromYDelta="0%" android:toYDelta="8%"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/accelerate_quart"
-            android:duration="250"/>
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
+    <translate
+        android:fromYDelta="0"
+        android:toYDelta="4.1%"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="425"/>
+    <cliprect
+        android:fromLeft="0%"
+        android:fromTop="0%"
+        android:fromRight="100%"
+        android:fromBottom="100%"
+        android:toLeft="0%"
+        android:toTop="95.9%"
+        android:toRight="100%"
+        android:toBottom="100%"
+        android:interpolator="@interpolator/exaggerated_ease"
+        android:duration="425"/>
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
+        android:interpolator="@interpolator/fast_out_linear_in"
+        android:duration="425"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 1d949d2..f9381b4 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -18,15 +17,21 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false"
-        android:zAdjustment="top">
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:interpolator="@interpolator/decelerate_quart"
-            android:fillEnabled="true"
-            android:fillBefore="false" android:fillAfter="true"
-            android:duration="200"/>
-    <translate android:fromYDelta="8%" android:toYDelta="0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:duration="350"/>
+    android:shareInterpolator="false">
+    <translate
+        android:fromYDelta="4.1%"
+        android:toYDelta="0"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="425"/>
+    <cliprect
+        android:fromLeft="0%"
+        android:fromTop="95.9%"
+        android:fromRight="100%"
+        android:fromBottom="100%"
+        android:toLeft="0%"
+        android:toTop="0%"
+        android:toRight="100%"
+        android:toBottom="100%"
+        android:interpolator="@interpolator/exaggerated_ease"
+        android:duration="425"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 3a84197..d52b150 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -18,9 +17,15 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:background="#ff000000" android:zAdjustment="normal">
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.7"
-            android:fillEnabled="true" android:fillBefore="false" android:fillAfter="true"
-            android:interpolator="@interpolator/fast_out_slow_in"
-            android:duration="217"/>
+    android:shareInterpolator="false">
+    <translate
+        android:fromYDelta="0"
+        android:toYDelta="-2%"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="425"/>
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="0.9"
+        android:interpolator="@interpolator/linear"
+        android:duration="117"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index bea0ee5..81d1300 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -16,27 +15,54 @@
 ** limitations under the License.
 */
 -->
-
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false" android:zAdjustment="normal">
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
 
-    <alpha android:fromAlpha="0.6" android:toAlpha="1.0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="600"
-            android:duration="133"/>
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="217"/>
 
-    <translate android:fromYDelta="10%" android:toYDelta="0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_cubic"
-            android:startOffset="300"
-            android:duration="433" />
+    <translate
+        android:fromXDelta="105%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
 
-    <scale android:fromXScale=".9" android:toXScale="1.0"
-            android:fromYScale=".9" android:toYScale="1.0"
-            android:pivotX="50%p" android:pivotY="0%p"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/fast_out_slow_in"
-            android:startOffset="300"
-            android:duration="433" />
+    <scale
+        android:fromXScale="1.0526"
+        android:toXScale="1"
+        android:fromYScale="1.0526"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
+
+    <scale
+        android:fromXScale="0.95"
+        android:toXScale="1"
+        android:fromYScale="0.95"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:startOffset="283"
+        android:duration="317"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index b6a0807..ab8b89c 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -18,20 +17,44 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false" android:zAdjustment="top">
+    android:shareInterpolator="false">
 
-    <alpha android:fromAlpha="1.0" android:toAlpha="0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/accelerate_quad"
-            android:startOffset="250"
-            android:duration="167"/>
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="283"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="110%"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/accelerate_quint"
-            android:duration="417"/>
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="-105%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
+
+    <scale
+        android:fromXScale="1.0"
+        android:toXScale="0.95"
+        android:fromYScale="1.0"
+        android:toYScale="0.95"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:duration="700" />
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
+        android:duration="600"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index b73e14f..2ee7cd8 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -15,20 +14,55 @@
 ** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
--->
-<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
+--><!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:shareInterpolator="false" android:zAdjustment="top">
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
 
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quart"
-            android:startOffset="300"
-            android:duration="167"/>
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="217"/>
 
-    <translate android:fromYDelta="110%" android:toYDelta="0"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/decelerate_quint"
-            android:startOffset="300"
-            android:duration="417" />
+    <translate
+        android:fromXDelta="-105%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
+
+    <scale
+        android:fromXScale="1.0526"
+        android:toXScale="1"
+        android:fromYScale="1.0526"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
+
+    <scale
+        android:fromXScale="0.95"
+        android:toXScale="1"
+        android:fromYScale="0.95"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:startOffset="283"
+        android:duration="317"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
index ad89fde..a92425e 100644
--- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -18,24 +18,61 @@
 -->
 <!-- This should in sync with task_open_enter.xml -->
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-     android:shareInterpolator="false" android:zAdjustment="top">
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
 
-    <alpha android:fromAlpha="0" android:toAlpha="1.0"
-           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-           android:interpolator="@interpolator/decelerate_quart"
-           android:startOffset="300"
-           android:duration="167"/>
+    <alpha
+        android:fromAlpha="1"
+        android:toAlpha="1.0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="217"/>
 
-    <translate android:fromYDelta="110%" android:toYDelta="0"
-               android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-               android:interpolator="@interpolator/decelerate_quint"
-               android:startOffset="300"
-               android:duration="417"/>
+    <translate
+        android:fromXDelta="-105%"
+        android:toXDelta="0"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
+
+    <scale
+        android:fromXScale="1.0526"
+        android:toXScale="1"
+        android:fromYScale="1.0526"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
+
+    <scale
+        android:fromXScale="0.95"
+        android:toXScale="1"
+        android:fromYScale="0.95"
+        android:toYScale="1"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:startOffset="283"
+        android:duration="317"/>
 
     <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
          cross_profile_apps_thumbmail.xml -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-           android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-           android:startOffset="717"
-           android:duration="200"/>
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
+        android:startOffset="717"
+        android:duration="200"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 78d0fb0..ecb98ce 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
 /*
 ** Copyright 2009, The Android Open Source Project
 **
@@ -18,26 +17,44 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+    android:shareInterpolator="false">
 
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.6"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/accelerate_cubic"
-            android:duration="133"/>
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/linear"
+        android:startOffset="67"
+        android:duration="283"/>
 
-    <translate android:fromYDelta="0" android:toYDelta="10%"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:interpolator="@interpolator/accelerate_cubic"
-            android:duration="433"/>
+    <translate
+        android:fromXDelta="0"
+        android:toXDelta="105%"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:interpolator="@interpolator/aggressive_ease"
+        android:startOffset="50"
+        android:duration="383"/>
 
-    <scale android:fromXScale="1.0" android:toXScale="0.9"
-            android:fromYScale="1.0" android:toYScale="0.9"
-            android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
-            android:pivotX="50%p" android:pivotY="50%p"
-            android:interpolator="@interpolator/fast_out_slow_in"
-            android:duration="433" />
+    <scale
+        android:fromXScale="1.0"
+        android:toXScale="0.95"
+        android:fromYScale="1.0"
+        android:toYScale="0.95"
+        android:fillEnabled="true"
+        android:fillBefore="true"
+        android:fillAfter="true"
+        android:pivotX="50%"
+        android:pivotY="50%"
+        android:interpolator="@interpolator/fast_out_slow_in"
+        android:duration="283"/>
 
     <!-- This is needed to keep the animation running while task_open_enter completes -->
-    <alpha android:fromAlpha="1.0" android:toAlpha="1.0"
-            android:duration="700" />
+    <alpha
+        android:fromAlpha="1.0"
+        android:toAlpha="1.0"
+        android:duration="600"/>
 </set>
\ No newline at end of file
diff --git a/core/res/res/interpolator/activity_close_dim.xml b/core/res/res/interpolator/activity_close_dim.xml
new file mode 100644
index 0000000..faad139
--- /dev/null
+++ b/core/res/res/interpolator/activity_close_dim.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.33"
+    android:controlY1="0"
+    android:controlX2="1"
+    android:controlY2="1"/>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 4eaf93d..354d658 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1897,6 +1897,7 @@
         <enum name="KEYCODE_SYSTEM_NAVIGATION_LEFT" value="282" />
         <enum name="KEYCODE_SYSTEM_NAVIGATION_RIGHT" value="283" />
         <enum name="KEYCODE_ALL_APPS" value="284" />
+        <enum name="KEYCODE_REFRESH" value="285" />
     </attr>
 
     <!-- ***************************************************************** -->
@@ -2066,7 +2067,8 @@
              <p>For this to take effect, the window must be drawing the system bar backgrounds with
              {@link android.R.attr#windowDrawsSystemBarBackgrounds} and the navigation bar must not
              have been requested to be translucent with
-             {@link android.R.attr#windowTranslucentNavigation}. -->
+             {@link android.R.attr#windowTranslucentNavigation}.
+             Corresponds to {@link android.view.Window#setNavigationBarDividerColor(int)}. -->
         <attr name="navigationBarDividerColor" format="color" />
 
         <!-- The duration, in milliseconds, of the window background fade duration
@@ -6336,6 +6338,17 @@
         <attr name="toAlpha" format="float" />
     </declare-styleable>
 
+    <declare-styleable name="ClipRectAnimation">
+        <attr name="fromLeft" format="fraction" />
+        <attr name="fromTop" format="fraction" />
+        <attr name="fromRight" format="fraction" />
+        <attr name="fromBottom" format="fraction" />
+        <attr name="toLeft" format="fraction" />
+        <attr name="toTop" format="fraction" />
+        <attr name="toRight" format="fraction" />
+        <attr name="toBottom" format="fraction" />
+    </declare-styleable>
+
     <declare-styleable name="LayoutAnimation">
         <!-- Fraction of the animation duration used to delay the beginning of
          the animation of each child. -->
diff --git a/core/res/res/values/locale_config.xml b/core/res/res/values/locale_config.xml
index 2c4058a..35eee6a 100644
--- a/core/res/res/values/locale_config.xml
+++ b/core/res/res/values/locale_config.xml
@@ -23,35 +23,60 @@
         <item>ak-GH</item> <!-- Akan (Ghana) -->
         <item>am-ET</item> <!-- Amharic (Ethiopia) -->
         <item>ar-AE</item> <!-- Arabic (United Arab Emirates) -->
+        <item>ar-AE-u-nu-latn</item> <!-- Arabic (United Arab Emirates,Western Digits) -->
         <item>ar-BH</item> <!-- Arabic (Bahrain) -->
+        <item>ar-BH-u-nu-latn</item> <!-- Arabic (Bahrain,Western Digits) -->
         <item>ar-DJ</item> <!-- Arabic (Djibouti) -->
+        <item>ar-DJ-u-nu-latn</item> <!-- Arabic (Djibouti,Western Digits) -->
         <item>ar-DZ</item> <!-- Arabic (Algeria) -->
+        <item>ar-DZ-u-nu-arab</item> <!-- Arabic (Algeria,Arabic-Indic Digits) -->
         <item>ar-EG</item> <!-- Arabic (Egypt) -->
         <item>ar-EG-u-nu-latn</item> <!-- Arabic (Egypt,Western Digits) -->
         <item>ar-EH</item> <!-- Arabic (Western Sahara) -->
+        <item>ar-EH-u-nu-arab</item> <!-- Arabic (Western Sahara,Arabic-Indic Digits) -->
         <item>ar-ER</item> <!-- Arabic (Eritrea) -->
+        <item>ar-ER-u-nu-latn</item> <!-- Arabic (Eritrea,Western Digits) -->
         <item>ar-IL</item> <!-- Arabic (Israel) -->
+        <item>ar-IL-u-nu-latn</item> <!-- Arabic (Israel,Western Digits) -->
         <item>ar-IQ</item> <!-- Arabic (Iraq) -->
+        <item>ar-IQ-u-nu-latn</item> <!-- Arabic (Iraq,Western Digits) -->
         <item>ar-JO</item> <!-- Arabic (Jordan) -->
+        <item>ar-JO-u-nu-latn</item> <!-- Arabic (Jordan,Western Digits) -->
         <item>ar-KM</item> <!-- Arabic (Comoros) -->
+        <item>ar-KM-u-nu-latn</item> <!-- Arabic (Comoros,Western Digits) -->
         <item>ar-KW</item> <!-- Arabic (Kuwait) -->
+        <item>ar-KW-u-nu-latn</item> <!-- Arabic (Kuwait,Western Digits) -->
         <item>ar-LB</item> <!-- Arabic (Lebanon) -->
+        <item>ar-LB-u-nu-latn</item> <!-- Arabic (Lebanon,Western Digits) -->
         <item>ar-LY</item> <!-- Arabic (Libya) -->
+        <item>ar-LY-u-nu-arab</item> <!-- Arabic (Libya,Arabic-Indic Digits) -->
         <item>ar-MA</item> <!-- Arabic (Morocco) -->
+        <item>ar-MA-u-nu-arab</item> <!-- Arabic (Morocco,Arabic-Indic Digits) -->
         <item>ar-MR</item> <!-- Arabic (Mauritania) -->
+        <item>ar-MR-u-nu-latn</item> <!-- Arabic (Mauritania,Western Digits) -->
         <item>ar-OM</item> <!-- Arabic (Oman) -->
+        <item>ar-OM-u-nu-latn</item> <!-- Arabic (Oman,Western Digits) -->
         <item>ar-PS</item> <!-- Arabic (Palestine) -->
+        <item>ar-PS-u-nu-latn</item> <!-- Arabic (Palestine,Western Digits) -->
         <item>ar-QA</item> <!-- Arabic (Qatar) -->
+        <item>ar-QA-u-nu-latn</item> <!-- Arabic (Qatar,Western Digits) -->
         <item>ar-SA</item> <!-- Arabic (Saudi Arabia) -->
+        <item>ar-SA-u-nu-latn</item> <!-- Arabic (Saudi Arabia,Western Digits) -->
         <item>ar-SD</item> <!-- Arabic (Sudan) -->
+        <item>ar-SD-u-nu-latn</item> <!-- Arabic (Sudan,Western Digits) -->
         <item>ar-SO</item> <!-- Arabic (Somalia) -->
+        <item>ar-SO-u-nu-latn</item> <!-- Arabic (Somalia,Western Digits) -->
         <item>ar-SS</item> <!-- Arabic (South Sudan) -->
+        <item>ar-SS-u-nu-latn</item> <!-- Arabic (South Sudan,Western Digits) -->
         <item>ar-SY</item> <!-- Arabic (Syria) -->
+        <item>ar-SY-u-nu-latn</item> <!-- Arabic (Syria,Western Digits) -->
         <item>ar-TD</item> <!-- Arabic (Chad) -->
+        <item>ar-TD-u-nu-latn</item> <!-- Arabic (Chad,Western Digits) -->
         <item>ar-TN</item> <!-- Arabic (Tunisia) -->
         <item>ar-TN-u-nu-arab</item> <!-- Arabic (Tunisia,Arabic-Indic Digits) -->
         <item>ar-XB</item> <!-- Right-to-left pseudolocale -->
         <item>ar-YE</item> <!-- Arabic (Yemen) -->
+        <item>ar-YE-u-nu-latn</item> <!-- Arabic (Yemen,Western Digits) -->
         <item>as-IN</item> <!-- Assamese (India) -->
         <item>asa-TZ</item> <!-- Asu (Tanzania) -->
         <item>az-Cyrl-AZ</item> <!-- Azerbaijani (Cyrillic,Azerbaijan) -->
@@ -63,7 +88,9 @@
         <item>bg-BG</item> <!-- Bulgarian (Bulgaria) -->
         <item>bm-ML</item> <!-- Bambara (Mali) -->
         <item>bn-BD</item> <!-- Bengali (Bangladesh) -->
+        <item>bn-BD-u-nu-latn</item> <!-- Bengali (Bangladesh,Western Digits) -->
         <item>bn-IN</item> <!-- Bengali (India) -->
+        <item>bn-IN-u-nu-latn</item> <!-- Bengali (India,Western Digits) -->
         <item>bo-CN</item> <!-- Tibetan (China) -->
         <item>bo-IN</item> <!-- Tibetan (India) -->
         <item>br-FR</item> <!-- Breton (France) -->
@@ -230,7 +257,9 @@
         <item>eu-ES</item> <!-- Basque (Spain) -->
         <item>ewo-CM</item> <!-- Ewondo (Cameroon) -->
         <item>fa-AF</item> <!-- Persian (Afghanistan) -->
+        <item>fa-AF-u-nu-latn</item> <!-- Persian (Afghanistan,Western Digits) -->
         <item>fa-IR</item> <!-- Persian (Iran) -->
+        <item>fa-IR-u-nu-latn</item> <!-- Persian (Iran,Western Digits) -->
         <item>ff-CM</item> <!-- Fulah (Cameroon) -->
         <item>ff-GN</item> <!-- Fulah (Guinea) -->
         <item>ff-MR</item> <!-- Fulah (Mauritania) -->
@@ -473,7 +502,9 @@
         <item>ug-CN</item> <!-- Uyghur (China) -->
         <item>uk-UA</item> <!-- Ukrainian (Ukraine) -->
         <item>ur-IN</item> <!-- Urdu (India) -->
+        <item>ur-IN-u-nu-latn</item> <!-- Urdu (India,Western Digits) -->
         <item>ur-PK</item> <!-- Urdu (Pakistan) -->
+        <item>ur-PK-u-nu-arabext</item> <!-- Urdu (Pakistan,Extended Arabic-Indic Digits) -->
         <item>uz-Arab-AF</item> <!-- Uzbek (Arabic,Afghanistan) -->
         <item>uz-Cyrl-UZ</item> <!-- Uzbek (Cyrillic,Uzbekistan) -->
         <item>uz-Latn-UZ</item> <!-- Uzbek (Latin,Uzbekistan) -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2be5237..88549b5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4485,7 +4485,7 @@
     <string name="zen_mode_alarm">Until <xliff:g id="formattedTime" example="10:00 PM">%1$s</xliff:g> (next alarm)</string>
 
     <!-- Zen mode condition: no exit criteria. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_forever">Until you turn off Do Not Disturb</string>
+    <string name="zen_mode_forever">Until you turn off</string>
 
     <!-- Zen mode condition: no exit criteria, includes the name of the feature for emphasis. [CHAR LIMIT=NONE] -->
     <string name="zen_mode_forever_dnd">Until you turn off Do Not Disturb</string>
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index fa0ea5c..bc2d099 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -391,6 +391,7 @@
                     Settings.Global.WFC_IMS_ROAMING_MODE,
                     Settings.Global.WIFI_BADGING_THRESHOLDS,
                     Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
                     Settings.Global.WIFI_COUNTRY_CODE,
                     Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN,
                     Settings.Global.WIFI_DISPLAY_CERTIFICATION_ON,
@@ -480,7 +481,6 @@
                  Settings.Secure.INSTALL_NON_MARKET_APPS,
                  Settings.Secure.LAST_SETUP_SHOWN,
                  Settings.Secure.LOCATION_MODE,
-                 Settings.Secure.LOCATION_PREVIOUS_MODE,
                  Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, // Candidate?
                  Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT, // Candidate?
                  Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 4be6408..09192f4 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -172,6 +172,8 @@
     <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
     <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
 
+    <assign-permission name="android.permission.DUMP" uid="statsd" />
+    <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
     <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
 
     <!-- This is a list of all the libraries available for application
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 74f8c71..8699cb4 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -192,7 +192,7 @@
 # key 170 "KEY_ISO"
 key 171   MUSIC
 key 172   HOME
-# key 173 "KEY_REFRESH"
+key 173   REFRESH
 # key 174 "KEY_EXIT"
 # key 175 "KEY_MOVE"
 # key 176 "KEY_EDIT"
diff --git a/docs/html/reference/images/text/style/custombulletspan.png b/docs/html/reference/images/text/style/custombulletspan.png
new file mode 100644
index 0000000..251f8a1
--- /dev/null
+++ b/docs/html/reference/images/text/style/custombulletspan.png
Binary files differ
diff --git a/docs/html/reference/images/text/style/defaultbulletspan.png b/docs/html/reference/images/text/style/defaultbulletspan.png
new file mode 100644
index 0000000..854143f
--- /dev/null
+++ b/docs/html/reference/images/text/style/defaultbulletspan.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 4d715d1..02d22be 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import static android.system.OsConstants.SEEK_CUR;
 import static android.system.OsConstants.SEEK_SET;
 
 import android.annotation.IntDef;
@@ -31,6 +32,7 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.NinePatchDrawable;
 import android.net.Uri;
+import android.util.Size;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.DisplayMetrics;
@@ -40,6 +42,7 @@
 import dalvik.system.CloseGuard;
 
 import java.nio.ByteBuffer;
+import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -55,14 +58,16 @@
 
 /**
  *  Class for decoding images as {@link Bitmap}s or {@link Drawable}s.
- *  @hide
  */
 public final class ImageDecoder implements AutoCloseable {
     /**
      *  Source of the encoded image data.
      */
     public static abstract class Source {
+        private Source() {}
+
         /* @hide */
+        @Nullable
         Resources getResources() { return null; }
 
         /* @hide */
@@ -79,11 +84,12 @@
         }
 
         /* @hide */
+        @NonNull
         abstract ImageDecoder createImageDecoder() throws IOException;
     };
 
     private static class ByteArraySource extends Source {
-        ByteArraySource(byte[] data, int offset, int length) {
+        ByteArraySource(@NonNull byte[] data, int offset, int length) {
             mData = data;
             mOffset = offset;
             mLength = length;
@@ -99,7 +105,7 @@
     }
 
     private static class ByteBufferSource extends Source {
-        ByteBufferSource(ByteBuffer buffer) {
+        ByteBufferSource(@NonNull ByteBuffer buffer) {
             mBuffer = buffer;
         }
         private final ByteBuffer mBuffer;
@@ -116,7 +122,7 @@
     }
 
     private static class ContentResolverSource extends Source {
-        ContentResolverSource(ContentResolver resolver, Uri uri) {
+        ContentResolverSource(@NonNull ContentResolver resolver, @NonNull Uri uri) {
             mResolver = resolver;
             mUri = uri;
         }
@@ -167,7 +173,31 @@
         }
     }
 
-    private static ImageDecoder createFromStream(InputStream is) throws IOException {
+    @NonNull
+    private static ImageDecoder createFromFile(@NonNull File file) throws IOException {
+        FileInputStream stream = new FileInputStream(file);
+        FileDescriptor fd = stream.getFD();
+        try {
+            Os.lseek(fd, 0, SEEK_CUR);
+        } catch (ErrnoException e) {
+            return createFromStream(stream);
+        }
+
+        ImageDecoder decoder = null;
+        try {
+            decoder = nCreate(fd);
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(stream);
+            } else {
+                decoder.mInputStream = stream;
+            }
+        }
+        return decoder;
+    }
+
+    @NonNull
+    private static ImageDecoder createFromStream(@NonNull InputStream is) throws IOException {
         // Arbitrary size matches BitmapFactory.
         byte[] storage = new byte[16 * 1024];
         ImageDecoder decoder = null;
@@ -220,7 +250,7 @@
     }
 
     private static class ResourceSource extends Source {
-        ResourceSource(Resources res, int resId) {
+        ResourceSource(@NonNull Resources res, int resId) {
             mResources = res;
             mResId = resId;
             mResDensity = Bitmap.DENSITY_NONE;
@@ -269,59 +299,54 @@
         }
     }
 
+    private static class FileSource extends Source {
+        FileSource(@NonNull File file) {
+            mFile = file;
+        }
+
+        private final File mFile;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            return createFromFile(mFile);
+        }
+    }
+
     /**
      *  Contains information about the encoded image.
      */
     public static class ImageInfo {
-        /**
-         * Width of the image, without scaling or cropping.
-         */
-        public final int width;
+        private final Size mSize;
+        private ImageDecoder mDecoder;
 
-        /**
-         * Height of the image, without scaling or cropping.
-         */
-        public final int height;
-
-        /* @hide */
-        ImageDecoder decoder;
-
-        /* @hide */
-        ImageInfo(ImageDecoder decoder) {
-            this.width   = decoder.mWidth;
-            this.height  = decoder.mHeight;
-            this.decoder = decoder;
+        private ImageInfo(@NonNull ImageDecoder decoder) {
+            mSize = new Size(decoder.mWidth, decoder.mHeight);
+            mDecoder = decoder;
         }
 
         /**
-         * The mimeType of the image, if known.
+         * Size of the image, without scaling or cropping.
          */
+        @NonNull
+        public Size getSize() {
+            return mSize;
+        }
+
+        /**
+         * The mimeType of the image.
+         */
+        @NonNull
         public String getMimeType() {
-            return decoder.getMimeType();
+            return mDecoder.getMimeType();
         }
     };
 
     /**
-     *  Used if the provided data is incomplete.
-     *
-     *  May be thrown if there is nothing to display.
-     *
-     *  If supplied to onPartialImage, there may be a correct partial image to
-     *  display.
+     *  Thrown if the provided data is incomplete.
      */
     public static class IncompleteException extends IOException {};
 
     /**
-     *  Used if the provided data is corrupt.
-     *
-     *  May be thrown if there is nothing to display.
-     *
-     *  If supplied to onPartialImage, there may be a correct partial image to
-     *  display.
-     */
-    public static class CorruptException extends IOException {};
-
-    /**
      *  Optional listener supplied to {@link #decodeDrawable} or
      *  {@link #decodeBitmap}.
      */
@@ -329,31 +354,55 @@
         /**
          *  Called when the header is decoded and the size is known.
          *
-         *  @param info Information about the encoded image.
          *  @param decoder allows changing the default settings of the decode.
+         *  @param info Information about the encoded image.
+         *  @param source that created the decoder.
          */
-        public void onHeaderDecoded(ImageInfo info, ImageDecoder decoder);
+        public void onHeaderDecoded(@NonNull ImageDecoder decoder,
+                @NonNull ImageInfo info, @NonNull Source source);
 
     };
 
     /**
+     *  An Exception was thrown reading the {@link Source}.
+     */
+    public static final int ERROR_SOURCE_EXCEPTION  = 1;
+
+    /**
+     *  The encoded data was incomplete.
+     */
+    public static final int ERROR_SOURCE_INCOMPLETE = 2;
+
+    /**
+     *  The encoded data contained an error.
+     */
+    public static final int ERROR_SOURCE_ERROR      = 3;
+
+    @Retention(SOURCE)
+    @IntDef({ ERROR_SOURCE_EXCEPTION, ERROR_SOURCE_INCOMPLETE, ERROR_SOURCE_ERROR })
+    public @interface Error {};
+
+    /**
      *  Optional listener supplied to the ImageDecoder.
+     *
+     *  Without this listener, errors will throw {@link java.io.IOException}.
      */
     public static interface OnPartialImageListener {
         /**
          *  Called when there is only a partial image to display.
          *
-         *  If the input is incomplete or contains an error, this listener lets
-         *  the client know that and allows them to optionally bypass the rest
-         *  of the decode/creation process.
+         *  If decoding is interrupted after having decoded a partial image,
+         *  this listener lets the client know that and allows them to
+         *  optionally finish the rest of the decode/creation process to create
+         *  a partial {@link Drawable}/{@link Bitmap}.
          *
-         *  @param e IOException containing information about the error that
-         *      interrupted the decode.
-         *  @return True (which is the default) to create and return a
-         *      {@link Drawable}/{@link Bitmap} with partial data. False to
-         *      abort the decode and throw the {@link java.io.IOException}.
+         *  @param error indicating what interrupted the decode.
+         *  @param source that had the error.
+         *  @return True to create and return a {@link Drawable}/{@link Bitmap}
+         *      with partial data. False (which is the default) to abort the
+         *      decode and throw {@link java.io.IOException}.
          */
-        public boolean onPartialImage(IOException e);
+        public boolean onPartialImage(@Error int error, @NonNull Source source);
     };
 
     // Fields
@@ -364,14 +413,15 @@
 
     private int     mDesiredWidth;
     private int     mDesiredHeight;
-    private int     mAllocator = DEFAULT_ALLOCATOR;
+    private int     mAllocator = ALLOCATOR_DEFAULT;
     private boolean mRequireUnpremultiplied = false;
     private boolean mMutable = false;
     private boolean mPreferRamOverQuality = false;
     private boolean mAsAlphaMask = false;
     private Rect    mCropRect;
+    private Source  mSource;
 
-    private PostProcess            mPostProcess;
+    private PostProcessor          mPostProcessor;
     private OnPartialImageListener mOnPartialImageListener;
 
     // Objects for interacting with the input.
@@ -412,6 +462,7 @@
 
     /**
      * Create a new {@link Source} from an asset.
+     * @hide
      *
      * @param res the {@link Resources} object containing the image data.
      * @param resId resource ID of the image data.
@@ -441,6 +492,7 @@
 
     /**
      * Create a new {@link Source} from a byte array.
+     *
      * @param data byte array of compressed image data.
      * @param offset offset into data for where the decoder should begin
      *      parsing.
@@ -448,7 +500,9 @@
      * @throws NullPointerException if data is null.
      * @throws ArrayIndexOutOfBoundsException if offset and length are
      *      not within data.
+     * @hide
      */
+    @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
         if (data == null) {
@@ -464,7 +518,9 @@
 
     /**
      * See {@link #createSource(byte[], int, int).
+     * @hide
      */
+    @NonNull
     public static Source createSource(@NonNull byte[] data) {
         return createSource(data, 0, data.length);
     }
@@ -472,13 +528,15 @@
     /**
      * Create a new {@link Source} from a {@link java.nio.ByteBuffer}.
      *
-     * The returned {@link Source} effectively takes ownership of the
+     * <p>The returned {@link Source} effectively takes ownership of the
      * {@link java.nio.ByteBuffer}; i.e. no other code should modify it after
-     * this call.
+     * this call.</p>
      *
-     * Decoding will start from {@link java.nio.ByteBuffer#position()}.
+     * Decoding will start from {@link java.nio.ByteBuffer#position()}. The
+     * position after decoding is undefined.
      */
-    public static Source createSource(ByteBuffer buffer) {
+    @NonNull
+    public static Source createSource(@NonNull ByteBuffer buffer) {
         return new ByteBufferSource(buffer);
     }
 
@@ -499,20 +557,29 @@
     }
 
     /**
+     * Create a new {@link Source} from a {@link java.io.File}.
+     */
+    @NonNull
+    public static Source createSource(@NonNull File file) {
+        return new FileSource(file);
+    }
+
+    /**
      *  Return the width and height of a given sample size.
      *
-     *  This takes an input that functions like
+     *  <p>This takes an input that functions like
      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
      *  height that can be acheived by sampling the encoded image. Other widths
      *  and heights may be supported, but will require an additional (internal)
      *  scaling step. Such internal scaling is *not* supported with
-     *  {@link #requireUnpremultiplied}.
+     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
-     *  @return Point {@link Point#x} and {@link Point#y} correspond to the
-     *      width and height after sampling.
+     *  @return {@link android.util.Size} of the width and height after
+     *      sampling.
      */
-    public Point getSampledSize(int sampleSize) {
+    @NonNull
+    public Size getSampledSize(int sampleSize) {
         if (sampleSize <= 0) {
             throw new IllegalArgumentException("sampleSize must be positive! "
                     + "provided " + sampleSize);
@@ -531,7 +598,7 @@
      *  @param width must be greater than 0.
      *  @param height must be greater than 0.
      */
-    public void resize(int width, int height) {
+    public void setResize(int width, int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -544,14 +611,14 @@
     /**
      *  Resize based on a sample size.
      *
-     *  This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #resize(int, int)}.
+     *  <p>This has the same effect as passing the result of
+     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
      */
-    public void resize(int sampleSize) {
-        Point dimensions = this.getSampledSize(sampleSize);
-        this.resize(dimensions.x, dimensions.y);
+    public void setResize(int sampleSize) {
+        Size size = this.getSampledSize(sampleSize);
+        this.setResize(size.getWidth(), size.getHeight());
     }
 
     private boolean requestedResize() {
@@ -567,7 +634,7 @@
      *  switch to software when HARDWARE is incompatible, e.g.
      *  {@link #setMutable}, {@link #setAsAlphaMask}.
      */
-    public static final int DEFAULT_ALLOCATOR = 0;
+    public static final int ALLOCATOR_DEFAULT = 0;
 
     /**
      *  Use a software allocation for the pixel memory.
@@ -575,28 +642,29 @@
      *  Useful for drawing to a software {@link Canvas} or for
      *  accessing the pixels on the final output.
      */
-    public static final int SOFTWARE_ALLOCATOR = 1;
+    public static final int ALLOCATOR_SOFTWARE = 1;
 
     /**
      *  Use shared memory for the pixel memory.
      *
      *  Useful for sharing across processes.
      */
-    public static final int SHARED_MEMORY_ALLOCATOR = 2;
+    public static final int ALLOCATOR_SHARED_MEMORY = 2;
 
     /**
      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
      *
-     *  This will throw an {@link java.lang.IllegalStateException} when combined
-     *  with incompatible options, like {@link #setMutable} or
-     *  {@link #setAsAlphaMask}.
+     *  When this is combined with incompatible options, like
+     *  {@link #setMutable} or {@link #setAsAlphaMask}, {@link #decodeDrawable}
+     *  / {@link #decodeBitmap} will throw an
+     *  {@link java.lang.IllegalStateException}.
      */
-    public static final int HARDWARE_ALLOCATOR = 3;
+    public static final int ALLOCATOR_HARDWARE = 3;
 
     /** @hide **/
     @Retention(SOURCE)
-    @IntDef({ DEFAULT_ALLOCATOR, SOFTWARE_ALLOCATOR, SHARED_MEMORY_ALLOCATOR,
-              HARDWARE_ALLOCATOR })
+    @IntDef({ ALLOCATOR_DEFAULT, ALLOCATOR_SOFTWARE, ALLOCATOR_SHARED_MEMORY,
+              ALLOCATOR_HARDWARE })
     public @interface Allocator {};
 
     /**
@@ -604,47 +672,46 @@
      *
      *  This is ignored for animated drawables.
      *
-     *  TODO: Allow accessing the backing from the Bitmap.
-     *
      *  @param allocator Type of allocator to use.
      */
     public void setAllocator(@Allocator int allocator) {
-        if (allocator < DEFAULT_ALLOCATOR || allocator > HARDWARE_ALLOCATOR) {
+        if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
             throw new IllegalArgumentException("invalid allocator " + allocator);
         }
         mAllocator = allocator;
     }
 
     /**
-     *  Create a {@link Bitmap} with unpremultiplied pixels.
+     *  Specify whether the {@link Bitmap} should have unpremultiplied pixels.
      *
      *  By default, ImageDecoder will create a {@link Bitmap} with
      *  premultiplied pixels, which is required for drawing with the
      *  {@link android.view.View} system (i.e. to a {@link Canvas}). Calling
-     *  this method will result in {@link #decodeBitmap} returning a
-     *  {@link Bitmap} with unpremultiplied pixels. See
-     *  {@link Bitmap#isPremultiplied}. Incompatible with
+     *  this method with a value of {@code true} will result in
+     *  {@link #decodeBitmap} returning a {@link Bitmap} with unpremultiplied
+     *  pixels. See {@link Bitmap#isPremultiplied}. This is incompatible with
      *  {@link #decodeDrawable}; attempting to decode an unpremultiplied
      *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
      */
-    public void requireUnpremultiplied() {
-        mRequireUnpremultiplied = true;
+    public void setRequireUnpremultiplied(boolean requireUnpremultiplied) {
+        mRequireUnpremultiplied = requireUnpremultiplied;
     }
 
     /**
      *  Modify the image after decoding and scaling.
      *
-     *  This allows adding effects prior to returning a {@link Drawable} or
+     *  <p>This allows adding effects prior to returning a {@link Drawable} or
      *  {@link Bitmap}. For a {@code Drawable} or an immutable {@code Bitmap},
-     *  this is the only way to process the image after decoding.
+     *  this is the only way to process the image after decoding.</p>
      *
-     *  If set on a nine-patch image, the nine-patch data is ignored.
+     *  <p>If set on a nine-patch image, the nine-patch data is ignored.</p>
      *
-     *  For an animated image, the drawing commands drawn on the {@link Canvas}
-     *  will be recorded immediately and then applied to each frame.
+     *  <p>For an animated image, the drawing commands drawn on the
+     *  {@link Canvas} will be recorded immediately and then applied to each
+     *  frame.</p>
      */
-    public void setPostProcess(PostProcess p) {
-        mPostProcess = p;
+    public void setPostProcessor(@Nullable PostProcessor p) {
+        mPostProcessor = p;
     }
 
     /**
@@ -653,66 +720,72 @@
      *  Will be called if there is an error in the input. Without one, a
      *  partial {@link Bitmap} will be created.
      */
-    public void setOnPartialImageListener(OnPartialImageListener l) {
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
         mOnPartialImageListener = l;
     }
 
     /**
      *  Crop the output to {@code subset} of the (possibly) scaled image.
      *
-     *  {@code subset} must be contained within the size set by {@link #resize}
-     *  or the bounds of the image if resize was not called. Otherwise an
-     *  {@link IllegalStateException} will be thrown.
+     *  <p>{@code subset} must be contained within the size set by
+     *  {@link #setResize} or the bounds of the image if setResize was not
+     *  called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
      *
-     *  NOT intended as a replacement for
+     *  <p>NOT intended as a replacement for
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
-     *  but merely crops the output.
+     *  but merely crops the output.</p>
      */
-    public void crop(Rect subset) {
+    public void setCrop(@Nullable Rect subset) {
         mCropRect = subset;
     }
 
     /**
-     *  Create a mutable {@link Bitmap}.
+     *  Specify whether the {@link Bitmap} should be mutable.
      *
-     *  By default, a {@link Bitmap} created will be immutable, but that can be
-     *  changed with this call.
+     *  <p>By default, a {@link Bitmap} created will be immutable, but that can
+     *  be changed with this call.</p>
      *
-     *  Incompatible with {@link #HARDWARE_ALLOCATOR}, because
-     *  {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable. Attempting to
-     *  combine them will throw an {@link java.lang.IllegalStateException}.
+     *  <p>Mutable Bitmaps are incompatible with {@link #ALLOCATOR_HARDWARE},
+     *  because {@link Bitmap.Config#HARDWARE} Bitmaps cannot be mutable.
+     *  Attempting to combine them will throw an
+     *  {@link java.lang.IllegalStateException}.</p>
      *
-     *  Incompatible with {@link #decodeDrawable}, which would require
-     *  retrieving the Bitmap from the returned Drawable in order to modify.
-     *  Attempting to decode a mutable {@link Drawable} will throw an
-     *  {@link java.lang.IllegalStateException}
+     *  <p>Mutable Bitmaps are also incompatible with {@link #decodeDrawable},
+     *  which would require retrieving the Bitmap from the returned Drawable in
+     *  order to modify. Attempting to decode a mutable {@link Drawable} will
+     *  throw an {@link java.lang.IllegalStateException}.</p>
      */
-    public void setMutable() {
-        mMutable = true;
+    public void setMutable(boolean mutable) {
+        mMutable = mutable;
     }
 
     /**
-     *  Potentially save RAM at the expense of quality.
+     *  Specify whether to potentially save RAM at the expense of quality.
      *
-     *  This may result in a {@link Bitmap} with a denser {@link Bitmap.Config},
-     *  depending on the image. For example, for an opaque {@link Bitmap}, this
-     *  may result in a {@link Bitmap.Config} with no alpha information.
+     *  Setting this to {@code true} may result in a {@link Bitmap} with a
+     *  denser {@link Bitmap.Config}, depending on the image. For example, for
+     *  an opaque {@link Bitmap}, this may result in a {@link Bitmap.Config}
+     *  with no alpha information.
      */
-    public void setPreferRamOverQuality() {
-        mPreferRamOverQuality = true;
+    public void setPreferRamOverQuality(boolean preferRamOverQuality) {
+        mPreferRamOverQuality = preferRamOverQuality;
     }
 
     /**
-     *  Potentially treat the output as an alpha mask.
+     *  Specify whether to potentially treat the output as an alpha mask.
      *
-     *  If the image is encoded in a format with only one channel, treat that
-     *  channel as alpha. Otherwise this call has no effect.
+     *  <p>If this is set to {@code true} and the image is encoded in a format
+     *  with only one channel, treat that channel as alpha. Otherwise this call has
+     *  no effect.</p>
      *
-     *  Incompatible with {@link #HARDWARE_ALLOCATOR}. Trying to combine them
-     *  will throw an {@link java.lang.IllegalStateException}.
+     *  <p>setAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+     *  combine them will result in {@link #decodeDrawable}/
+     *  {@link #decodeBitmap} throwing an
+     *  {@link java.lang.IllegalStateException}.</p>
      */
-    public void setAsAlphaMask() {
-        mAsAlphaMask = true;
+    public void setAsAlphaMask(boolean asAlphaMask) {
+        mAsAlphaMask = asAlphaMask;
     }
 
     @Override
@@ -739,7 +812,7 @@
 
         checkSubset(mDesiredWidth, mDesiredHeight, mCropRect);
 
-        if (mAllocator == HARDWARE_ALLOCATOR) {
+        if (mAllocator == ALLOCATOR_HARDWARE) {
             if (mMutable) {
                 throw new IllegalStateException("Cannot make mutable HARDWARE Bitmap!");
             }
@@ -748,7 +821,7 @@
             }
         }
 
-        if (mPostProcess != null && mRequireUnpremultiplied) {
+        if (mPostProcessor != null && mRequireUnpremultiplied) {
             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
         }
     }
@@ -763,18 +836,34 @@
         }
     }
 
+    @NonNull
     private Bitmap decodeBitmap() throws IOException {
         checkState();
-        // nDecodeBitmap calls postProcessAndRelease only if mPostProcess
+        // nDecodeBitmap calls onPartialImage only if mOnPartialImageListener
+        // exists
+        ImageDecoder partialImagePtr = mOnPartialImageListener == null ? null : this;
+        // nDecodeBitmap calls postProcessAndRelease only if mPostProcessor
         // exists.
-        ImageDecoder postProcessPtr = mPostProcess == null ? null : this;
-        return nDecodeBitmap(mNativePtr, mOnPartialImageListener,
+        ImageDecoder postProcessPtr = mPostProcessor == null ? null : this;
+        return nDecodeBitmap(mNativePtr, partialImagePtr,
                 postProcessPtr, mDesiredWidth, mDesiredHeight, mCropRect,
                 mMutable, mAllocator, mRequireUnpremultiplied,
                 mPreferRamOverQuality, mAsAlphaMask);
 
     }
 
+    private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
+            @NonNull Source src) {
+        if (listener != null) {
+            ImageInfo info = new ImageInfo(this);
+            try {
+                listener.onHeaderDecoded(this, info, src);
+            } finally {
+                info.mDecoder = null;
+            }
+        }
+    }
+
     /**
      *  Create a {@link Drawable} from a {@code Source}.
      *
@@ -791,14 +880,8 @@
     public static Drawable decodeDrawable(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
         try (ImageDecoder decoder = src.createImageDecoder()) {
-            if (listener != null) {
-                ImageInfo info = new ImageInfo(decoder);
-                try {
-                    listener.onHeaderDecoded(info, decoder);
-                } finally {
-                    info.decoder = null;
-                }
-            }
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
 
             if (decoder.mRequireUnpremultiplied) {
                 // Though this could be supported (ignored) for opaque images,
@@ -817,8 +900,8 @@
             final int srcDensity = computeDensity(src, decoder);
             if (decoder.mAnimated) {
                 // AnimatedImageDrawable calls postProcessAndRelease only if
-                // mPostProcess exists.
-                ImageDecoder postProcessPtr = decoder.mPostProcess == null ?
+                // mPostProcessor exists.
+                ImageDecoder postProcessPtr = decoder.mPostProcessor == null ?
                         null : decoder;
                 Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
                         postProcessPtr, decoder.mDesiredWidth,
@@ -878,14 +961,8 @@
     public static Bitmap decodeBitmap(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
         try (ImageDecoder decoder = src.createImageDecoder()) {
-            if (listener != null) {
-                ImageInfo info = new ImageInfo(decoder);
-                try {
-                    listener.onHeaderDecoded(info, decoder);
-                } finally {
-                    info.decoder = null;
-                }
-            }
+            decoder.mSource = src;
+            decoder.callHeaderDecoded(listener, src);
 
             // this call potentially manipulates the decoder so it must be performed prior to
             // decoding the bitmap
@@ -922,13 +999,14 @@
             float scale = (float) dstDensity / srcDensity;
             int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
             int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-            decoder.resize(scaledWidth, scaledHeight);
+            decoder.setResize(scaledWidth, scaledHeight);
             return dstDensity;
         }
 
         return srcDensity;
     }
 
+    @NonNull
     private String getMimeType() {
         return nGetMimeType(mNativePtr);
     }
@@ -945,14 +1023,22 @@
      * Private method called by JNI.
      */
     @SuppressWarnings("unused")
-    private int postProcessAndRelease(@NonNull Canvas canvas, int width, int height) {
+    private int postProcessAndRelease(@NonNull Canvas canvas) {
         try {
-            return mPostProcess.postProcess(canvas, width, height);
+            return mPostProcessor.onPostProcess(canvas);
         } finally {
             canvas.release();
         }
     }
 
+    /**
+     * Private method called by JNI.
+     */
+    @SuppressWarnings("unused")
+    private boolean onPartialImage(@Error int error) {
+        return mOnPartialImageListener.onPartialImage(error, mSource);
+    }
+
     private static native ImageDecoder nCreate(long asset) throws IOException;
     private static native ImageDecoder nCreate(ByteBuffer buffer,
                                                int position,
@@ -960,19 +1046,20 @@
     private static native ImageDecoder nCreate(byte[] data, int offset,
                                                int length) throws IOException;
     private static native ImageDecoder nCreate(InputStream is, byte[] storage);
+    // The fd must be seekable.
     private static native ImageDecoder nCreate(FileDescriptor fd) throws IOException;
     @NonNull
     private static native Bitmap nDecodeBitmap(long nativePtr,
-            OnPartialImageListener listener,
-            @Nullable ImageDecoder decoder,     // Only used if mPostProcess != null
+            @Nullable ImageDecoder partialImageListener,
+            @Nullable ImageDecoder postProcessor,
             int width, int height,
-            Rect cropRect, boolean mutable,
+            @Nullable Rect cropRect, boolean mutable,
             int allocator, boolean requireUnpremul,
             boolean preferRamOverQuality, boolean asAlphaMask)
         throws IOException;
-    private static native Point nGetSampledSize(long nativePtr,
-                                                int sampleSize);
-    private static native void nGetPadding(long nativePtr, Rect outRect);
+    private static native Size nGetSampledSize(long nativePtr,
+                                               int sampleSize);
+    private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
     private static native void nClose(long nativePtr);
     private static native String nGetMimeType(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/PostProcess.java b/graphics/java/android/graphics/PostProcessor.java
similarity index 78%
rename from graphics/java/android/graphics/PostProcess.java
rename to graphics/java/android/graphics/PostProcessor.java
index c5a31e8..b1712e9 100644
--- a/graphics/java/android/graphics/PostProcess.java
+++ b/graphics/java/android/graphics/PostProcessor.java
@@ -20,38 +20,38 @@
 import android.annotation.NonNull;
 import android.graphics.drawable.Drawable;
 
-
 /**
  *  Helper interface for adding custom processing to an image.
  *
- *  The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
+ *  <p>The image being processed may be a {@link Drawable}, {@link Bitmap} or frame
  *  of an animated image produced by {@link ImageDecoder}. This is called before
- *  the requested object is returned.
+ *  the requested object is returned.</p>
  *
- *  This custom processing also applies to image types that are otherwise
- *  immutable, such as {@link Bitmap.Config#HARDWARE}.
+ *  <p>This custom processing also applies to image types that are otherwise
+ *  immutable, such as {@link Bitmap.Config#HARDWARE}.</p>
  *
- *  On an animated image, the callback will only be called once, but the drawing
+ *  <p>On an animated image, the callback will only be called once, but the drawing
  *  commands will be applied to each frame, as if the {@code Canvas} had been
- *  returned by {@link Picture#beginRecording}.
+ *  returned by {@link Picture#beginRecording}.<p>
  *
- *  Supplied to ImageDecoder via {@link ImageDecoder#setPostProcess}.
- *  @hide
+ *  <p>Supplied to ImageDecoder via {@link ImageDecoder#setPostProcessor}.</p>
  */
-public interface PostProcess {
+public interface PostProcessor {
     /**
      *  Do any processing after (for example) decoding.
      *
-     *  Drawing to the {@link Canvas} will behave as if the initial processing
+     *  <p>Drawing to the {@link Canvas} will behave as if the initial processing
      *  (e.g. decoding) already exists in the Canvas. An implementation can draw
      *  effects on top of this, or it can even draw behind it using
      *  {@link PorterDuff.Mode#DST_OVER}. A common effect is to add transparency
      *  to the corners to achieve rounded corners. That can be done with the
-     *  following code:
+     *  following code:</p>
      *
      *  <code>
      *      Path path = new Path();
      *      path.setFillType(Path.FillType.INVERSE_EVEN_ODD);
+     *      int width = canvas.getWidth();
+     *      int height = canvas.getHeight();
      *      path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW);
      *      Paint paint = new Paint();
      *      paint.setAntiAlias(true);
@@ -63,10 +63,6 @@
      *
      *
      *  @param canvas The {@link Canvas} to draw to.
-     *  @param width Width of {@code canvas}. Anything drawn outside of this
-     *      will be ignored.
-     *  @param height Height of {@code canvas}. Anything drawn outside of this
-     *      will be ignored.
      *  @return Opacity of the result after drawing.
      *      {@link PixelFormat#UNKNOWN} means that the implementation did not
      *      change whether the image has alpha. Return this unless you added
@@ -87,5 +83,5 @@
      *      {@link java.lang.IllegalArgumentException}.
      */
     @PixelFormat.Opacity
-    public int postProcess(@NonNull Canvas canvas, int width, int height);
+    public int onPostProcess(@NonNull Canvas canvas);
 }
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index f74c39d..3a5f7b7 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -165,8 +165,8 @@
         Bitmap bitmap = null;
         try (FileInputStream stream = new FileInputStream(filepath)) {
             bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, stream),
-                    (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (Exception e) {
             /*  do nothing. This matches the behavior of BitmapFactory.decodeFile()
@@ -198,8 +198,8 @@
         Bitmap bitmap = null;
         try {
             bitmap = ImageDecoder.decodeBitmap(ImageDecoder.createSource(res, is),
-                    (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                    (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (Exception e) {
             /*  do nothing. This matches the behavior of BitmapFactory.decodeStream()
@@ -838,8 +838,8 @@
             Bitmap bitmap = null;
             try (InputStream is = r.openRawResource(srcResId, value)) {
                 ImageDecoder.Source source = ImageDecoder.createSource(r, is, density);
-                bitmap = ImageDecoder.decodeBitmap(source, (info, decoder) -> {
-                    decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+                bitmap = ImageDecoder.decodeBitmap(source, (decoder, info, src) -> {
+                    decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
                 });
             } catch (Exception e) {
                 // Do nothing and pick up the error below.
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 291b0a0..36a4d26 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1229,8 +1229,8 @@
                 source = ImageDecoder.createSource(res, is);
             }
 
-            return ImageDecoder.decodeDrawable(source, (info, decoder) -> {
-                decoder.setAllocator(ImageDecoder.SOFTWARE_ALLOCATOR);
+            return ImageDecoder.decodeDrawable(source, (decoder, info, src) -> {
+                decoder.setAllocator(ImageDecoder.ALLOCATOR_SOFTWARE);
             });
         } catch (IOException e) {
             /*  do nothing.
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1690e8c..e25386b 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -510,6 +510,19 @@
         return importKey(alias, args, format, keyData, UID_SELF, flags, outCharacteristics);
     }
 
+    public int importWrappedKey(String wrappedKeyAlias, byte[] wrappedKey,
+            String wrappingKeyAlias,
+            byte[] maskingKey, KeymasterArguments args, long rootSid, long fingerprintSid, int uid,
+            KeyCharacteristics outCharacteristics) {
+        try {
+            return mBinder.importWrappedKey(wrappedKeyAlias, wrappedKey, wrappingKeyAlias,
+                    maskingKey, args, rootSid, fingerprintSid, outCharacteristics);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return SYSTEM_ERROR;
+        }
+    }
+
     public ExportResult exportKey(String alias, int format, KeymasterBlob clientId,
             KeymasterBlob appId, int uid) {
         try {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java
new file mode 100644
index 0000000..01fd062
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStore3DESCipherSpi.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2018 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.security.keystore;
+
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.IvParameterSpec;
+
+/**
+ * Base class for Android Keystore 3DES {@link CipherSpi} implementations.
+ *
+ * @hide
+ */
+public class AndroidKeyStore3DESCipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+    private static final int BLOCK_SIZE_BYTES = 8;
+
+    private final int mKeymasterBlockMode;
+    private final int mKeymasterPadding;
+    /** Whether this transformation requires an IV. */
+    private final boolean mIvRequired;
+
+    private byte[] mIv;
+
+    /** Whether the current {@code #mIv} has been used by the underlying crypto operation. */
+    private boolean mIvHasBeenUsed;
+
+    AndroidKeyStore3DESCipherSpi(
+            int keymasterBlockMode,
+            int keymasterPadding,
+            boolean ivRequired) {
+        mKeymasterBlockMode = keymasterBlockMode;
+        mKeymasterPadding = keymasterPadding;
+        mIvRequired = ivRequired;
+    }
+
+    abstract static class ECB extends AndroidKeyStore3DESCipherSpi {
+        protected ECB(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_ECB, keymasterPadding, false);
+        }
+
+        public static class NoPadding extends ECB {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends ECB {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    abstract static class CBC extends AndroidKeyStore3DESCipherSpi {
+        protected CBC(int keymasterPadding) {
+            super(KeymasterDefs.KM_MODE_CBC, keymasterPadding, true);
+        }
+
+        public static class NoPadding extends CBC {
+            public NoPadding() {
+                super(KeymasterDefs.KM_PAD_NONE);
+            }
+        }
+
+        public static class PKCS7Padding extends CBC {
+            public PKCS7Padding() {
+                super(KeymasterDefs.KM_PAD_PKCS7);
+            }
+        }
+    }
+
+    @Override
+    protected void initKey(int i, Key key) throws InvalidKeyException {
+        if (!(key instanceof AndroidKeyStoreSecretKey)) {
+            throw new InvalidKeyException(
+                    "Unsupported key: " + ((key != null) ? key.getClass().getName() : "null"));
+        }
+        if (!KeyProperties.KEY_ALGORITHM_3DES.equalsIgnoreCase(key.getAlgorithm())) {
+            throw new InvalidKeyException(
+                    "Unsupported key algorithm: " + key.getAlgorithm() + ". Only " +
+                            KeyProperties.KEY_ALGORITHM_3DES + " supported");
+        }
+        setKey((AndroidKeyStoreSecretKey) key);
+    }
+
+    @Override
+    protected int engineGetBlockSize() {
+        return BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected int engineGetOutputSize(int inputLen) {
+        return inputLen + 3 * BLOCK_SIZE_BYTES;
+    }
+
+    @Override
+    protected final byte[] engineGetIV() {
+        return ArrayUtils.cloneIfNotEmpty(mIv);
+    }
+
+    @Override
+    protected AlgorithmParameters engineGetParameters() {
+        if (!mIvRequired) {
+            return null;
+        }
+        if ((mIv != null) && (mIv.length > 0)) {
+            try {
+                AlgorithmParameters params = AlgorithmParameters.getInstance("DESede");
+                params.init(new IvParameterSpec(mIv));
+                return params;
+            } catch (NoSuchAlgorithmException e) {
+                throw new ProviderException(
+                        "Failed to obtain 3DES AlgorithmParameters", e);
+            } catch (InvalidParameterSpecException e) {
+                throw new ProviderException(
+                        "Failed to initialize 3DES AlgorithmParameters with an IV",
+                        e);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters() throws InvalidKeyException {
+        if (!mIvRequired) {
+            return;
+        }
+
+        // IV is used
+        if (!isEncrypting()) {
+            throw new InvalidKeyException("IV required when decrypting"
+                    + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+        }
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters(AlgorithmParameterSpec params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException(
+                        "IvParameterSpec must be provided when decrypting");
+            }
+            return;
+        }
+        if (!(params instanceof IvParameterSpec)) {
+            throw new InvalidAlgorithmParameterException("Only IvParameterSpec supported");
+        }
+        mIv = ((IvParameterSpec) params).getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in IvParameterSpec");
+        }
+    }
+
+    @Override
+    protected void initAlgorithmSpecificParameters(AlgorithmParameters params)
+            throws InvalidAlgorithmParameterException {
+        if (!mIvRequired) {
+            if (params != null) {
+                throw new InvalidAlgorithmParameterException("Unsupported parameters: " + params);
+            }
+            return;
+        }
+
+        // IV is used
+        if (params == null) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ". Use IvParameterSpec or AlgorithmParameters to provide it.");
+            }
+            return;
+        }
+
+        if (!"DESede".equalsIgnoreCase(params.getAlgorithm())) {
+            throw new InvalidAlgorithmParameterException(
+                    "Unsupported AlgorithmParameters algorithm: " + params.getAlgorithm()
+                            + ". Supported: DESede");
+        }
+
+        IvParameterSpec ivSpec;
+        try {
+            ivSpec = params.getParameterSpec(IvParameterSpec.class);
+        } catch (InvalidParameterSpecException e) {
+            if (!isEncrypting()) {
+                // IV must be provided by the caller
+                throw new InvalidAlgorithmParameterException("IV required when decrypting"
+                        + ", but not found in parameters: " + params, e);
+            }
+            mIv = null;
+            return;
+        }
+        mIv = ivSpec.getIV();
+        if (mIv == null) {
+            throw new InvalidAlgorithmParameterException("Null IV in AlgorithmParameters");
+        }
+    }
+
+    @Override
+    protected final int getAdditionalEntropyAmountForBegin() {
+        if ((mIvRequired) && (mIv == null) && (isEncrypting())) {
+            // IV will need to be generated
+            return BLOCK_SIZE_BYTES;
+        }
+
+        return 0;
+    }
+
+    @Override
+    protected int getAdditionalEntropyAmountForFinish() {
+        return 0;
+    }
+
+    @Override
+    protected void addAlgorithmSpecificParametersToBegin(KeymasterArguments keymasterArgs) {
+        if ((isEncrypting()) && (mIvRequired) && (mIvHasBeenUsed)) {
+            // IV is being reused for encryption: this violates security best practices.
+            throw new IllegalStateException(
+                    "IV has already been used. Reusing IV in encryption mode violates security best"
+                            + " practices.");
+        }
+
+        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_3DES);
+        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockMode);
+        keymasterArgs.addEnum(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+        if ((mIvRequired) && (mIv != null)) {
+            keymasterArgs.addBytes(KeymasterDefs.KM_TAG_NONCE, mIv);
+        }
+    }
+
+    @Override
+    protected void loadAlgorithmSpecificParametersFromBeginResult(
+            KeymasterArguments keymasterArgs) {
+        mIvHasBeenUsed = true;
+
+        // NOTE: Keymaster doesn't always return an IV, even if it's used.
+        byte[] returnedIv = keymasterArgs.getBytes(KeymasterDefs.KM_TAG_NONCE, null);
+        if ((returnedIv != null) && (returnedIv.length == 0)) {
+            returnedIv = null;
+        }
+
+        if (mIvRequired) {
+            if (mIv == null) {
+                mIv = returnedIv;
+            } else if ((returnedIv != null) && (!Arrays.equals(returnedIv, mIv))) {
+                throw new ProviderException("IV in use differs from provided IV");
+            }
+        } else {
+            if (returnedIv != null) {
+                throw new ProviderException(
+                        "IV in use despite IV not being used by this transformation");
+            }
+        }
+    }
+
+    @Override
+    protected final void resetAll() {
+        mIv = null;
+        mIvHasBeenUsed = false;
+        super.resetAll();
+    }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index be390ff..e4cf84a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -93,6 +93,16 @@
         putSymmetricCipherImpl("AES/CTR/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
 
+        putSymmetricCipherImpl("DESede/CBC/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$NoPadding");
+        putSymmetricCipherImpl("DESede/CBC/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$CBC$PKCS7Padding");
+
+        putSymmetricCipherImpl("DESede/ECB/NoPadding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$NoPadding");
+        putSymmetricCipherImpl("DESede/ECB/PKCS7Padding",
+                PACKAGE_NAME + ".AndroidKeyStore3DESCipherSpi$ECB$PKCS7Padding");
+
         putSymmetricCipherImpl("AES/GCM/NoPadding",
                 PACKAGE_NAME + ".AndroidKeyStoreAuthenticatedAESCipherSpi$GCM$NoPadding");
 
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index fdebf37..5bcb34a 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -307,7 +307,7 @@
      *
      * <p>This implementation returns {@code null}.
      *
-     * @returns stream or {@code null} if AAD is not supported by this cipher.
+     * @return stream or {@code null} if AAD is not supported by this cipher.
      */
     @Nullable
     protected KeyStoreCryptoOperationStreamer createAdditionalAuthenticationDataStreamer(
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index f1d1e16..379e177 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -60,6 +60,12 @@
         }
     }
 
+    public static class DESede extends AndroidKeyStoreKeyGeneratorSpi {
+        public DESede() {
+            super(KeymasterDefs.KM_ALGORITHM_3DES, 168);
+        }
+    }
+
     protected static abstract class HmacBase extends AndroidKeyStoreKeyGeneratorSpi {
         protected HmacBase(int keymasterDigest) {
             super(KeymasterDefs.KM_ALGORITHM_HMAC,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index 55e6519..1018926 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -80,6 +80,7 @@
 
         // javax.crypto.KeyGenerator
         put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
+        put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
         put("KeyGenerator.HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA1");
         put("KeyGenerator.HmacSHA224", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA224");
         put("KeyGenerator.HmacSHA256", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$HmacSHA256");
@@ -88,6 +89,7 @@
 
         // java.security.SecretKeyFactory
         putSecretKeyFactoryImpl("AES");
+        putSecretKeyFactoryImpl("DESede");
         putSecretKeyFactoryImpl("HmacSHA1");
         putSecretKeyFactoryImpl("HmacSHA224");
         putSecretKeyFactoryImpl("HmacSHA256");
@@ -348,7 +350,8 @@
         }
 
         if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
-                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) {
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
             return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
                     keyCharacteristics);
         } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index d73a9e2..440e086 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -18,6 +18,7 @@
 
 import libcore.util.EmptyArray;
 import android.security.Credentials;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.security.KeyStoreParameter;
 import android.security.keymaster.KeyCharacteristics;
@@ -25,6 +26,7 @@
 import android.security.keymaster.KeymasterDefs;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
+import android.security.keystore.WrappedKeyEntry;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -744,6 +746,31 @@
         }
     }
 
+    private void setWrappedKeyEntry(String alias, byte[] wrappedKeyBytes, String wrappingKeyAlias,
+            java.security.KeyStore.ProtectionParameter param) throws KeyStoreException {
+        if (param != null) {
+            throw new KeyStoreException("Protection parameters are specified inside wrapped keys");
+        }
+
+        byte[] maskingKey = new byte[32];
+        KeymasterArguments args = new KeymasterArguments(); // TODO: populate wrapping key args.
+
+        int errorCode = mKeyStore.importWrappedKey(
+            Credentials.USER_SECRET_KEY + alias,
+            wrappedKeyBytes,
+            Credentials.USER_PRIVATE_KEY + wrappingKeyAlias,
+            maskingKey,
+            args,
+            GateKeeper.getSecureUserId(),
+            0, // FIXME fingerprint id?
+            mUid,
+            new KeyCharacteristics());
+        if (errorCode != KeyStore.NO_ERROR) {
+            throw new KeyStoreException("Failed to import wrapped key. Keystore error code: "
+                + errorCode);
+        }
+    }
+
     @Override
     public void engineSetKeyEntry(String alias, byte[] userKey, Certificate[] chain)
             throws KeyStoreException {
@@ -974,6 +1001,9 @@
         } else if (entry instanceof SecretKeyEntry) {
             SecretKeyEntry secE = (SecretKeyEntry) entry;
             setSecretKeyEntry(alias, secE.getSecretKey(), param);
+        } else if (entry instanceof WrappedKeyEntry) {
+            WrappedKeyEntry wke = (WrappedKeyEntry) entry;
+            setWrappedKeyEntry(alias, wke.getWrappedKeyBytes(), wke.getWrappingKeyAlias(), param);
         } else {
             throw new KeyStoreException(
                     "Entry must be a PrivateKeyEntry, SecretKeyEntry or TrustedCertificateEntry"
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1238d87..1e2b873 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -262,6 +262,7 @@
     private final boolean mUniqueIdIncluded;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
+    private final boolean mIsStrongBoxBacked;
 
     /**
      * @hide should be built with Builder
@@ -289,7 +290,8 @@
             byte[] attestationChallenge,
             boolean uniqueIdIncluded,
             boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment) {
+            boolean invalidatedByBiometricEnrollment,
+            boolean isStrongBoxBacked) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -335,6 +337,7 @@
         mUniqueIdIncluded = uniqueIdIncluded;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
+        mIsStrongBoxBacked = isStrongBoxBacked;
     }
 
     /**
@@ -625,6 +628,13 @@
     }
 
     /**
+     * Returns {@code true} if the key is protected by a Strongbox security chip.
+     */
+    public boolean isStrongBoxBacked() {
+        return mIsStrongBoxBacked;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -652,6 +662,7 @@
         private boolean mUniqueIdIncluded = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
+        private boolean mIsStrongBoxBacked = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1177,6 +1188,15 @@
         }
 
         /**
+         * Sets whether this key should be protected by a StrongBox security chip.
+         */
+        @NonNull
+        public Builder setIsStrongBoxBacked(boolean isStrongBoxBacked) {
+            mIsStrongBoxBacked = isStrongBoxBacked;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1204,7 +1224,8 @@
                     mAttestationChallenge,
                     mUniqueIdIncluded,
                     mUserAuthenticationValidWhileOnBody,
-                    mInvalidatedByBiometricEnrollment);
+                    mInvalidatedByBiometricEnrollment,
+                    mIsStrongBoxBacked);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index a250d1f0..f54b6de 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -44,6 +44,7 @@
             PURPOSE_DECRYPT,
             PURPOSE_SIGN,
             PURPOSE_VERIFY,
+            PURPOSE_WRAP_KEY,
     })
     public @interface PurposeEnum {}
 
@@ -68,6 +69,11 @@
     public static final int PURPOSE_VERIFY = 1 << 3;
 
     /**
+     * Purpose of key: wrapping and unwrapping wrapped keys for secure import.
+     */
+    public static final int PURPOSE_WRAP_KEY = 1 << 5;
+
+    /**
      * @hide
      */
     public static abstract class Purpose {
@@ -83,6 +89,8 @@
                     return KeymasterDefs.KM_PURPOSE_SIGN;
                 case PURPOSE_VERIFY:
                     return KeymasterDefs.KM_PURPOSE_VERIFY;
+                case PURPOSE_WRAP_KEY:
+                    return KeymasterDefs.KM_PURPOSE_WRAP;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -98,6 +106,8 @@
                     return PURPOSE_SIGN;
                 case KeymasterDefs.KM_PURPOSE_VERIFY:
                     return PURPOSE_VERIFY;
+                case KeymasterDefs.KM_PURPOSE_WRAP:
+                    return PURPOSE_WRAP_KEY;
                 default:
                     throw new IllegalArgumentException("Unknown purpose: " + purpose);
             }
@@ -146,6 +156,15 @@
     /** Advanced Encryption Standard (AES) key. */
     public static final String KEY_ALGORITHM_AES = "AES";
 
+    /**
+     * Triple Data Encryption Algorithm (3DES) key.
+     *
+     * @deprecated Included for interoperability with legacy systems. Prefer {@link
+     * KeyProperties#KEY_ALGORITHM_AES} for new development.
+     */
+    @Deprecated
+    public static final String KEY_ALGORITHM_3DES = "DESede";
+
     /** Keyed-Hash Message Authentication Code (HMAC) key using SHA-1 as the hash. */
     public static final String KEY_ALGORITHM_HMAC_SHA1 = "HmacSHA1";
 
@@ -196,6 +215,8 @@
                 @NonNull @KeyAlgorithmEnum String algorithm) {
             if (KEY_ALGORITHM_AES.equalsIgnoreCase(algorithm)) {
                 return KeymasterDefs.KM_ALGORITHM_AES;
+            } else if (KEY_ALGORITHM_3DES.equalsIgnoreCase(algorithm)) {
+                return KeymasterDefs.KM_ALGORITHM_3DES;
             } else if (algorithm.toUpperCase(Locale.US).startsWith("HMAC")) {
                 return KeymasterDefs.KM_ALGORITHM_HMAC;
             } else {
@@ -210,6 +231,8 @@
             switch (keymasterAlgorithm) {
                 case KeymasterDefs.KM_ALGORITHM_AES:
                     return KEY_ALGORITHM_AES;
+                case KeymasterDefs.KM_ALGORITHM_3DES:
+                    return KEY_ALGORITHM_3DES;
                 case KeymasterDefs.KM_ALGORITHM_HMAC:
                     switch (keymasterDigest) {
                         case KeymasterDefs.KM_DIGEST_SHA1:
@@ -666,6 +689,10 @@
      */
     public static final int ORIGIN_UNKNOWN = 1 << 2;
 
+    /** Key was imported into the AndroidKeyStore in an encrypted wrapper */
+    public static final int ORIGIN_SECURELY_IMPORTED = 1 << 3;
+
+
     /**
      * @hide
      */
@@ -680,6 +707,8 @@
                     return ORIGIN_IMPORTED;
                 case KeymasterDefs.KM_ORIGIN_UNKNOWN:
                     return ORIGIN_UNKNOWN;
+                case KeymasterDefs.KM_ORIGIN_SECURELY_IMPORTED:
+                    return ORIGIN_SECURELY_IMPORTED;
                 default:
                     throw new IllegalArgumentException("Unknown origin: " + origin);
             }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 2eb0663..dbacb9c 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -488,9 +488,9 @@
         private int mUserAuthenticationValidityDurationSeconds = -1;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
-
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
+
         /**
          * Creates a new instance of the {@code Builder}.
          *
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java
similarity index 72%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to keystore/java/android/security/keystore/StrongBoxUnavailableException.java
index b35713f..ad41a58 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/keystore/java/android/security/keystore/StrongBoxUnavailableException.java
@@ -16,5 +16,13 @@
 
 package android.security.keystore;
 
-/* @hide */
-parcelable KeychainSnapshot;
+import java.security.ProviderException;
+
+/**
+ * Indicates that an operation could not be performed because the requested security hardware
+ * is not available.
+ */
+public class StrongBoxUnavailableException extends ProviderException {
+
+}
+
diff --git a/keystore/java/android/security/keystore/WrappedKeyEntry.java b/keystore/java/android/security/keystore/WrappedKeyEntry.java
new file mode 100644
index 0000000..a8f4afe
--- /dev/null
+++ b/keystore/java/android/security/keystore/WrappedKeyEntry.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 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.security.keystore;
+
+import java.security.KeyStore.Entry;
+import java.security.spec.AlgorithmParameterSpec;
+
+/**
+ * An {@link Entry} that holds a wrapped key.
+ */
+public class WrappedKeyEntry implements Entry {
+
+    private final byte[] mWrappedKeyBytes;
+    private final String mWrappingKeyAlias;
+    private final String mTransformation;
+    private final AlgorithmParameterSpec mAlgorithmParameterSpec;
+
+    public WrappedKeyEntry(byte[] wrappedKeyBytes, String wrappingKeyAlias, String transformation,
+            AlgorithmParameterSpec algorithmParameterSpec) {
+        mWrappedKeyBytes = wrappedKeyBytes;
+        mWrappingKeyAlias = wrappingKeyAlias;
+        mTransformation = transformation;
+        mAlgorithmParameterSpec = algorithmParameterSpec;
+    }
+
+    public byte[] getWrappedKeyBytes() {
+        return mWrappedKeyBytes;
+    }
+
+    public String getWrappingKeyAlias() {
+        return mWrappingKeyAlias;
+    }
+
+    public String getTransformation() {
+        return mTransformation;
+    }
+
+    public AlgorithmParameterSpec getAlgorithmParameterSpec() {
+        return mAlgorithmParameterSpec;
+    }
+}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 5b95c81..696a00c 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1877,19 +1877,27 @@
         return (l.locale > r.locale) ? 1 : -1;
     }
 
-    // The language & region are equal, so compare the scripts and variants.
+    // The language & region are equal, so compare the scripts, variants and
+    // numbering systms in this order. Comparison of variants and numbering
+    // systems should happen very infrequently (if at all.)
+    // The comparison code relies on memcmp low-level optimizations that make it
+    // more efficient than strncmp.
     const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
     const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
     const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
+
     int script = memcmp(lScript, rScript, sizeof(l.localeScript));
     if (script) {
         return script;
     }
 
-    // The language, region and script are equal, so compare variants.
-    //
-    // This should happen very infrequently (if at all.)
-    return memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
+    int variant = memcmp(l.localeVariant, r.localeVariant, sizeof(l.localeVariant));
+    if (variant) {
+        return variant;
+    }
+
+    return memcmp(l.localeNumberingSystem, r.localeNumberingSystem,
+                  sizeof(l.localeNumberingSystem));
 }
 
 int ResTable_config::compare(const ResTable_config& o) const {
@@ -2030,6 +2038,22 @@
     return diffs;
 }
 
+// There isn't a well specified "importance" order between variants and
+// scripts. We can't easily tell whether, say "en-Latn-US" is more or less
+// specific than "en-US-POSIX".
+//
+// We therefore arbitrarily decide to give priority to variants over
+// scripts since it seems more useful to do so. We will consider
+// "en-US-POSIX" to be more specific than "en-Latn-US".
+//
+// Unicode extension keywords are considered to be less important than
+// scripts and variants.
+inline int ResTable_config::getImportanceScoreOfLocale() const {
+  return (localeVariant[0] ? 4 : 0)
+      + (localeScript[0] && !localeScriptWasComputed ? 2: 0)
+      + (localeNumberingSystem[0] ? 1: 0);
+}
+
 int ResTable_config::isLocaleMoreSpecificThan(const ResTable_config& o) const {
     if (locale || o.locale) {
         if (language[0] != o.language[0]) {
@@ -2043,21 +2067,7 @@
         }
     }
 
-    // There isn't a well specified "importance" order between variants and
-    // scripts. We can't easily tell whether, say "en-Latn-US" is more or less
-    // specific than "en-US-POSIX".
-    //
-    // We therefore arbitrarily decide to give priority to variants over
-    // scripts since it seems more useful to do so. We will consider
-    // "en-US-POSIX" to be more specific than "en-Latn-US".
-
-    const int score = ((localeScript[0] != '\0' && !localeScriptWasComputed) ? 1 : 0) +
-        ((localeVariant[0] != '\0') ? 2 : 0);
-
-    const int oScore = (o.localeScript[0] != '\0' && !o.localeScriptWasComputed ? 1 : 0) +
-        ((o.localeVariant[0] != '\0') ? 2 : 0);
-
-    return score - oScore;
+    return getImportanceScoreOfLocale() - o.getImportanceScoreOfLocale();
 }
 
 bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const {
@@ -2314,6 +2324,17 @@
         return localeMatches;
     }
 
+    // The variants are the same, try numbering system.
+    const bool localeNumsysMatches = strncmp(localeNumberingSystem,
+                                             requested->localeNumberingSystem,
+                                             sizeof(localeNumberingSystem)) == 0;
+    const bool otherNumsysMatches = strncmp(o.localeNumberingSystem,
+                                            requested->localeNumberingSystem,
+                                            sizeof(localeNumberingSystem)) == 0;
+    if (localeNumsysMatches != otherNumsysMatches) {
+        return localeNumsysMatches;
+    }
+
     // Finally, the languages, although equivalent, may still be different
     // (like for Tagalog and Filipino). Identical is better than just
     // equivalent.
@@ -2781,7 +2802,7 @@
         return;
     }
     const bool scriptWasProvided = localeScript[0] != '\0' && !localeScriptWasComputed;
-    if (!scriptWasProvided && !localeVariant[0]) {
+    if (!scriptWasProvided && !localeVariant[0] && !localeNumberingSystem[0]) {
         // Legacy format.
         if (out.size() > 0) {
             out.append("-");
@@ -2826,6 +2847,12 @@
         out.append("+");
         out.append(localeVariant, strnlen(localeVariant, sizeof(localeVariant)));
     }
+
+    if (localeNumberingSystem[0]) {
+        out.append("+u+nu+");
+        out.append(localeNumberingSystem,
+                   strnlen(localeNumberingSystem, sizeof(localeNumberingSystem)));
+    }
 }
 
 void ResTable_config::getBcp47Locale(char str[RESTABLE_MAX_LOCALE_LEN], bool canonicalize) const {
@@ -2868,10 +2895,17 @@
             str[charsWritten++] = '-';
         }
         memcpy(str + charsWritten, localeVariant, sizeof(localeVariant));
+        charsWritten += strnlen(str + charsWritten, sizeof(localeVariant));
     }
 
-    /* TODO: Add BCP47 extension. It requires RESTABLE_MAX_LOCALE_LEN
-     * increase from 28 to 42 bytes (-u-nu-xxxxxxxx) */
+    // Add Unicode extension only if at least one other locale component is present
+    if (localeNumberingSystem[0] != '\0' && charsWritten > 0) {
+        static constexpr char NU_PREFIX[] = "-u-nu-";
+        static constexpr size_t NU_PREFIX_LEN = sizeof(NU_PREFIX) - 1;
+        memcpy(str + charsWritten, NU_PREFIX, NU_PREFIX_LEN);
+        charsWritten += NU_PREFIX_LEN;
+        memcpy(str + charsWritten, localeNumberingSystem, sizeof(localeNumberingSystem));
+    }
 }
 
 struct LocaleParserState {
@@ -3004,10 +3038,7 @@
 }
 
 void ResTable_config::setBcp47Locale(const char* in) {
-    locale = 0;
-    memset(localeScript, 0, sizeof(localeScript));
-    memset(localeVariant, 0, sizeof(localeVariant));
-    memset(localeNumberingSystem, 0, sizeof(localeNumberingSystem));
+    clearLocale();
 
     const char* start = in;
     LocaleParserState state;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 8cf4de9..a1f15f0 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -894,9 +894,10 @@
 // - a 8 char variant code prefixed by a 'v'
 //
 // each separated by a single char separator, which sums up to a total of 24
-// chars, (25 include the string terminator) rounded up to 28 to be 4 byte
-// aligned.
-#define RESTABLE_MAX_LOCALE_LEN 28
+// chars, (25 include the string terminator). Numbering system specificator,
+// if present, can add up to 14 bytes (-u-nu-xxxxxxxx), giving 39 bytes,
+// or 40 bytes to make it 4 bytes aligned.
+#define RESTABLE_MAX_LOCALE_LEN 40
 
 
 /**
@@ -1303,6 +1304,9 @@
     // and 0 if they're equally specific.
     int isLocaleMoreSpecificThan(const ResTable_config &o) const;
 
+    // Returns an integer representng the imporance score of the configuration locale.
+    int getImportanceScoreOfLocale() const;
+
     // Return true if 'this' is a better locale match than 'o' for the
     // 'requested' configuration. Similar to isBetterThan(), this assumes that
     // match() has already been used to remove any configurations that don't
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 35007c8..ac08c52 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -173,6 +173,18 @@
     fillIn("en", "US", NULL, "POSIX", &r);
     EXPECT_FALSE(l.isMoreSpecificThan(r));
     EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+    fillIn("ar", "EG", NULL, NULL, &l);
+    fillIn("ar", "EG", NULL, NULL, &r);
+    memcpy(&r.localeNumberingSystem, "latn", 4);
+    EXPECT_FALSE(l.isMoreSpecificThan(r));
+    EXPECT_TRUE(r.isMoreSpecificThan(l));
+
+    fillIn("en", "US", NULL, NULL, &l);
+    fillIn("es", "ES", NULL, NULL, &r);
+
+    EXPECT_FALSE(l.isMoreSpecificThan(r));
+    EXPECT_FALSE(r.isMoreSpecificThan(l));
 }
 
 TEST(ConfigLocaleTest, setLocale) {
@@ -321,6 +333,22 @@
     EXPECT_EQ(0, strcmp("en", out));
 }
 
+TEST(ConfigLocaleTest, getBcp47Locale_numberingSystem) {
+    ResTable_config config;
+    fillIn("en", NULL, NULL, NULL, &config);
+
+    char out[RESTABLE_MAX_LOCALE_LEN];
+
+    memcpy(&config.localeNumberingSystem, "latn", 4);
+    config.getBcp47Locale(out);
+    EXPECT_EQ(0, strcmp("en-u-nu-latn", out));
+
+    fillIn("sr", "SR", "Latn", NULL, &config);
+    memcpy(&config.localeNumberingSystem, "latn", 4);
+    config.getBcp47Locale(out);
+    EXPECT_EQ(0, strcmp("sr-Latn-SR-u-nu-latn", out));
+}
+
 TEST(ConfigLocaleTest, getBcp47Locale_canonicalize) {
     ResTable_config config;
     char out[RESTABLE_MAX_LOCALE_LEN];
@@ -433,6 +461,11 @@
     fillIn("ar", "XB", NULL, NULL, &requested);
     // Even if they are pseudo-locales, exactly equal locales match.
     EXPECT_TRUE(supported.match(requested));
+
+    fillIn("ar", "EG", NULL, NULL, &supported);
+    fillIn("ar", "TN", NULL, NULL, &requested);
+    memcpy(&supported.localeNumberingSystem, "latn", 4);
+    EXPECT_TRUE(supported.match(requested));
 }
 
 TEST(ConfigLocaleTest, match_emptyScript) {
@@ -758,6 +791,26 @@
     EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
 }
 
+TEST(ConfigLocaleTest, isLocaleBetterThan_numberingSystem) {
+    ResTable_config config1, config2, request;
+
+    fillIn("ar", "EG", NULL, NULL, &request);
+    memcpy(&request.localeNumberingSystem, "latn", 4);
+    fillIn("ar", NULL, NULL, NULL, &config1);
+    memcpy(&config1.localeNumberingSystem, "latn", 4);
+    fillIn("ar", NULL, NULL, NULL, &config2);
+    EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+    EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+    fillIn("ar", "EG", NULL, NULL, &request);
+    memcpy(&request.localeNumberingSystem, "latn", 4);
+    fillIn("ar", "TN", NULL, NULL, &config1);
+    memcpy(&config1.localeNumberingSystem, "latn", 4);
+    fillIn("ar", NULL, NULL, NULL, &config2);
+    EXPECT_TRUE(config2.isLocaleBetterThan(config1, &request));
+    EXPECT_FALSE(config1.isLocaleBetterThan(config2, &request));
+}
+
 // Default resources are considered better matches for US English
 // and US-like English locales than International English locales
 TEST(ConfigLocaleTest, isLocaleBetterThan_UsEnglishIsSpecial) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 0990dcc..018db9a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -89,6 +89,10 @@
     ProviderProperties getProviderProperties(String provider);
     String getNetworkProviderPackage();
     boolean isProviderEnabled(String provider);
+    boolean isProviderEnabledForUser(String provider, int userId);
+    boolean setProviderEnabledForUser(String provider, boolean enabled, int userId);
+    boolean isLocationEnabledForUser(int userId);
+    void setLocationEnabledForUser(boolean enabled, int userId);
 
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f0b2774..9db9d33 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -16,7 +16,10 @@
 
 package android.location;
 
-import com.android.internal.location.ProviderProperties;
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.LOCATION_HARDWARE;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -24,7 +27,6 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
@@ -33,17 +35,15 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
-
+import com.android.internal.location.ProviderProperties;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 
-import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
-import static android.Manifest.permission.LOCATION_HARDWARE;
-
 /**
  * This class provides access to the system location services.  These
  * services allow applications to obtain periodic updates of the
@@ -1171,13 +1171,57 @@
     }
 
     /**
+     * Returns the current enabled/disabled status of location
+     *
+     * @return true if location is enabled. false if location is disabled.
+     */
+    public boolean isLocationEnabled() {
+        return isLocationEnabledForUser(Process.myUserHandle());
+    }
+
+    /**
+     * Method for enabling or disabling location.
+     *
+     * @param enabled true to enable location. false to disable location
+     * @param userHandle the user to set
+     * @return true if the value was set, false on database errors
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+        try {
+            mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the current enabled/disabled status of location
+     *
+     * @param userHandle the user to query
+     * @return true location is enabled. false if location is disabled.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isLocationEnabledForUser(UserHandle userHandle) {
+        try {
+            return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns the current enabled/disabled status of the given provider.
      *
      * <p>If the user has enabled this provider in the Settings menu, true
      * is returned otherwise false is returned
      *
-     * <p>Callers should instead use
-     * {@link android.provider.Settings.Secure#LOCATION_MODE}
+     * <p>Callers should instead use {@link #isLocationEnabled()}
      * unless they depend on provider-specific APIs such as
      * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
      *
@@ -1202,6 +1246,64 @@
     }
 
     /**
+     * Returns the current enabled/disabled status of the given provider and user.
+     *
+     * <p>If the user has enabled this provider in the Settings menu, true
+     * is returned otherwise false is returned
+     *
+     * <p>Callers should instead use {@link #isLocationEnabled()}
+     * unless they depend on provider-specific APIs such as
+     * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
+     *
+     * <p>
+     * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
+     * method would throw {@link SecurityException} if the location permissions
+     * were not sufficient to use the specified provider.
+     *
+     * @param provider the name of the provider
+     * @param userHandle the user to query
+     * @return true if the provider exists and is enabled
+     *
+     * @throws IllegalArgumentException if provider is null
+     * @hide
+     */
+    @SystemApi
+    public boolean isProviderEnabledForUser(String provider, UserHandle userHandle) {
+        checkProvider(provider);
+
+        try {
+            return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Method for enabling or disabling a single location provider.
+     *
+     * @param provider the name of the provider
+     * @param enabled true to enable the provider. false to disable the provider
+     * @param userHandle the user to set
+     * @return true if the value was set, false on database errors
+     *
+     * @throws IllegalArgumentException if provider is null
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(WRITE_SECURE_SETTINGS)
+    public boolean setProviderEnabledForUser(
+            String provider, boolean enabled, UserHandle userHandle) {
+        checkProvider(provider);
+
+        try {
+            return mService.setProviderEnabledForUser(
+                    provider, enabled, userHandle.getIdentifier());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Get the last known location.
      *
      * <p>This location could be very old so use
diff --git a/media/java/android/media/AudioFocusRequest.java b/media/java/android/media/AudioFocusRequest.java
index de59ac39..7104dad 100644
--- a/media/java/android/media/AudioFocusRequest.java
+++ b/media/java/android/media/AudioFocusRequest.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 
@@ -220,6 +221,9 @@
     private final static AudioAttributes FOCUS_DEFAULT_ATTR = new AudioAttributes.Builder()
             .setUsage(AudioAttributes.USAGE_MEDIA).build();
 
+    /** @hide */
+    public static final String KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING = "a11y_force_ducking";
+
     private final OnAudioFocusChangeListener mFocusListener; // may be null
     private final Handler mListenerHandler;                  // may be null
     private final AudioAttributes mAttr;                     // never null
@@ -349,6 +353,7 @@
         private boolean mPausesOnDuck = false;
         private boolean mDelayedFocus = false;
         private boolean mFocusLocked = false;
+        private boolean mA11yForceDucking = false;
 
         /**
          * Constructs a new {@code Builder}, and specifies how audio focus
@@ -526,6 +531,21 @@
         }
 
         /**
+         * Marks this focus request as forcing ducking, regardless of the conditions in which
+         * the system would or would not enforce ducking.
+         * Forcing ducking will only be honored when requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
+         * with an {@link AudioAttributes} usage of
+         * {@link AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY}, coming from an accessibility
+         * service, and will be ignored otherwise.
+         * @param forceDucking {@code true} to force ducking
+         * @return this {@code Builder} instance
+         */
+        public @NonNull Builder setForceDucking(boolean forceDucking) {
+            mA11yForceDucking = forceDucking;
+            return this;
+        }
+
+        /**
          * Builds a new {@code AudioFocusRequest} instance combining all the information gathered
          * by this {@code Builder}'s configuration methods.
          * @return the {@code AudioFocusRequest} instance qualified by all the properties set
@@ -538,6 +558,17 @@
                 throw new IllegalStateException(
                         "Can't use delayed focus or pause on duck without a listener");
             }
+            if (mA11yForceDucking) {
+                final Bundle extraInfo;
+                if (mAttr.getBundle() == null) {
+                    extraInfo = new Bundle();
+                } else {
+                    extraInfo = mAttr.getBundle();
+                }
+                // checking of usage and focus request is done server side
+                extraInfo.putBoolean(KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING, true);
+                mAttr = new AudioAttributes.Builder(mAttr).addBundle(extraInfo).build();
+            }
             final int flags = 0
                     | (mDelayedFocus ? AudioManager.AUDIOFOCUS_FLAG_DELAY_OK : 0)
                     | (mPausesOnDuck ? AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS : 0)
diff --git a/media/java/android/media/IMediaSession2.aidl b/media/java/android/media/IMediaSession2.aidl
new file mode 100644
index 0000000..078b611
--- /dev/null
+++ b/media/java/android/media/IMediaSession2.aidl
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.media.session.PlaybackState;
+import android.media.IMediaSession2Callback;
+import android.os.Bundle;
+
+/**
+ * Interface to MediaSession2. Framework MUST only call oneway APIs.
+ *
+ * @hide
+ */
+// TODO(jaewan): Make this oneway interface.
+//               Malicious app can fake session binder and holds commands from controller.
+interface IMediaSession2 {
+    // TODO(jaewan): add onCommand() to send private command
+    // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order
+    //               Add id for individual calls to address this.
+
+    // TODO(jaewan): We may consider to add another binder just for the connection
+    //               not to expose other methods to the controller whose connection wasn't accepted.
+    //               But this would be enough for now because it's the same as existing
+    //               MediaBrowser and MediaBrowserService.
+    oneway void connect(String callingPackage, IMediaSession2Callback callback);
+    oneway void release(IMediaSession2Callback caller);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // send command
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    oneway void sendCommand(IMediaSession2Callback caller, in Bundle command, in Bundle args);
+
+    PlaybackState getPlaybackState();
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Get library service specific
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    oneway void getBrowserRoot(IMediaSession2Callback callback, in Bundle rootHints);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Callbacks -- remove them
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    /**
+     * @param callbackBinder binder to be used to notify changes.
+     * @param callbackFlag one of {@link MediaController2#FLAG_CALLBACK_PLAYBACK} or
+     *     {@link MediaController2#FLAG_CALLBACK_SESSION_ACTIVENESS}
+     * @param requestCode If >= 0, this code will be called back by the callback after the callback
+     *     is registered.
+     */
+    // TODO(jaewan): Due to the nature of the binder, calls can be called out of order.
+    //               Need a way to ensure calling of unregisterCallback unregisters later
+    //               registerCallback.
+    oneway void registerCallback(IMediaSession2Callback callbackBinder,
+            int callbackFlag, int requestCode);
+    oneway void unregisterCallback(IMediaSession2Callback callbackBinder, int callbackFlag);
+}
diff --git a/media/java/android/media/IMediaSession2Callback.aidl b/media/java/android/media/IMediaSession2Callback.aidl
new file mode 100644
index 0000000..a63b6bf
--- /dev/null
+++ b/media/java/android/media/IMediaSession2Callback.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.os.Bundle;
+import android.media.session.PlaybackState;
+import android.media.IMediaSession2;
+
+/**
+ * Interface from MediaSession2 to MediaSession2Record.
+ * <p>
+ * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
+ * and holds calls from session to make session owner(s) frozen.
+ *
+ * @hide
+ */
+oneway interface IMediaSession2Callback {
+    void onPlaybackStateChanged(in PlaybackState state);
+
+    /**
+     * Called only when the controller is created with service's token.
+     *
+     * @param sessionBinder {@code null} if the connect is rejected or is disconnected. a session
+     *     binder if the connect is accepted.
+     * @param commands initially allowed commands.
+     */
+    // TODO(jaewan): Also need to pass flags for allowed actions for permission check.
+    //               For example, a media can allow setRating only for whitelisted apps
+    //               it's better for controller to know such information in advance.
+    //               Follow-up TODO: Add similar functions to the session.
+    // TODO(jaewan): Is term 'accepted/rejected' correct? For permission, 'grant' is used.
+    void onConnectionChanged(IMediaSession2 sessionBinder, in Bundle commandGroup);
+
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    // Browser sepcific
+    //////////////////////////////////////////////////////////////////////////////////////////////
+    void onGetRootResult(in Bundle rootHints, String rootMediaId, in Bundle rootExtra);
+}
diff --git a/media/java/android/media/MediaBrowser2.java b/media/java/android/media/MediaBrowser2.java
new file mode 100644
index 0000000..fa00902
--- /dev/null
+++ b/media/java/android/media/MediaBrowser2.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.update.ApiLoader;
+import android.media.update.MediaBrowser2Provider;
+import android.os.Bundle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Browses media content offered by a {@link MediaLibraryService2}.
+ * @hide
+ */
+public class MediaBrowser2 extends MediaController2 {
+    // Equals to the ((MediaBrowser2Provider) getProvider())
+    private final MediaBrowser2Provider mProvider;
+
+    /**
+     * Callback to listen events from {@link MediaLibraryService2}.
+     */
+    public abstract static class BrowserCallback extends MediaController2.ControllerCallback {
+        /**
+         * Called with the result of {@link #getBrowserRoot(Bundle)}.
+         * <p>
+         * {@code rootMediaId} and {@code rootExtra} can be {@code null} if the browser root isn't
+         * available.
+         *
+         * @param rootHints rootHints that you previously requested.
+         * @param rootMediaId media id of the browser root. Can be {@code null}
+         * @param rootExtra extra of the browser root. Can be {@code null}
+         */
+        public abstract void onGetRootResult(Bundle rootHints, @Nullable String rootMediaId,
+                @Nullable Bundle rootExtra);
+    }
+
+    public MediaBrowser2(Context context, SessionToken token, BrowserCallback callback,
+            Executor executor) {
+        super(context, token, callback, executor);
+        mProvider = (MediaBrowser2Provider) getProvider();
+    }
+
+    @Override
+    MediaBrowser2Provider createProvider(Context context, SessionToken token,
+            ControllerCallback callback, Executor executor) {
+        return ApiLoader.getProvider(context)
+                .createMediaBrowser2(this, context, token, (BrowserCallback) callback, executor);
+    }
+
+    public void getBrowserRoot(Bundle rootHints) {
+        mProvider.getBrowserRoot_impl(rootHints);
+    }
+}
diff --git a/media/java/android/media/MediaController2.java b/media/java/android/media/MediaController2.java
new file mode 100644
index 0000000..9112450
--- /dev/null
+++ b/media/java/android/media/MediaController2.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaSession2.CommandGroup;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaController2Provider;
+import android.os.Handler;
+import java.util.concurrent.Executor;
+
+/**
+ * Allows an app to interact with an active {@link MediaSession2} or a
+ * {@link MediaSessionService2} in any status. Media buttons and other commands can be sent to
+ * the session.
+ * <p>
+ * When you're done, use {@link #release()} to clean up resources. This also helps session service
+ * to be destroyed when there's no controller associated with it.
+ * <p>
+ * When controlling {@link MediaSession2}, the controller will be available immediately after
+ * the creation.
+ * <p>
+ * When controlling {@link MediaSessionService2}, the {@link MediaController2} would be
+ * available only if the session service allows this controller by
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)} for the service. Wait
+ * {@link ControllerCallback#onConnected(CommandGroup)} or
+ * {@link ControllerCallback#onDisconnected()} for the result.
+ * <p>
+ * A controller can be created through token from {@link MediaSessionManager} if you hold the
+ * signature|privileged permission "android.permission.MEDIA_CONTENT_CONTROL" permission or are
+ * an enabled notification listener or by getting a {@link SessionToken} directly the
+ * the session owner.
+ * <p>
+ * MediaController2 objects are thread-safe.
+ * <p>
+ * @see MediaSession2
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently MediaBrowser case is missing.
+public class MediaController2 extends MediaPlayerBase {
+    /**
+     * Interface for listening to change in activeness of the {@link MediaSession2}.  It's
+     * active if and only if it has set a player.
+     */
+    public abstract static class ControllerCallback {
+        /**
+         * Called when the controller is successfully connected to the session. The controller
+         * becomes available afterwards.
+         *
+         * @param allowedCommands commands that's allowed by the session.
+         */
+        public void onConnected(CommandGroup allowedCommands) { }
+
+        /**
+         * Called when the session refuses the controller or the controller is disconnected from
+         * the session. The controller becomes unavailable afterwards and the callback wouldn't
+         * be called.
+         * <p>
+         * It will be also called after the {@link #release()}, so you can put clean up code here.
+         * You don't need to call {@link #release()} after this.
+         */
+        public void onDisconnected() { }
+    }
+
+    private final MediaController2Provider mProvider;
+
+    /**
+     * Create a {@link MediaController2} from the {@link SessionToken}. This connects to the session
+     * and may wake up the service if it's not available.
+     *
+     * @param context Context
+     * @param token token to connect to
+     * @param callback controller callback to receive changes in
+     * @param executor executor to run callbacks on.
+     */
+    // TODO(jaewan): Put @CallbackExecutor to the constructor.
+    public MediaController2(@NonNull Context context, @NonNull SessionToken token,
+            @NonNull ControllerCallback callback, @NonNull Executor executor) {
+        super();
+
+        // This also connects to the token.
+        // Explicit connect() isn't added on purpose because retrying connect() is impossible with
+        // session whose session binder is only valid while it's active.
+        // prevent a controller from reusable after the
+        // session is released and recreated.
+        mProvider = createProvider(context, token, callback, executor);
+    }
+
+    MediaController2Provider createProvider(@NonNull Context context,
+            @NonNull SessionToken token, @NonNull ControllerCallback callback,
+            @NonNull Executor executor) {
+        return ApiLoader.getProvider(context)
+                .createMediaController2(this, context, token, callback, executor);
+    }
+
+    /**
+     * Release this object, and disconnect from the session. After this, callbacks wouldn't be
+     * received.
+     */
+    public void release() {
+        mProvider.release_impl();
+    }
+
+    /**
+     * @hide
+     */
+    public MediaController2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * @return token
+     */
+    public @NonNull
+    SessionToken getSessionToken() {
+        return mProvider.getSessionToken_impl();
+    }
+
+    /**
+     * Returns whether this class is connected to active {@link MediaSession2} or not.
+     */
+    public boolean isConnected() {
+        return mProvider.isConnected_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @Nullable PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * {@link MediaSession2}.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException Called when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Match with the addSessionAvailabilityListener() that tells the current state
+    //               through the listener.
+    // TODO(jaewan): Can handler be null? Follow the API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(@NonNull PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index e2f9b47..b908c21 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -16,13 +16,6 @@
 
 package android.media;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.UUID;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -33,7 +26,18 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.Parcel;
+import android.os.PersistableBundle;
 import android.util.Log;
+import dalvik.system.CloseGuard;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
 
 /**
  * MediaDrm can be used to obtain keys for decrypting protected media streams, in
@@ -117,10 +121,13 @@
  * MediaDrm objects on a thread with its own Looper running (main UI
  * thread by default has a Looper running).
  */
-public final class MediaDrm {
+public final class MediaDrm implements AutoCloseable {
 
     private static final String TAG = "MediaDrm";
 
+    private final AtomicBoolean mClosed = new AtomicBoolean();
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
     private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES;
 
     private EventHandler mEventHandler;
@@ -215,6 +222,8 @@
          */
         native_setup(new WeakReference<MediaDrm>(this),
                 getByteArrayFromUUID(uuid),  ActivityThread.currentOpPackageName());
+
+        mCloseGuard.open("release");
     }
 
     /**
@@ -954,6 +963,168 @@
      */
     public native void releaseAllSecureStops();
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({HDCP_LEVEL_UNKNOWN, HDCP_NONE, HDCP_V1, HDCP_V2,
+                        HDCP_V2_1, HDCP_V2_2, HDCP_NO_DIGITAL_OUTPUT})
+    public @interface HdcpLevel {}
+
+
+    /**
+     * The DRM plugin did not report an HDCP level, or an error
+     * occurred accessing it
+     */
+    public static final int HDCP_LEVEL_UNKNOWN = 0;
+
+    /**
+     * HDCP is not supported on this device, content is unprotected
+     */
+    public static final int HDCP_NONE = 1;
+
+    /**
+     * HDCP version 1.0
+     */
+    public static final int HDCP_V1 = 2;
+
+    /**
+     * HDCP version 2.0 Type 1.
+     */
+    public static final int HDCP_V2 = 3;
+
+    /**
+     * HDCP version 2.1 Type 1.
+     */
+    public static final int HDCP_V2_1 = 4;
+
+    /**
+     *  HDCP version 2.2 Type 1.
+     */
+    public static final int HDCP_V2_2 = 5;
+
+    /**
+     * No digital output, implicitly secure
+     */
+    public static final int HDCP_NO_DIGITAL_OUTPUT = Integer.MAX_VALUE;
+
+    /**
+     * Return the HDCP level negotiated with downstream receivers the
+     * device is connected to. If multiple HDCP-capable displays are
+     * simultaneously connected to separate interfaces, this method
+     * returns the lowest negotiated level of all interfaces.
+     * <p>
+     * This method should only be used for informational purposes, not for
+     * enforcing compliance with HDCP requirements. Trusted enforcement of
+     * HDCP policies must be handled by the DRM system.
+     * <p>
+     * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
+     * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
+     * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+     */
+    @HdcpLevel
+    public native int getConnectedHdcpLevel();
+
+    /**
+     * Return the maximum supported HDCP level. The maximum HDCP level is a
+     * constant for a given device, it does not depend on downstream receivers
+     * that may be connected. If multiple HDCP-capable interfaces are present,
+     * it indicates the highest of the maximum HDCP levels of all interfaces.
+     * <p>
+     * @return one of {@link #HDCP_LEVEL_UNKNOWN}, {@link #HDCP_NONE},
+     * {@link #HDCP_V1}, {@link #HDCP_V2}, {@link #HDCP_V2_1}, {@link #HDCP_V2_2}
+     * or {@link #HDCP_NO_DIGITAL_OUTPUT}.
+     */
+    @HdcpLevel
+    public native int getMaxHdcpLevel();
+
+    /**
+     * Return the number of MediaDrm sessions that are currently opened
+     * simultaneously among all MediaDrm instances for the active DRM scheme.
+     * @return the number of open sessions.
+     */
+    public native int getOpenSessionCount();
+
+    /**
+     * Return the maximum number of MediaDrm sessions that may be opened
+     * simultaneosly among all MediaDrm instances for the active DRM
+     * scheme. The maximum number of sessions is not affected by any
+     * sessions that may have already been opened.
+     * @return maximum sessions.
+     */
+    public native int getMaxSessionCount();
+
+    /**
+     * Security level indicates the robustness of the device's DRM
+     * implementation.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
+                        HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+    public @interface SecurityLevel {}
+
+    /**
+     * The DRM plugin did not report a security level, or an error occurred
+     * accessing it
+     */
+    public static final int SECURITY_LEVEL_UNKNOWN = 0;
+
+    /**
+     *  Software-based whitebox crypto
+     */
+    public static final int SW_SECURE_CRYPTO = 1;
+
+    /**
+     * Software-based whitebox crypto and an obfuscated decoder
+     */
+     public static final int SW_SECURE_DECODE = 2;
+
+    /**
+     * DRM key management and crypto operations are performed within a
+     * hardware backed trusted execution environment
+     */
+    public static final int HW_SECURE_CRYPTO = 3;
+
+    /**
+     * DRM key management, crypto operations and decoding of content
+     * are performed within a hardware backed trusted execution environment
+     */
+     public static final int HW_SECURE_DECODE = 4;
+
+    /**
+     * DRM key management, crypto operations, decoding of content and all
+     * handling of the media (compressed and uncompressed) is handled within
+     * a hardware backed trusted execution environment.
+     */
+    public static final int HW_SECURE_ALL = 5;
+
+    /**
+     * Return the current security level of a session. A session
+     * has an initial security level determined by the robustness of
+     * the DRM system's implementation on the device. The security
+     * level may be adjusted using {@link #setSecurityLevel}.
+     * @param sessionId the session to query.
+     * <p>
+     * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
+     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
+     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
+     * {@link #HW_SECURE_ALL}.
+     */
+    @SecurityLevel
+    public native int getSecurityLevel(@NonNull byte[] sessionId);
+
+    /**
+     * Set the security level of a session. This can be useful if specific
+     * attributes of a lower security level are needed by an application,
+     * such as image manipulation or compositing. Reducing the security
+     * level will typically limit decryption to lower content resolutions,
+     * depending on the license policy.
+     * @param sessionId the session to set the security level on.
+     * @param level the new security level, one of
+     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
+     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
+     * {@link #HW_SECURE_ALL}.
+     */
+    public native void setSecurityLevel(@NonNull byte[] sessionId,
+            @SecurityLevel int level);
+
     /**
      * String property name: identifies the maker of the DRM plugin
      */
@@ -1031,7 +1202,6 @@
     public native void setPropertyByteArray(@NonNull @ArrayProperty
             String propertyName, @NonNull byte[] value);
 
-
     private static final native void setCipherAlgorithmNative(
             @NonNull MediaDrm drm, @NonNull byte[] sessionId, @NonNull String algorithm);
 
@@ -1058,6 +1228,25 @@
             @NonNull byte[] keyId, @NonNull byte[] message, @NonNull byte[] signature);
 
     /**
+     * Return Metrics data about the current MediaDrm instance.
+     *
+     * @return a {@link PersistableBundle} containing the set of attributes and values
+     * available for this instance of MediaDrm.
+     * The attributes are described in {@link MetricsConstants}.
+     *
+     * Additional vendor-specific fields may also be present in
+     * the return value.
+     *
+     * @hide - not part of the public API at this time
+     */
+    public PersistableBundle getMetrics() {
+        PersistableBundle bundle = getMetricsNative();
+        return bundle;
+    }
+
+    private native PersistableBundle getMetricsNative();
+
+    /**
      * In addition to supporting decryption of DASH Common Encrypted Media, the
      * MediaDrm APIs provide the ability to securely deliver session keys from
      * an operator's session key server to a client device, based on the factory-installed
@@ -1311,20 +1500,81 @@
     }
 
     @Override
-    protected void finalize() {
-        native_finalize();
+    protected void finalize() throws Throwable {
+        try {
+            if (mCloseGuard != null) {
+                mCloseGuard.warnIfOpen();
+            }
+            release();
+        } finally {
+            super.finalize();
+        }
     }
 
-    public native final void release();
+    /**
+     * Releases resources associated with the current session of
+     * MediaDrm. It is considered good practice to call this method when
+     * the {@link MediaDrm} object is no longer needed in your
+     * application. After this method is called, {@link MediaDrm} is no
+     * longer usable since it has lost all of its required resource.
+     *
+     * This method was added in API 28. In API versions 18 through 27, release()
+     * should be called instead. There is no need to do anything for API
+     * versions prior to 18.
+     */
+    @Override
+    public void close() {
+        release();
+    }
+
+    /**
+     * @deprecated replaced by {@link #close()}.
+     */
+    @Deprecated
+    public void release() {
+        mCloseGuard.close();
+        if (mClosed.compareAndSet(false, true)) {
+            native_release();
+        }
+    }
+
+    /** @hide */
+    public native final void native_release();
+
     private static native final void native_init();
 
     private native final void native_setup(Object mediadrm_this, byte[] uuid,
             String appPackageName);
 
-    private native final void native_finalize();
-
     static {
         System.loadLibrary("media_jni");
         native_init();
     }
+
+    /**
+     * Definitions for the metrics that are reported via the
+     * {@link #getMetrics} call.
+     *
+     * @hide - not part of the public API at this time
+     */
+    public final static class MetricsConstants
+    {
+        private MetricsConstants() {}
+
+        /**
+         * Key to extract the number of successful {@link #openSession} calls
+         * from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         */
+        public static final String OPEN_SESSION_OK_COUNT
+            = "/drm/mediadrm/open_session/ok/count";
+
+        /**
+         * Key to extract the number of failed {@link #openSession} calls
+         * from the {@link PersistableBundle} returned by a
+         * {@link #getMetrics} call.
+         */
+        public static final String OPEN_SESSION_ERROR_COUNT
+            = "/drm/mediadrm/open_session/error/count";
+    }
 }
diff --git a/media/java/android/media/MediaLibraryService2.java b/media/java/android/media/MediaLibraryService2.java
new file mode 100644
index 0000000..bbc9407
--- /dev/null
+++ b/media/java/android/media/MediaLibraryService2.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.MediaSession2.BuilderBase;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSessionService2Provider;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService.BrowserRoot;
+
+/**
+ * Base class for media library services.
+ * <p>
+ * Media library services enable applications to browse media content provided by an application
+ * and ask the application to start playing it. They may also be used to control content that
+ * is already playing by way of a {@link MediaSession2}.
+ * <p>
+ * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.MediaLibraryService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * A {@link MediaLibraryService2} is extension of {@link MediaSessionService2}. IDs shouldn't
+ * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
+ * default, an empty string will be used for ID of the service. If you want to specify an ID,
+ * declare metadata in the manifest as follows.
+ * @hide
+ */
+// TODO(jaewan): Unhide
+public abstract class MediaLibraryService2 extends MediaSessionService2 {
+    /**
+     * This is the interface name that a service implementing a session service should say that it
+     * support -- that is, this is the action it uses for its intent filter.
+     */
+    public static final String SERVICE_INTERFACE = "android.media.MediaLibraryService2";
+
+    /**
+     * Session for the media library service.
+     */
+    public class MediaLibrarySession extends MediaSession2 {
+        MediaLibrarySession(Context context, MediaPlayerBase player, String id,
+                SessionCallback callback) {
+            super(context, player, id, callback);
+        }
+        // TODO(jaewan): Place public methods here.
+    }
+
+    public static abstract class MediaLibrarySessionCallback extends MediaSession2.SessionCallback {
+        /**
+         * Called to get the root information for browsing by a particular client.
+         * <p>
+         * The implementation should verify that the client package has permission
+         * to access browse media information before returning the root id; it
+         * should return null if the client is not allowed to access this
+         * information.
+         *
+         * @param controllerInfo information of the controller requesting access to browse media.
+         * @param rootHints An optional bundle of service-specific arguments to send
+         * to the media browser service when connecting and retrieving the
+         * root id for browsing, or null if none. The contents of this
+         * bundle may affect the information returned when browsing.
+         * @return The {@link BrowserRoot} for accessing this app's content or null.
+         * @see BrowserRoot#EXTRA_RECENT
+         * @see BrowserRoot#EXTRA_OFFLINE
+         * @see BrowserRoot#EXTRA_SUGGESTED
+         */
+        public abstract @Nullable BrowserRoot onGetRoot(
+                @NonNull ControllerInfo controllerInfo, @Nullable Bundle rootHints);
+    }
+
+    /**
+     * Builder for {@link MediaLibrarySession}.
+     */
+    // TODO(jaewan): Move this to updatable.
+    public class MediaLibrarySessionBuilder
+            extends BuilderBase<MediaLibrarySessionBuilder, MediaLibrarySessionCallback> {
+        public MediaLibrarySessionBuilder(
+                @NonNull Context context, @NonNull MediaPlayerBase player,
+                @NonNull MediaLibrarySessionCallback callback) {
+            super(context, player);
+            setSessionCallback(callback);
+        }
+
+        @Override
+        public MediaLibrarySessionBuilder setSessionCallback(
+                @NonNull MediaLibrarySessionCallback callback) {
+            if (callback == null) {
+                throw new IllegalArgumentException("MediaLibrarySessionCallback cannot be null");
+            }
+            return super.setSessionCallback(callback);
+        }
+
+        @Override
+        public MediaLibrarySession build() throws IllegalStateException {
+            return new MediaLibrarySession(mContext, mPlayer, mId, mCallback);
+        }
+    }
+
+    @Override
+    MediaSessionService2Provider createProvider() {
+        return ApiLoader.getProvider(this).createMediaLibraryService2(this);
+    }
+
+    /**
+     * Called when another app requested to start this service.
+     * <p>
+     * Library service will accept or reject the connection with the
+     * {@link MediaLibrarySessionCallback} in the created session.
+     * <p>
+     * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
+     * expected ID that you've specified through the AndroidManifest.xml.
+     * <p>
+     * This method will be called on the main thread.
+     *
+     * @param sessionId session id written in the AndroidManifest.xml.
+     * @return a new browser session
+     * @see MediaLibrarySessionBuilder
+     * @see #getSession()
+     * @throws RuntimeException if returned session is invalid
+     */
+    @Override
+    public @NonNull abstract MediaLibrarySession onCreateSession(String sessionId);
+}
diff --git a/media/java/android/media/MediaPlayerBase.java b/media/java/android/media/MediaPlayerBase.java
new file mode 100644
index 0000000..980c70f
--- /dev/null
+++ b/media/java/android/media/MediaPlayerBase.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * Tentative interface for all media players that want media session.
+ * APIs are named to avoid conflicts with MediaPlayer APIs.
+ * All calls should be asynchrounous.
+ *
+ * @hide
+ */
+// TODO(wjia) Finalize the list of MediaPlayer2, which MediaPlayerBase's APIs will be come from.
+public abstract class MediaPlayerBase {
+    /**
+     * Listens change in {@link PlaybackState}.
+     */
+    public interface PlaybackListener {
+        /**
+         * Called when {@link PlaybackState} for this player is changed.
+         */
+        void onPlaybackChanged(PlaybackState state);
+    }
+
+    // TODO(jaewan): setDataSources()?
+    // TODO(jaewan): Add release() or do that in stop()?
+
+    // TODO(jaewan): Add set/getSupportedActions().
+    public abstract void play();
+    public abstract void pause();
+    public abstract void stop();
+    public abstract void skipToPrevious();
+    public abstract void skipToNext();
+
+    // Currently PlaybackState's error message is the content title (for testing only)
+    // TODO(jaewan): Add metadata support
+    public abstract PlaybackState getPlaybackState();
+
+    /**
+     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     */
+    public abstract void addPlaybackListener(PlaybackListener listener, Handler handler);
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     */
+    public abstract void removePlaybackListener(PlaybackListener listener);
+}
diff --git a/media/java/android/media/MediaSession2.java b/media/java/android/media/MediaSession2.java
new file mode 100644
index 0000000..33a5807
--- /dev/null
+++ b/media/java/android/media/MediaSession2.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.session.MediaSession;
+import android.media.session.MediaSession.Callback;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSession2Provider;
+import android.media.update.MediaSession2Provider.ControllerInfoProvider;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Allows a media app to expose its transport controls and playback information in a process to
+ * other processes including the Android framework and other apps. Common use cases are as follows.
+ * <ul>
+ *     <li>Bluetooth/wired headset key events support</li>
+ *     <li>Android Auto/Wearable support</li>
+ *     <li>Separating UI process and playback process</li>
+ * </ul>
+ * <p>
+ * A MediaSession2 should be created when an app wants to publish media playback information or
+ * handle media keys. In general an app only needs one session for all playback, though multiple
+ * sessions can be created to provide finer grain controls of media.
+ * <p>
+ * If you want to support background playback, {@link MediaSessionService2} is preferred
+ * instead. With it, your playback can be revived even after you've finished playback. See
+ * {@link MediaSessionService2} for details.
+ * <p>
+ * A session can be obtained by {@link Builder}. The owner of the session may pass its session token
+ * to other processes to allow them to create a {@link MediaController2} to interact with the
+ * session.
+ * <p>
+ * When a session receive transport control commands, the session sends the commands directly to
+ * the the underlying media player set by {@link Builder} or {@link #setPlayer(MediaPlayerBase)}.
+ * <p>
+ * When an app is finished performing playback it must call {@link #close()} to clean up the session
+ * and notify any controllers.
+ * <p>
+ * {@link MediaSession2} objects should be used on the thread on the looper.
+ *
+ * @see MediaSessionService2
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Revisit comments. Currently it's borrowed from the MediaSession.
+// TODO(jaewan): Add explicit release(), and make token @NonNull. Session will be active while the
+//               session exists, and controllers will be invalidated when session becomes inactive.
+// TODO(jaewan): Should we support thread safe? It may cause tricky issue such as b/63797089
+// TODO(jaewan): Should we make APIs for MediaSessionService2 public? It's helpful for
+//               developers that doesn't want to override from Browser, but user may not use this
+//               correctly.
+public class MediaSession2 extends MediaPlayerBase {
+    private final MediaSession2Provider mProvider;
+
+    // Note: Do not define IntDef because subclass can add more command code on top of these.
+    public static final int COMMAND_CODE_CUSTOM = 0;
+    public static final int COMMAND_CODE_PLAYBACK_START = 1;
+    public static final int COMMAND_CODE_PLAYBACK_PAUSE = 2;
+    public static final int COMMAND_CODE_PLAYBACK_STOP = 3;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM = 4;
+    public static final int COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM = 5;
+
+    /**
+     * Define a command that a {@link MediaController2} can send to a {@link MediaSession2}.
+     * <p>
+     * If {@link #getCommandCode()} isn't {@link #COMMAND_CODE_CUSTOM}), it's predefined command.
+     * If {@link #getCommandCode()} is {@link #COMMAND_CODE_CUSTOM}), it's custom command and
+     * {@link #getCustomCommand()} shouldn't be {@code null}.
+     */
+    // TODO(jaewan): Move this into the updatable.
+    public static final class Command {
+        private static final String KEY_COMMAND_CODE
+                = "android.media.mediasession2.command.command_command";
+        private static final String KEY_COMMAND_CUSTOM_COMMAND
+                = "android.media.mediasession2.command.custom_command";
+        private static final String KEY_COMMAND_EXTRA
+                = "android.media.mediasession2.command.extra";
+
+        private final int mCommandCode;
+        // Nonnull if it's custom command
+        private final String mCustomCommand;
+        private final Bundle mExtra;
+
+        public Command(int commandCode) {
+            mCommandCode = commandCode;
+            mCustomCommand = null;
+            mExtra = null;
+        }
+
+        public Command(@NonNull String action, @Nullable Bundle extra) {
+            if (action == null) {
+                throw new IllegalArgumentException("action shouldn't be null");
+            }
+            mCommandCode = COMMAND_CODE_CUSTOM;
+            mCustomCommand = action;
+            mExtra = extra;
+        }
+
+        public int getCommandCode() {
+            return mCommandCode;
+        }
+
+        public @Nullable String getCustomCommand() {
+            return mCustomCommand;
+        }
+
+        public @Nullable Bundle getExtra() {
+            return mExtra;
+        }
+
+        /**
+         * @return a new Bundle instance from the Command
+         * @hide
+         */
+        public Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putInt(KEY_COMMAND_CODE, mCommandCode);
+            bundle.putString(KEY_COMMAND_CUSTOM_COMMAND, mCustomCommand);
+            bundle.putBundle(KEY_COMMAND_EXTRA, mExtra);
+            return bundle;
+        }
+
+        /**
+         * @return a new Command instance from the Bundle
+         * @hide
+         */
+        public static Command fromBundle(Bundle command) {
+            int code = command.getInt(KEY_COMMAND_CODE);
+            if (code != COMMAND_CODE_CUSTOM) {
+                return new Command(code);
+            } else {
+                String customCommand = command.getString(KEY_COMMAND_CUSTOM_COMMAND);
+                if (customCommand == null) {
+                    return null;
+                }
+                return new Command(customCommand, command.getBundle(KEY_COMMAND_EXTRA));
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof Command)) {
+                return false;
+            }
+            Command other = (Command) obj;
+            // TODO(jaewan): Should we also compare contents in bundle?
+            //               It may not be possible if the bundle contains private class.
+            return mCommandCode == other.mCommandCode
+                    && TextUtils.equals(mCustomCommand, other.mCustomCommand);
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            return ((mCustomCommand != null) ? mCustomCommand.hashCode() : 0) * prime + mCommandCode;
+        }
+    }
+
+    /**
+     * Represent set of {@link Command}.
+     */
+    // TODO(jaewan): Move this to updatable
+    public static class CommandGroup {
+        private static final String KEY_COMMANDS =
+                "android.media.mediasession2.commandgroup.commands";
+        private ArraySet<Command> mCommands = new ArraySet<>();
+
+        public CommandGroup() {
+        }
+
+        public CommandGroup(CommandGroup others) {
+            mCommands.addAll(others.mCommands);
+        }
+
+        public void addCommand(Command command) {
+            mCommands.add(command);
+        }
+
+        public void addAllPredefinedCommands() {
+            // TODO(jaewan): Is there any better way than this?
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_START));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_PAUSE));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_STOP));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
+            mCommands.add(new Command(COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
+        }
+
+        public void removeCommand(Command command) {
+            mCommands.remove(command);
+        }
+
+        public boolean hasCommand(Command command) {
+            return mCommands.contains(command);
+        }
+
+        public boolean hasCommand(int code) {
+            if (code == COMMAND_CODE_CUSTOM) {
+                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
+            }
+            for (int i = 0; i < mCommands.size(); i++) {
+                if (mCommands.valueAt(i).getCommandCode() == code) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * @return new bundle from the CommandGroup
+         * @hide
+         */
+        public Bundle toBundle() {
+            ArrayList<Bundle> list = new ArrayList<>();
+            for (int i = 0; i < mCommands.size(); i++) {
+                list.add(mCommands.valueAt(i).toBundle());
+            }
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(KEY_COMMANDS, list);
+            return bundle;
+        }
+
+        /**
+         * @return new instance of CommandGroup from the bundle
+         * @hide
+         */
+        public static @Nullable CommandGroup fromBundle(Bundle commands) {
+            if (commands == null) {
+                return null;
+            }
+            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
+            if (list == null) {
+                return null;
+            }
+            CommandGroup commandGroup = new CommandGroup();
+            for (int i = 0; i < list.size(); i++) {
+                Parcelable parcelable = list.get(i);
+                if (!(parcelable instanceof Bundle)) {
+                    continue;
+                }
+                Bundle commandBundle = (Bundle) parcelable;
+                Command command = Command.fromBundle(commandBundle);
+                if (command != null) {
+                    commandGroup.addCommand(command);
+                }
+            }
+            return commandGroup;
+        }
+    }
+
+    /**
+     * Callback to be called for all incoming commands from {@link MediaController2}s.
+     * <p>
+     * If it's not set, the session will accept all controllers and all incoming commands by
+     * default.
+     */
+    // TODO(jaewan): Can we move this inside of the updatable for default implementation.
+    public static class SessionCallback {
+        /**
+         * Called when a controller is created for this session. Return allowed commands for
+         * controller. By default it allows all connection requests and commands.
+         * <p>
+         * You can reject the connection by return {@code null}. In that case, controller receives
+         * {@link MediaController2.ControllerCallback#onDisconnected()} and cannot be usable.
+         *
+         * @param controller controller information.
+         * @return allowed commands. Can be {@code null} to reject coonnection.
+         */
+        // TODO(jaewan): Change return type. Once we do, null is for reject.
+        public @Nullable CommandGroup onConnect(@NonNull ControllerInfo controller) {
+            CommandGroup commands = new CommandGroup();
+            commands.addAllPredefinedCommands();
+            return commands;
+        }
+
+        /**
+         * Called when a controller sent a command to the session. You can also reject the request
+         * by return {@code false}.
+         * <p>
+         * This method will be called on the session handler.
+         *
+         * @param controller controller information.
+         * @param command a command. This method will be called for every single command.
+         * @return {@code true} if you want to accept incoming command. {@code false} otherwise.
+         */
+        public boolean onCommandRequest(@NonNull ControllerInfo controller,
+                @NonNull Command command) {
+            return true;
+        }
+    };
+
+    /**
+     * Base builder class for MediaSession2 and its subclass.
+     *
+     * @hide
+     */
+    static abstract class BuilderBase
+            <T extends MediaSession2.BuilderBase<T, C>, C extends SessionCallback> {
+        final Context mContext;
+        final MediaPlayerBase mPlayer;
+        String mId;
+        C mCallback;
+
+        /**
+         * Constructor.
+         *
+         * @param context a context
+         * @param player a player to handle incoming command from any controller.
+         * @throws IllegalArgumentException if any parameter is null, or the player is a
+         *      {@link MediaSession2} or {@link MediaController2}.
+         */
+        // TODO(jaewan): Also need executor
+        public BuilderBase(@NonNull Context context, @NonNull MediaPlayerBase player) {
+            if (context == null) {
+                throw new IllegalArgumentException("context shouldn't be null");
+            }
+            if (player == null) {
+                throw new IllegalArgumentException("player shouldn't be null");
+            }
+            if (player instanceof MediaSession2 || player instanceof MediaController2) {
+                throw new IllegalArgumentException("player doesn't accept MediaSession2 nor"
+                        + " MediaController2");
+            }
+            mContext = context;
+            mPlayer = player;
+            // Ensure non-null
+            mId = "";
+        }
+
+        /**
+         * Set ID of the session. If it's not set, an empty string with used to create a session.
+         * <p>
+         * Use this if and only if your app supports multiple playback at the same time and also
+         * wants to provide external apps to have finer controls of them.
+         *
+         * @param id id of the session. Must be unique per package.
+         * @throws IllegalArgumentException if id is {@code null}
+         * @return
+         */
+        public T setId(@NonNull String id) {
+            if (id == null) {
+                throw new IllegalArgumentException("id shouldn't be null");
+            }
+            mId = id;
+            return (T) this;
+        }
+
+        /**
+         * Set {@link SessionCallback}.
+         *
+         * @param callback session callback.
+         * @return
+         */
+        public T setSessionCallback(@Nullable C callback) {
+            mCallback = callback;
+            return (T) this;
+        }
+
+        /**
+         * Build {@link MediaSession2}.
+         *
+         * @return a new session
+         * @throws IllegalStateException if the session with the same id is already exists for the
+         *      package.
+         */
+        public abstract MediaSession2 build() throws IllegalStateException;
+    }
+
+    /**
+     * Builder for {@link MediaSession2}.
+     * <p>
+     * Any incoming event from the {@link MediaController2} will be handled on the thread
+     * that created session with the {@link Builder#build()}.
+     */
+    // TODO(jaewan): Move this to updatable
+    // TODO(jaewan): Add setRatingType()
+    // TODO(jaewan): Add setSessionActivity()
+    public static final class Builder extends BuilderBase<Builder, SessionCallback> {
+        public Builder(Context context, @NonNull MediaPlayerBase player) {
+            super(context, player);
+        }
+
+        @Override
+        public MediaSession2 build() throws IllegalStateException {
+            if (mCallback == null) {
+                mCallback = new SessionCallback();
+            }
+            return new MediaSession2(mContext, mPlayer, mId, mCallback);
+        }
+    }
+
+    /**
+     * Information of a controller.
+     */
+    // TODO(jaewan): Move implementation to the updatable.
+    public static final class ControllerInfo {
+        private final ControllerInfoProvider mProvider;
+
+        /**
+         * @hide
+         */
+        // TODO(jaewan): SystemApi
+        // TODO(jaewan): Also accept componentName to check notificaiton listener.
+        public ControllerInfo(Context context, int uid, int pid, String packageName,
+                IMediaSession2Callback callback) {
+            mProvider = ApiLoader.getProvider(context)
+                    .createMediaSession2ControllerInfoProvider(
+                            this, context, uid, pid, packageName, callback);
+        }
+
+        /**
+         * @return package name of the controller
+         */
+        public String getPackageName() {
+            return mProvider.getPackageName_impl();
+        }
+
+        /**
+         * @return uid of the controller
+         */
+        public int getUid() {
+            return mProvider.getUid_impl();
+        }
+
+        /**
+         * Return if the controller has granted {@code android.permission.MEDIA_CONTENT_CONTROL} or
+         * has a enabled notification listener so can be trusted to accept connection and incoming
+         * command request.
+         *
+         * @return {@code true} if the controller is trusted.
+         */
+        public boolean isTrusted() {
+            return mProvider.isTrusted_impl();
+        }
+
+        /**
+         * @hide
+         * @return
+         */
+        // TODO(jaewan): SystemApi
+        public ControllerInfoProvider getProvider() {
+            return mProvider;
+        }
+
+        @Override
+        public int hashCode() {
+            return mProvider.hashCode_impl();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof ControllerInfo)) {
+                return false;
+            }
+            ControllerInfo other = (ControllerInfo) obj;
+            return mProvider.equals_impl(other.mProvider);
+        }
+
+        @Override
+        public String toString() {
+            return "ControllerInfo {pkg=" + getPackageName() + ", uid=" + getUid() + ", trusted="
+                    + isTrusted() + "}";
+        }
+    }
+
+    /**
+     * Constructor is hidden and apps can only instantiate indirectly through {@link Builder}.
+     * <p>
+     * This intended behavior and here's the reasons.
+     *    1. Prevent multiple sessions with the same tag in a media app.
+     *       Whenever it happens only one session was properly setup and others were all dummies.
+     *       Android framework couldn't find the right session to dispatch media key event.
+     *    2. Simplify session's lifecycle.
+     *       {@link MediaSession} can be available after all of {@link MediaSession#setFlags(int)},
+     *       {@link MediaSession#setCallback(Callback)}, and
+     *       {@link MediaSession#setActive(boolean)}. It was common for an app to omit one, so
+     *       framework had to add heuristics to figure out if an app is
+     * @hide
+     */
+    MediaSession2(Context context, MediaPlayerBase player, String id,
+            SessionCallback callback) {
+        super();
+        mProvider = ApiLoader.getProvider(context)
+                .createMediaSession2(this, context, player, id, callback);
+    }
+
+    /**
+     * @hide
+     */
+    // TODO(jaewan): SystemApi
+    public MediaSession2Provider getProvider() {
+        return mProvider;
+    }
+
+    /**
+     * Set the underlying {@link MediaPlayerBase} for this session to dispatch incoming event to.
+     * Events from the {@link MediaController2} will be sent directly to the underlying
+     * player on the {@link Handler} where the session is created on.
+     * <p>
+     * If the new player is successfully set, {@link PlaybackListener}
+     * will be called to tell the current playback state of the new player.
+     * <p>
+     * Calling this method with {@code null} will disconnect binding connection between the
+     * controllers and also release this object.
+     *
+     * @param player a {@link MediaPlayerBase} that handles actual media playback in your app.
+     *      It shouldn't be {@link MediaSession2} nor {@link MediaController2}.
+     * @throws IllegalArgumentException if the player is either {@link MediaSession2}
+     *      or {@link MediaController2}.
+     */
+    // TODO(jaewan): Add release instead of setPlayer(null).
+    public void setPlayer(MediaPlayerBase player) throws IllegalArgumentException {
+        mProvider.setPlayer_impl(player);
+    }
+
+    /**
+     * @return player
+     */
+    public @Nullable MediaPlayerBase getPlayer() {
+        return mProvider.getPlayer_impl();
+    }
+
+    /**
+     * Returns the {@link SessionToken} for creating {@link MediaController2}.
+     */
+    public @NonNull
+    SessionToken getToken() {
+        return mProvider.getToken_impl();
+    }
+
+    public @NonNull List<ControllerInfo> getConnectedControllers() {
+        return mProvider.getConnectedControllers_impl();
+    }
+
+    @Override
+    public void play() {
+        mProvider.play_impl();
+    }
+
+    @Override
+    public void pause() {
+        mProvider.pause_impl();
+    }
+
+    @Override
+    public void stop() {
+        mProvider.stop_impl();
+    }
+
+    @Override
+    public void skipToPrevious() {
+        mProvider.skipToPrevious_impl();
+    }
+
+    @Override
+    public void skipToNext() {
+        mProvider.skipToNext_impl();
+    }
+
+    @Override
+    public @NonNull PlaybackState getPlaybackState() {
+        return mProvider.getPlaybackState_impl();
+    }
+
+    /**
+     * Add a {@link PlaybackListener} to listen changes in the
+     * underlying {@link MediaPlayerBase} which is previously set by
+     * {@link #setPlayer(MediaPlayerBase)}.
+     * <p>
+     * Added listeners will be also called when the underlying player is changed.
+     *
+     * @param listener the listener that will be run
+     * @param handler the Handler that will receive the listener
+     * @throws IllegalArgumentException when either the listener or handler is {@code null}.
+     */
+    // TODO(jaewan): Can handler be null? Follow API guideline after it's finalized.
+    @Override
+    public void addPlaybackListener(@NonNull PlaybackListener listener, @NonNull Handler handler) {
+        mProvider.addPlaybackListener_impl(listener, handler);
+    }
+
+    /**
+     * Remove previously added {@link PlaybackListener}.
+     *
+     * @param listener the listener to be removed
+     * @throws IllegalArgumentException if the listener is {@code null}.
+     */
+    @Override
+    public void removePlaybackListener(PlaybackListener listener) {
+        mProvider.removePlaybackListener_impl(listener);
+    }
+}
diff --git a/media/java/android/media/MediaSessionService2.java b/media/java/android/media/MediaSessionService2.java
new file mode 100644
index 0000000..1a12d68
--- /dev/null
+++ b/media/java/android/media/MediaSessionService2.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.session.PlaybackState;
+import android.media.update.ApiLoader;
+import android.media.update.MediaSessionService2Provider;
+import android.os.IBinder;
+
+/**
+ * Base class for media session services, which is the service version of the {@link MediaSession2}.
+ * <p>
+ * It's highly recommended for an app to use this instead of {@link MediaSession2} if it wants
+ * to keep media playback in the background.
+ * <p>
+ * Here's the benefits of using {@link MediaSessionService2} instead of
+ * {@link MediaSession2}.
+ * <ul>
+ * <li>Another app can know that your app supports {@link MediaSession2} even when your app
+ * isn't running.
+ * <li>Another app can start playback of your app even when your app isn't running.
+ * </ul>
+ * For example, user's voice command can start playback of your app even when it's not running.
+ * <p>
+ * To extend this class, adding followings directly to your {@code AndroidManifest.xml}.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * A {@link MediaSessionService2} is another form of {@link MediaSession2}. IDs shouldn't
+ * be shared between the {@link MediaSessionService2} and {@link MediaSession2}. By
+ * default, an empty string will be used for ID of the service. If you want to specify an ID,
+ * declare metadata in the manifest as follows.
+ * <pre>
+ * &lt;service android:name="component_name_of_your_implementation" &gt;
+ *   &lt;intent-filter&gt;
+ *     &lt;action android:name="android.media.MediaSessionService2" /&gt;
+ *   &lt;/intent-filter&gt;
+ *   &lt;meta-data android:name="android.media.session"
+ *       android:value="session_id"/&gt;
+ * &lt;/service&gt;</pre>
+ * <p>
+ * It's recommended for an app to have a single {@link MediaSessionService2} declared in the
+ * manifest. Otherwise, your app might be shown twice in the list of the Auto/Wearable, or another
+ * app fails to pick the right session service when it wants to start the playback this app.
+ * <p>
+ * If there's conflicts with the session ID among the services, services wouldn't be available for
+ * any controllers.
+ * <p>
+ * Topic covered here:
+ * <ol>
+ * <li><a href="#ServiceLifecycle">Service Lifecycle</a>
+ * <li><a href="#Permissions">Permissions</a>
+ * </ol>
+ * <div class="special reference">
+ * <a name="ServiceLifecycle"></a>
+ * <h3>Service Lifecycle</h3>
+ * <p>
+ * Session service is bounded service. When a {@link MediaController2} is created for the
+ * session service, the controller binds to the session service. {@link #onCreateSession(String)}
+ * may be called after the {@link #onCreate} if the service hasn't created yet.
+ * <p>
+ * After the binding, session's {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}
+ * will be called to accept or reject connection request from a controller. If the connection is
+ * rejected, the controller will unbind. If it's accepted, the controller will be available to use
+ * and keep binding.
+ * <p>
+ * When playback is started for this session service, {@link #onUpdateNotification(PlaybackState)}
+ * is called and service would become a foreground service. It's needed to keep playback after the
+ * controller is destroyed. The session service becomes background service when the playback is
+ * stopped.
+ * <a name="Permissions"></a>
+ * <h3>Permissions</h3>
+ * <p>
+ * Any app can bind to the session service with controller, but the controller can be used only if
+ * the session service accepted the connection request through
+ * {@link MediaSession2.SessionCallback#onConnect(ControllerInfo)}.
+ *
+ * @hide
+ */
+// TODO(jaewan): Unhide
+// TODO(jaewan): Can we clean up sessions in onDestroy() automatically instead?
+//               What about currently running SessionCallback when the onDestroy() is called?
+// TODO(jaewan): Protect this with system|privilleged permission - Q.
+// TODO(jaewan): Add permission check for the service to know incoming connection request.
+//               Follow-up questions: What about asking a XML for list of white/black packages for
+//                                    allowing enumeration?
+//                                    We can read the information even when the service is started,
+//                                    so SessionManager.getXXXXService() can only return apps
+//                                    TODO(jaewan): Will be the black/white listing persistent?
+//                                                  In other words, can we cache the rejection?
+public abstract class MediaSessionService2 extends Service {
+    private final MediaSessionService2Provider mProvider;
+
+    /**
+     * This is the interface name that a service implementing a session service should say that it
+     * support -- that is, this is the action it uses for its intent filter.
+     */
+    public static final String SERVICE_INTERFACE = "android.media.MediaSessionService2";
+
+    /**
+     * Name under which a MediaSessionService2 component publishes information about itself.
+     * This meta-data must provide a string value for the ID.
+     */
+    public static final String SERVICE_META_DATA = "android.media.session";
+
+    public MediaSessionService2() {
+        super();
+        mProvider = createProvider();
+    }
+
+    MediaSessionService2Provider createProvider() {
+        return ApiLoader.getProvider(this).createMediaSessionService2(this);
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to initialize session service.
+     * <p>
+     * Override this method if you need your own initialization. Derived classes MUST call through
+     * to the super class's implementation of this method.
+     */
+    @CallSuper
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mProvider.onCreate_impl();
+    }
+
+    /**
+     * Called when another app requested to start this service to get {@link MediaSession2}.
+     * <p>
+     * Session service will accept or reject the connection with the
+     * {@link MediaSession2.SessionCallback} in the created session.
+     * <p>
+     * Service wouldn't run if {@code null} is returned or session's ID doesn't match with the
+     * expected ID that you've specified through the AndroidManifest.xml.
+     * <p>
+     * This method will be called on the main thread.
+     *
+     * @param sessionId session id written in the AndroidManifest.xml.
+     * @return a new session
+     * @see MediaSession2.Builder
+     * @see #getSession()
+     */
+    public @NonNull abstract MediaSession2 onCreateSession(String sessionId);
+
+    /**
+     * Called when the playback state of this session is changed, and notification needs update.
+     * Override this method to show your own notification UI.
+     * <p>
+     * With the notification returned here, the service become foreground service when the playback
+     * is started. It becomes background service after the playback is stopped.
+     *
+     * @param state playback state
+     * @return a {@link MediaNotification}. If it's {@code null}, notification wouldn't be shown.
+     */
+    // TODO(jaewan): Also add metadata
+    public MediaNotification onUpdateNotification(PlaybackState state) {
+        return mProvider.onUpdateNotification_impl(state);
+    }
+
+    /**
+     * Get instance of the {@link MediaSession2} that you've previously created with the
+     * {@link #onCreateSession} for this service.
+     *
+     * @return created session
+     */
+    public final MediaSession2 getSession() {
+        return mProvider.getSession_impl();
+    }
+
+    /**
+     * Default implementation for {@link MediaSessionService2} to handle incoming binding
+     * request. If the request is for getting the session, the intent will have action
+     * {@link #SERVICE_INTERFACE}.
+     * <p>
+     * Override this method if this service also needs to handle binder requests other than
+     * {@link #SERVICE_INTERFACE}. Derived classes MUST call through to the super class's
+     * implementation of this method.
+     *
+     * @param intent
+     * @return Binder
+     */
+    @CallSuper
+    @Nullable
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mProvider.onBind_impl(intent);
+    }
+
+    /**
+     * Returned by {@link #onUpdateNotification(PlaybackState)} for making session service
+     * foreground service to keep playback running in the background. It's highly recommended to
+     * show media style notification here.
+     */
+    // TODO(jaewan): Should we also move this to updatable?
+    public static class MediaNotification {
+        public final int id;
+        public final Notification notification;
+
+        private MediaNotification(int id, @NonNull Notification notification) {
+            this.id = id;
+            this.notification = notification;
+        }
+
+        /**
+         * Create a {@link MediaNotification}.
+         *
+         * @param notificationId notification id to be used for
+         *      {@link android.app.NotificationManager#notify(int, Notification)}.
+         * @param notification a notification to make session service foreground service. Media
+         *      style notification is recommended here.
+         * @return
+         */
+        public static MediaNotification create(int notificationId,
+                @NonNull Notification notification) {
+            if (notification == null) {
+                throw new IllegalArgumentException("Notification cannot be null");
+            }
+            return new MediaNotification(notificationId, notification);
+        }
+    }
+}
diff --git a/media/java/android/media/SessionToken.java b/media/java/android/media/SessionToken.java
new file mode 100644
index 0000000..53fc24a
--- /dev/null
+++ b/media/java/android/media/SessionToken.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.session.MediaSessionManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Represents an ongoing {@link MediaSession2} or a {@link MediaSessionService2}.
+ * If it's representing a session service, it may not be ongoing.
+ * <p>
+ * This may be passed to apps by the session owner to allow them to create a
+ * {@link MediaController2} to communicate with the session.
+ * <p>
+ * It can be also obtained by {@link MediaSessionManager}.
+ * @hide
+ */
+// TODO(jaewan): Unhide. SessionToken2?
+// TODO(jaewan): Move Token to updatable!
+// TODO(jaewan): Find better name for this (SessionToken or Session2Token)
+public final class SessionToken {
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {TYPE_SESSION, TYPE_SESSION_SERVICE, TYPE_LIBRARY_SERVICE})
+    public @interface TokenType {
+    }
+
+    public static final int TYPE_SESSION = 0;
+    public static final int TYPE_SESSION_SERVICE = 1;
+    public static final int TYPE_LIBRARY_SERVICE = 2;
+
+    private static final String KEY_TYPE = "android.media.token.type";
+    private static final String KEY_PACKAGE_NAME = "android.media.token.package_name";
+    private static final String KEY_SERVICE_NAME = "android.media.token.service_name";
+    private static final String KEY_ID = "android.media.token.id";
+    private static final String KEY_SESSION_BINDER = "android.media.token.session_binder";
+
+    private final @TokenType int mType;
+    private final String mPackageName;
+    private final String mServiceName;
+    private final String mId;
+    private final IMediaSession2 mSessionBinder;
+
+    /**
+     * Constructor for the token.
+     *
+     * @hide
+     * @param type type
+     * @param packageName package name
+     * @param id id
+     * @param serviceName name of service. Can be {@code null} if it's not an service.
+     * @param sessionBinder binder for this session. Can be {@code null} if it's service.
+     * @hide
+     */
+    // TODO(jaewan): UID is also needed.
+    // TODO(jaewan): Unhide
+    public SessionToken(@TokenType int type, @NonNull String packageName, @NonNull String id,
+            @Nullable String serviceName, @Nullable IMediaSession2 sessionBinder) {
+        // TODO(jaewan): Add sanity check.
+        mType = type;
+        mPackageName = packageName;
+        mId = id;
+        mServiceName = serviceName;
+        mSessionBinder = sessionBinder;
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        return mType
+                + prime * (mPackageName.hashCode()
+                + prime * (mId.hashCode()
+                + prime * ((mServiceName != null ? mServiceName.hashCode() : 0)
+                + prime * (mSessionBinder != null ? mSessionBinder.asBinder().hashCode() : 0))));
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        SessionToken other = (SessionToken) obj;
+        if (!mPackageName.equals(other.getPackageName())
+                || !mServiceName.equals(other.getServiceName())
+                || !mId.equals(other.getId())
+                || mType != other.getType()) {
+            return false;
+        }
+        if (mSessionBinder == other.getSessionBinder()) {
+            return true;
+        } else if (mSessionBinder == null || other.getSessionBinder() == null) {
+            return false;
+        }
+        return mSessionBinder.asBinder().equals(other.getSessionBinder().asBinder());
+    }
+
+    @Override
+    public String toString() {
+        return "SessionToken {pkg=" + mPackageName + " id=" + mId + " type=" + mType
+                + " service=" + mServiceName + " binder=" + mSessionBinder + "}";
+    }
+
+    /**
+     * @return package name
+     */
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * @return id
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * @return type of the token
+     * @see #TYPE_SESSION
+     * @see #TYPE_SESSION_SERVICE
+     */
+    public @TokenType int getType() {
+        return mType;
+    }
+
+    /**
+     * @return session binder.
+     * @hide
+     */
+    public @Nullable IMediaSession2 getSessionBinder() {
+        return mSessionBinder;
+    }
+
+    /**
+     * @return service name if it's session service.
+     * @hide
+     */
+    public @Nullable String getServiceName() {
+        return mServiceName;
+    }
+
+    /**
+     * Create a token from the bundle, exported by {@link #toBundle()}.
+     *
+     * @param bundle
+     * @return
+     */
+    public static SessionToken fromBundle(@NonNull Bundle bundle) {
+        if (bundle == null) {
+            return null;
+        }
+        final @TokenType int type = bundle.getInt(KEY_TYPE, -1);
+        final String packageName = bundle.getString(KEY_PACKAGE_NAME);
+        final String serviceName = bundle.getString(KEY_SERVICE_NAME);
+        final String id = bundle.getString(KEY_ID);
+        final IBinder sessionBinder = bundle.getBinder(KEY_SESSION_BINDER);
+
+        // Sanity check.
+        switch (type) {
+            case TYPE_SESSION:
+                if (!(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Session needs sessionBinder");
+                }
+                break;
+            case TYPE_SESSION_SERVICE:
+                if (TextUtils.isEmpty(serviceName)) {
+                    throw new IllegalArgumentException("Session service needs service name");
+                }
+                if (sessionBinder != null && !(sessionBinder instanceof IMediaSession2)) {
+                    throw new IllegalArgumentException("Invalid session binder");
+                }
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid type");
+        }
+        if (TextUtils.isEmpty(packageName) || id == null) {
+            throw new IllegalArgumentException("Package name nor ID cannot be null.");
+        }
+        // TODO(jaewan): Revisit here when we add connection callback to the session for individual
+        //               controller's permission check. With it, sessionBinder should be available
+        //               if and only if for session, not session service.
+        return new SessionToken(type, packageName, id, serviceName,
+                sessionBinder != null ? IMediaSession2.Stub.asInterface(sessionBinder) : null);
+    }
+
+    /**
+     * Create a {@link Bundle} from this token to share it across processes.
+     *
+     * @return Bundle
+     * @hide
+     */
+    public Bundle toBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putString(KEY_PACKAGE_NAME, mPackageName);
+        bundle.putString(KEY_SERVICE_NAME, mServiceName);
+        bundle.putString(KEY_ID, mId);
+        bundle.putInt(KEY_TYPE, mType);
+        bundle.putBinder(KEY_SESSION_BINDER,
+                mSessionBinder != null ? mSessionBinder.asBinder() : null);
+        return bundle;
+    }
+}
diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index 5fcb430..b8463dd 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,6 +17,7 @@
 
 import android.content.ComponentName;
 import android.media.IRemoteVolumeController;
+import android.media.IMediaSession2;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -49,4 +50,8 @@
     void setCallback(in ICallback callback);
     void setOnVolumeKeyLongPressListener(in IOnVolumeKeyLongPressListener listener);
     void setOnMediaKeyListener(in IOnMediaKeyListener listener);
+
+    // MediaSession2
+    Bundle createSessionToken(String callingPackage, String id, IMediaSession2 binder);
+    List<Bundle> getSessionTokens(boolean activeSessionOnly, boolean sessionServiceOnly);
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index b215825..6a9f04a 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -24,8 +24,13 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.ISessionManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -38,6 +43,7 @@
 import android.view.KeyEvent;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 
 /**
@@ -331,6 +337,101 @@
     }
 
     /**
+     * Called when a {@link MediaSession2} is created.
+     *
+     * @hide
+     */
+    // TODO(jaewan): System API
+    public SessionToken createSessionToken(@NonNull String callingPackage, @NonNull String id,
+            @NonNull IMediaSession2 binder) {
+        try {
+            Bundle bundle = mService.createSessionToken(callingPackage, id, binder);
+            return SessionToken.fromBundle(bundle);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+        }
+        return null;
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} whose sessions are active now. This list represents
+     * active sessions regardless of whether they're {@link MediaSession2} or
+     * {@link MediaSessionService2}.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getActiveSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ true, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get {@link List} of {@link SessionToken} for {@link MediaSessionService2} regardless of their
+     * activeness. This list represents media apps that support background playback.
+     *
+     * @return list of Tokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getSessionServiceTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ true);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * Get all {@link SessionToken}s. This is the combined list of {@link #getActiveSessionTokens()}
+     * and {@link #getSessionServiceTokens}.
+     *
+     * @return list of Tokens
+     * @see #getActiveSessionTokens
+     * @see #getSessionServiceTokens
+     * @hide
+     */
+    // TODO(jaewan): Unhide
+    // TODO(jaewan): Protect this with permission.
+    // TODO(jaewna): Add listener for change in lists.
+    public List<SessionToken> getAllSessionTokens() {
+        try {
+            List<Bundle> bundles = mService.getSessionTokens(
+                    /* activeSessionOnly */ false, /* sessionServiceOnly */ false);
+            return toTokenList(bundles);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Cannot communicate with the service.", e);
+            return Collections.emptyList();
+        }
+    }
+
+    private static List<SessionToken> toTokenList(List<Bundle> bundles) {
+        List<SessionToken> tokens = new ArrayList<>();
+        if (bundles != null) {
+            for (int i = 0; i < bundles.size(); i++) {
+                SessionToken token = SessionToken.fromBundle(bundles.get(i));
+                if (token != null) {
+                    tokens.add(token);
+                }
+            }
+        }
+        return tokens;
+    }
+
+    /**
      * Check if the global priority session is currently active. This can be
      * used to decide if media keys should be sent to the session or to the app.
      *
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/media/java/android/media/update/MediaBrowser2Provider.java
similarity index 68%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to media/java/android/media/update/MediaBrowser2Provider.java
index b35713f..355dbc9 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/media/java/android/media/update/MediaBrowser2Provider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.media.update;
 
-/* @hide */
-parcelable KeychainSnapshot;
+import android.os.Bundle;
+
+/**
+ * @hide
+ */
+public interface MediaBrowser2Provider extends MediaController2Provider {
+    void getBrowserRoot_impl(Bundle rootHints);
+}
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/media/java/android/media/update/MediaController2Provider.java
similarity index 64%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to media/java/android/media/update/MediaController2Provider.java
index b35713f..b15d6db 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/media/java/android/media/update/MediaController2Provider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,15 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.media.update;
 
-/* @hide */
-parcelable KeychainSnapshot;
+import android.media.SessionToken;
+
+/**
+ * @hide
+ */
+public interface MediaController2Provider extends MediaPlayerBaseProvider {
+    void release_impl();
+    SessionToken getSessionToken_impl();
+    boolean isConnected_impl();
+}
diff --git a/core/java/android/security/keystore/KeychainSnapshot.aidl b/media/java/android/media/update/MediaLibraryService2Provider.java
similarity index 72%
copy from core/java/android/security/keystore/KeychainSnapshot.aidl
copy to media/java/android/media/update/MediaLibraryService2Provider.java
index b35713f..7e3444f 100644
--- a/core/java/android/security/keystore/KeychainSnapshot.aidl
+++ b/media/java/android/media/update/MediaLibraryService2Provider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright 2018 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
-package android.security.keystore;
+package android.media.update;
 
-/* @hide */
-parcelable KeychainSnapshot;
+/**
+ * @hide
+ */
+public interface MediaLibraryService2Provider extends MediaSessionService2Provider {
+    // Nothing new for now
+}
diff --git a/media/java/android/media/update/MediaPlayerBaseProvider.java b/media/java/android/media/update/MediaPlayerBaseProvider.java
new file mode 100644
index 0000000..5b13e74
--- /dev/null
+++ b/media/java/android/media/update/MediaPlayerBaseProvider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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.media.update;
+
+import android.media.MediaPlayerBase;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+
+/**
+ * @hide
+ */
+public interface MediaPlayerBaseProvider {
+    void play_impl();
+    void pause_impl();
+    void stop_impl();
+    void skipToPrevious_impl();
+    void skipToNext_impl();
+
+    PlaybackState getPlaybackState_impl();
+    void addPlaybackListener_impl(MediaPlayerBase.PlaybackListener listener, Handler handler);
+    void removePlaybackListener_impl(MediaPlayerBase.PlaybackListener listener);
+}
diff --git a/media/java/android/media/update/MediaSession2Provider.java b/media/java/android/media/update/MediaSession2Provider.java
new file mode 100644
index 0000000..36fd182
--- /dev/null
+++ b/media/java/android/media/update/MediaSession2Provider.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2018 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.media.update;
+
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2.ControllerInfo;
+import android.media.SessionToken;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public interface MediaSession2Provider extends MediaPlayerBaseProvider {
+    void setPlayer_impl(MediaPlayerBase player) throws IllegalArgumentException;
+    MediaPlayerBase getPlayer_impl();
+    SessionToken getToken_impl();
+    List<ControllerInfo> getConnectedControllers_impl();
+
+    /**
+     * @hide
+     */
+    interface ControllerInfoProvider {
+        String getPackageName_impl();
+        int getUid_impl();
+        boolean isTrusted_impl();
+        int hashCode_impl();
+        boolean equals_impl(ControllerInfoProvider obj);
+    }
+}
diff --git a/media/java/android/media/update/MediaSessionService2Provider.java b/media/java/android/media/update/MediaSessionService2Provider.java
new file mode 100644
index 0000000..1174915
--- /dev/null
+++ b/media/java/android/media/update/MediaSessionService2Provider.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2018 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.media.update;
+
+import android.content.Intent;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2.MediaNotification;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.IBinder;
+
+/**
+ * @hide
+ */
+public interface MediaSessionService2Provider {
+    MediaSession2 getSession_impl();
+    MediaNotification onUpdateNotification_impl(PlaybackState state);
+
+    // Service
+    void onCreate_impl();
+    IBinder onBind_impl(Intent intent);
+}
diff --git a/media/java/android/media/update/StaticProvider.java b/media/java/android/media/update/StaticProvider.java
index 1a0df52..fef2cbb 100644
--- a/media/java/android/media/update/StaticProvider.java
+++ b/media/java/android/media/update/StaticProvider.java
@@ -17,11 +17,23 @@
 package android.media.update;
 
 import android.annotation.Nullable;
-import android.annotation.SystemApi;
+import android.content.Context;
+import android.media.IMediaSession2Callback;
+import android.media.MediaBrowser2;
+import android.media.MediaBrowser2.BrowserCallback;
+import android.media.MediaController2;
+import android.media.MediaController2.ControllerCallback;
+import android.media.MediaLibraryService2;
+import android.media.MediaPlayerBase;
+import android.media.MediaSession2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.util.AttributeSet;
 import android.widget.MediaControlView2;
 import android.widget.VideoView2;
 
+import java.util.concurrent.Executor;
+
 /**
  * Interface for connecting the public API to an updatable implementation.
  *
@@ -37,4 +49,20 @@
     VideoView2Provider createVideoView2(
             VideoView2 instance, ViewProvider superProvider,
             @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes);
+
+    MediaSession2Provider createMediaSession2(MediaSession2 mediaSession2, Context context,
+            MediaPlayerBase player, String id, MediaSession2.SessionCallback callback);
+    MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
+            MediaSession2.ControllerInfo instance, Context context, int uid, int pid,
+            String packageName, IMediaSession2Callback callback);
+    MediaController2Provider createMediaController2(
+            MediaController2 instance, Context context, SessionToken token,
+            ControllerCallback callback, Executor executor);
+    MediaBrowser2Provider createMediaBrowser2(
+            MediaBrowser2 instance, Context context, SessionToken token,
+            BrowserCallback callback, Executor executor);
+    MediaSessionService2Provider createMediaSessionService2(
+            MediaSessionService2 instance);
+    MediaSessionService2Provider createMediaLibraryService2(
+            MediaLibraryService2 instance);
 }
diff --git a/media/java/android/media/update/ViewProvider.java b/media/java/android/media/update/ViewProvider.java
index e542404..78c5b36 100644
--- a/media/java/android/media/update/ViewProvider.java
+++ b/media/java/android/media/update/ViewProvider.java
@@ -37,6 +37,8 @@
 // TODO @SystemApi
 public interface ViewProvider {
     // TODO Add more (all?) methods from View
+    void onAttachedToWindow_impl();
+    void onDetachedFromWindow_impl();
     CharSequence getAccessibilityClassName_impl();
     boolean onTouchEvent_impl(MotionEvent ev);
     boolean onTrackballEvent_impl(MotionEvent ev);
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 51c9e5f..bbed93d 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "android_media_MediaDrm.h"
+#include "android_media_MediaMetricsJNI.h"
 
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
@@ -130,6 +131,26 @@
     jclass classId;
 };
 
+struct HDCPLevels {
+    jint kHdcpLevelUnknown;
+    jint kHdcpNone;
+    jint kHdcpV1;
+    jint kHdcpV2;
+    jint kHdcpV2_1;
+    jint kHdcpV2_2;
+    jint kHdcpNoOutput;
+} gHdcpLevels;
+
+struct SecurityLevels {
+    jint kSecurityLevelUnknown;
+    jint kSecurityLevelSwSecureCrypto;
+    jint kSecurityLevelSwSecureDecode;
+    jint kSecurityLevelHwSecureCrypto;
+    jint kSecurityLevelHwSecureDecode;
+    jint kSecurityLevelHwSecureAll;
+} gSecurityLevels;
+
+
 struct fields_t {
     jfieldID context;
     jmethodID post_event;
@@ -565,12 +586,19 @@
     return old;
 }
 
-static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
-{
+static bool CheckDrm(JNIEnv *env, const sp<IDrm> &drm) {
     if (drm == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException", "MediaDrm obj is null");
         return false;
     }
+    return true;
+}
+
+static bool CheckSession(JNIEnv *env, const sp<IDrm> &drm, jbyteArray const &jsessionId)
+{
+    if (!CheckDrm(env, drm)) {
+        return false;
+    }
 
     if (jsessionId == NULL) {
         jniThrowException(env, "java/lang/IllegalArgumentException", "sessionId is null");
@@ -579,7 +607,7 @@
     return true;
 }
 
-static void android_media_MediaDrm_release(JNIEnv *env, jobject thiz) {
+static void android_media_MediaDrm_native_release(JNIEnv *env, jobject thiz) {
     sp<JDrm> drm = setDrm(env, thiz, NULL);
     if (drm != NULL) {
         drm->setListener(NULL);
@@ -625,6 +653,34 @@
     GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I");
     gCertificateTypes.kCertificateTypeX509 = env->GetStaticIntField(clazz, field);
 
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_LEVEL_UNKNOWN", "I");
+    gHdcpLevels.kHdcpLevelUnknown = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_NONE", "I");
+    gHdcpLevels.kHdcpNone = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_V1", "I");
+    gHdcpLevels.kHdcpV1 = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2", "I");
+    gHdcpLevels.kHdcpV2 = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2_1", "I");
+    gHdcpLevels.kHdcpV2_1 = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_V2_2", "I");
+    gHdcpLevels.kHdcpV2_2 = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HDCP_NO_DIGITAL_OUTPUT", "I");
+    gHdcpLevels.kHdcpNoOutput = env->GetStaticIntField(clazz, field);
+
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_UNKNOWN", "I");
+    gSecurityLevels.kSecurityLevelUnknown = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_CRYPTO", "I");
+    gSecurityLevels.kSecurityLevelSwSecureCrypto = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_DECODE", "I");
+    gSecurityLevels.kSecurityLevelSwSecureDecode = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_CRYPTO", "I");
+    gSecurityLevels.kSecurityLevelHwSecureCrypto = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_DECODE", "I");
+    gSecurityLevels.kSecurityLevelHwSecureDecode = env->GetStaticIntField(clazz, field);
+    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I");
+    gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
+
     FIND_CLASS(clazz, "android/media/MediaDrm$KeyRequest");
     GET_FIELD_ID(gFields.keyRequest.data, clazz, "mData", "[B");
     GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;");
@@ -724,11 +780,6 @@
     setDrm(env, thiz, drm);
 }
 
-static void android_media_MediaDrm_native_finalize(
-        JNIEnv *env, jobject thiz) {
-    android_media_MediaDrm_release(env, thiz);
-}
-
 static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
     JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType) {
 
@@ -971,9 +1022,7 @@
     JNIEnv *env, jobject thiz, jint jcertType, jstring jcertAuthority) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1018,9 +1067,7 @@
     JNIEnv *env, jobject thiz, jbyteArray jresponse) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1057,9 +1104,7 @@
     JNIEnv *env, jobject thiz) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1078,9 +1123,7 @@
     JNIEnv *env, jobject thiz, jbyteArray ssid) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1099,9 +1142,7 @@
     JNIEnv *env, jobject thiz, jbyteArray jssRelease) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return;
     }
 
@@ -1116,9 +1157,7 @@
     JNIEnv *env, jobject thiz) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return;
     }
 
@@ -1127,13 +1166,173 @@
     throwExceptionAsNecessary(env, err, "Failed to release all secure stops");
 }
 
+
+static jint HdcpLevelTojint(DrmPlugin::HdcpLevel level) {
+    switch(level) {
+    case DrmPlugin::kHdcpLevelUnknown:
+        return gHdcpLevels.kHdcpLevelUnknown;
+    case DrmPlugin::kHdcpNone:
+        return gHdcpLevels.kHdcpNone;
+    case DrmPlugin::kHdcpV1:
+        return gHdcpLevels.kHdcpV1;
+    case DrmPlugin::kHdcpV2:
+        return gHdcpLevels.kHdcpV2;
+    case DrmPlugin::kHdcpV2_1:
+        return gHdcpLevels.kHdcpV2_1;
+    case DrmPlugin::kHdcpV2_2:
+        return gHdcpLevels.kHdcpV2_2;
+    case DrmPlugin::kHdcpNoOutput:
+        return gHdcpLevels.kHdcpNoOutput;
+    }
+    return gHdcpLevels.kHdcpNone;
+}
+
+static jint android_media_MediaDrm_getConnectedHdcpLevel(JNIEnv *env,
+        jobject thiz) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckDrm(env, drm)) {
+        return gHdcpLevels.kHdcpNone;
+    }
+
+    DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpNone;
+    DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpNone;
+
+    status_t err = drm->getHdcpLevels(&connected, &max);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) {
+        return gHdcpLevels.kHdcpLevelUnknown;
+    }
+    return HdcpLevelTojint(connected);
+}
+
+static jint android_media_MediaDrm_getMaxHdcpLevel(JNIEnv *env,
+        jobject thiz) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckDrm(env, drm)) {
+        return gHdcpLevels.kHdcpLevelUnknown;
+    }
+
+    DrmPlugin::HdcpLevel connected = DrmPlugin::kHdcpLevelUnknown;
+    DrmPlugin::HdcpLevel max = DrmPlugin::kHdcpLevelUnknown;
+
+    status_t err = drm->getHdcpLevels(&connected, &max);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get HDCP levels")) {
+        return gHdcpLevels.kHdcpLevelUnknown;
+    }
+    return HdcpLevelTojint(max);
+}
+
+static jint android_media_MediaDrm_getOpenSessionCount(JNIEnv *env,
+        jobject thiz) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckDrm(env, drm)) {
+        return 0;
+    }
+
+    uint32_t open = 0, max = 0;
+    status_t err = drm->getNumberOfSessions(&open, &max);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) {
+        return 0;
+    }
+    return open;
+}
+
+static jint android_media_MediaDrm_getMaxSessionCount(JNIEnv *env,
+        jobject thiz) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckDrm(env, drm)) {
+        return 0;
+    }
+
+    uint32_t open = 0, max = 0;
+    status_t err = drm->getNumberOfSessions(&open, &max);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get number of sessions")) {
+        return 0;
+    }
+    return max;
+}
+
+static jint android_media_MediaDrm_getSecurityLevel(JNIEnv *env,
+        jobject thiz, jbyteArray jsessionId) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckSession(env, drm, jsessionId)) {
+        return gSecurityLevels.kSecurityLevelUnknown;
+    }
+
+    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
+
+    DrmPlugin::SecurityLevel level = DrmPlugin::kSecurityLevelUnknown;
+
+    status_t err = drm->getSecurityLevel(sessionId, &level);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to get security level")) {
+        return gSecurityLevels.kSecurityLevelUnknown;
+    }
+
+    switch(level) {
+    case DrmPlugin::kSecurityLevelSwSecureCrypto:
+        return gSecurityLevels.kSecurityLevelSwSecureCrypto;
+    case DrmPlugin::kSecurityLevelSwSecureDecode:
+        return gSecurityLevels.kSecurityLevelSwSecureDecode;
+    case DrmPlugin::kSecurityLevelHwSecureCrypto:
+        return gSecurityLevels.kSecurityLevelHwSecureCrypto;
+    case DrmPlugin::kSecurityLevelHwSecureDecode:
+        return gSecurityLevels.kSecurityLevelHwSecureDecode;
+    case DrmPlugin::kSecurityLevelHwSecureAll:
+        return gSecurityLevels.kSecurityLevelHwSecureAll;
+    default:
+        return gSecurityLevels.kSecurityLevelUnknown;
+    }
+}
+
+
+static void android_media_MediaDrm_setSecurityLevel(JNIEnv *env,
+        jobject thiz, jbyteArray jsessionId, jint jlevel) {
+    sp<IDrm> drm = GetDrm(env, thiz);
+
+    if (!CheckSession(env, drm, jsessionId)) {
+        return;
+    }
+
+    Vector<uint8_t> sessionId(JByteArrayToVector(env, jsessionId));
+    DrmPlugin::SecurityLevel level;
+
+    if (jlevel == gSecurityLevels.kSecurityLevelSwSecureCrypto) {
+        level = DrmPlugin::kSecurityLevelSwSecureCrypto;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelSwSecureDecode) {
+        level = DrmPlugin::kSecurityLevelSwSecureDecode;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureCrypto) {
+        level = DrmPlugin::kSecurityLevelHwSecureCrypto;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureDecode) {
+        level = DrmPlugin::kSecurityLevelHwSecureDecode;
+    } else if (jlevel == gSecurityLevels.kSecurityLevelHwSecureAll) {
+        level = DrmPlugin::kSecurityLevelHwSecureAll;
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException", "Invalid security level");
+        return;
+    }
+
+    status_t err = drm->setSecurityLevel(sessionId, level);
+
+    if (throwExceptionAsNecessary(env, err, "Failed to set security level")) {
+        return;
+    }
+}
+
+
 static jstring android_media_MediaDrm_getPropertyString(
     JNIEnv *env, jobject thiz, jstring jname) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1159,9 +1358,7 @@
     JNIEnv *env, jobject thiz, jstring jname) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return NULL;
     }
 
@@ -1187,9 +1384,7 @@
     JNIEnv *env, jobject thiz, jstring jname, jstring jvalue) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return;
     }
 
@@ -1217,9 +1412,7 @@
     JNIEnv *env, jobject thiz, jstring jname, jbyteArray jvalue) {
     sp<IDrm> drm = GetDrm(env, thiz);
 
-    if (drm == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                          "MediaDrm obj is null");
+    if (!CheckDrm(env, drm)) {
         return;
     }
 
@@ -1411,6 +1604,31 @@
     return match;
 }
 
+static jobject
+android_media_MediaDrm_native_getMetrics(JNIEnv *env, jobject thiz)
+{
+    sp<IDrm> drm = GetDrm(env, thiz);
+    if (drm == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "MediaDrm obj is null");
+        return NULL;
+    }
+
+    // Retrieve current metrics snapshot from drm.
+    MediaAnalyticsItem item ;
+    status_t err = drm->getMetrics(&item);
+    if (err != OK) {
+        ALOGE("getMetrics failed: %d", (int)err);
+        return (jobject) NULL;
+    }
+
+    jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, &item, NULL);
+    if (mybundle == NULL) {
+        ALOGE("getMetrics metric conversion failed");
+    }
+
+    return mybundle;
+}
 
 static jbyteArray android_media_MediaDrm_signRSANative(
     JNIEnv *env, jobject /* thiz */, jobject jdrm, jbyteArray jsessionId,
@@ -1445,15 +1663,13 @@
 
 
 static const JNINativeMethod gMethods[] = {
-    { "release", "()V", (void *)android_media_MediaDrm_release },
+    { "native_release", "()V", (void *)android_media_MediaDrm_native_release },
+
     { "native_init", "()V", (void *)android_media_MediaDrm_native_init },
 
     { "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
       (void *)android_media_MediaDrm_native_setup },
 
-    { "native_finalize", "()V",
-      (void *)android_media_MediaDrm_native_finalize },
-
     { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;)Z",
       (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
 
@@ -1497,6 +1713,24 @@
     { "releaseAllSecureStops", "()V",
       (void *)android_media_MediaDrm_releaseAllSecureStops },
 
+    { "getConnectedHdcpLevel", "()I",
+      (void *)android_media_MediaDrm_getConnectedHdcpLevel },
+
+    { "getMaxHdcpLevel", "()I",
+      (void *)android_media_MediaDrm_getMaxHdcpLevel },
+
+    { "getOpenSessionCount", "()I",
+      (void *)android_media_MediaDrm_getOpenSessionCount },
+
+    { "getMaxSessionCount", "()I",
+      (void *)android_media_MediaDrm_getMaxSessionCount },
+
+    { "getSecurityLevel", "([B)I",
+      (void *)android_media_MediaDrm_getSecurityLevel },
+
+    { "setSecurityLevel", "([BI)V",
+      (void *)android_media_MediaDrm_setSecurityLevel },
+
     { "getPropertyString", "(Ljava/lang/String;)Ljava/lang/String;",
       (void *)android_media_MediaDrm_getPropertyString },
 
@@ -1531,6 +1765,9 @@
 
     { "signRSANative", "(Landroid/media/MediaDrm;[BLjava/lang/String;[B[B)[B",
       (void *)android_media_MediaDrm_signRSANative },
+
+    { "getMetricsNative", "()Landroid/os/PersistableBundle;",
+      (void *)android_media_MediaDrm_native_getMetrics },
 };
 
 int register_android_media_Drm(JNIEnv *env) {
diff --git a/packages/SettingsLib/res/drawable/btn_borderless_rect.xml b/packages/SettingsLib/res/drawable/btn_borderless_rect.xml
new file mode 100644
index 0000000..9eaba83
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/btn_borderless_rect.xml
@@ -0,0 +1,31 @@
+<!--
+     Copyright (C) 2018 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.
+-->
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:insetTop="4dp"
+    android:insetBottom="4dp">
+    <ripple
+        android:color="?android:attr/colorControlHighlight" >
+
+        <item android:id="@android:id/mask">
+            <shape>
+                <corners android:radius="2dp" />
+                <solid android:color="@android:color/white" />
+            </shape>
+        </item>
+
+    </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_minus.xml b/packages/SettingsLib/res/drawable/ic_minus.xml
new file mode 100644
index 0000000..9a929a4
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_minus.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 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:height="24.0dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0"
+        android:width="24.0dp" >
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,13H6c-0.55,0 -1,-0.45 -1,-1v0c0,-0.55 0.45,-1 1,-1h12c0.55,0 1,0.45 1,1v0C19,12.55 18.55,13 18,13z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_plus.xml b/packages/SettingsLib/res/drawable/ic_plus.xml
new file mode 100644
index 0000000..2a10e70
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_plus.xml
@@ -0,0 +1,24 @@
+<!--
+     Copyright (C) 2018 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:height="24.0dp"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0"
+        android:width="24.0dp" >
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M18,13h-5v5c0,0.55 -0.45,1 -1,1h0c-0.55,0 -1,-0.45 -1,-1v-5H6c-0.55,0 -1,-0.45 -1,-1v0c0,-0.55 0.45,-1 1,-1h5V6c0,-0.55 0.45,-1 1,-1h0c0.55,0 1,0.45 1,1v5h5c0.55,0 1,0.45 1,1v0C19,12.55 18.55,13 18,13z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/zen_mode_condition.xml b/packages/SettingsLib/res/layout/zen_mode_condition.xml
new file mode 100644
index 0000000..c85a892
--- /dev/null
+++ b/packages/SettingsLib/res/layout/zen_mode_condition.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:clipChildren="false"
+                android:layout_marginStart="1dp"
+                android:layout_marginEnd="0dp"
+                android:layout_weight="1"
+                android:gravity="center_vertical" >
+
+    <LinearLayout
+        android:id="@android:id/content"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:minHeight="48dp"
+        android:gravity="center_vertical"
+        android:layout_centerVertical="true"
+        android:orientation="vertical"
+        android:layout_toEndOf="@android:id/checkbox"
+        android:layout_toStartOf="@android:id/button1">
+
+        <TextView
+            android:id="@android:id/text1"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:ellipsize="end"
+            android:textAlignment="viewStart"
+            android:maxLines="1"
+            android:textColor="?android:attr/textColorPrimary"
+            android:textSize="16sp"/>
+
+        <TextView
+            android:id="@android:id/text2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/zen_mode_condition_detail_item_interline_spacing"
+            android:ellipsize="end"
+            android:textAlignment="viewStart"
+            android:maxLines="1"
+            android:textColor="?android:attr/textColorSecondary"
+            android:textSize="14sp"/>
+
+    </LinearLayout>
+
+    <ImageView
+        android:id="@android:id/button1"
+        style="@style/BorderlessButton"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_centerVertical="true"
+        android:scaleType="center"
+        android:layout_toStartOf="@android:id/button2"
+        android:contentDescription="@string/accessibility_manual_zen_less_time"
+        android:tint="?android:attr/colorAccent"
+        android:src="@drawable/ic_minus" />
+
+    <ImageView
+        android:id="@android:id/button2"
+        style="@style/BorderlessButton"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_alignParentEnd="true"
+        android:scaleType="center"
+        android:layout_centerVertical="true"
+        android:contentDescription="@string/accessibility_manual_zen_more_time"
+        android:tint="?android:attr/colorAccent"
+        android:src="@drawable/ic_plus" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/zen_mode_radio_button.xml b/packages/SettingsLib/res/layout/zen_mode_radio_button.xml
new file mode 100644
index 0000000..4c0faed
--- /dev/null
+++ b/packages/SettingsLib/res/layout/zen_mode_radio_button.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+<RadioButton
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/checkbox"
+    android:layout_width="40dp"
+    android:layout_marginStart="7dp"
+    android:layout_marginEnd="4dp"
+    android:layout_height="48dp"
+    android:layout_alignParentStart="true"
+    android:gravity="center"
+    android:paddingTop="10dp"
+    android:paddingBottom="10dp">
+
+</RadioButton>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml
new file mode 100644
index 0000000..ac56a2d
--- /dev/null
+++ b/packages/SettingsLib/res/layout/zen_mode_turn_on_dialog_container.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2018 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.
+-->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:fillViewport ="true"
+            android:orientation="vertical">
+
+    <com.android.settingslib.notification.ZenRadioLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/zen_conditions"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="8dp"
+        android:layout_marginEnd="4dp"
+        android:layout_marginStart="4dp"
+        android:paddingBottom="4dp"
+        android:orientation="horizontal">
+        <RadioGroup
+            android:id="@+id/zen_radio_buttons"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <LinearLayout
+            android:id="@+id/zen_radio_buttons_content"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:orientation="vertical"/>
+    </com.android.settingslib.notification.ZenRadioLayout>
+
+</ScrollView>
\ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index ff81fc1..93c0e10 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Inligtingruiling"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Draadlose skermsertifisering"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktiveer Wi-Fi-woordryke aanmelding"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressiewe Wi‑Fi-na-mobiel-oorhandiging"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Laat altyd Wi-Fi-swerfskanderings toe"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiele data is altyd aktief"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardewareversnelling vir verbinding"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Voer gasheernaam van DNS-verskaffer in"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Wys opsies vir draadlose skermsertifisering"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Verhoog Wi-Fi-aantekeningvlak, wys per SSID RSSI in Wi‑Fi-kieser"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Wanneer dit geaktiveer is, sal Wi-Fi die dataverbinding aggressiewer na mobiel oordra wanneer die Wi-Fi-sein swak is"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Laat toe of verbied Wi-Fi-swerfskanderings op grond van die hoeveelheid dataverkeer wat op die koppelvlak teenwoordig is"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Loggerbuffer se groottes"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Kies loggergroottes per logbuffer"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 111b7cf..83fd2af 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"አውታረ መረብ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"የWi‑Fi ተጨማሪ ቃላት ምዝግብ ማስታወሻ መያዝ"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"አስገዳጅ ከWi‑Fi ወደ ሞባይል ማቀበል"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ሁልጊዜ የWi‑Fi ማንቀሳቀስ ቅኝቶችን ይፍቀዱ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"የተንቀሳቃሽ ስልክ ውሂብ ሁልጊዜ ገቢር ነው"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"የሃርድዌር ማቀላጠፊያን በማስተሳሰር ላይ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"የዲኤንኤስ አቅራቢ አስተናጋጅ ስም ያስገቡ"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"የገመድ አልባ ማሳያ እውቅና ማረጋገጫ አማራጮችን አሳይ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"የWi‑Fi ምዝግብ ማስታወሻ አያያዝ ደረጃ ጨምር፣ በWi‑Fi መምረጫ ውስጥ በአንድ SSID RSSI አሳይ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ሲነቃ የWi‑Fi ምልክት ዝቅተኛ ሲሆን Wi‑Fi የውሂብ ግንኙነት ለሞባይል ማስረከብ ላይ ይበልጥ አስገዳጅ ይሆናል"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"በበይነገጹ ላይ ባለው የውሂብ ትራፊክ መጠን ላይ ተመስርተው የWi‑Fi ማንቀሳቀስ ቅኝቶችን ይፍቀዱ/ይከልክሉ"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"የምዝግብ ማስታወሻ ያዥ መጠኖች"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"በአንድ ምዝግብ ማስታወሻ ቋጥ የሚኖረው የምዝግብ ማስታወሻ ያዥ መጠኖች ይምረጡ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index b101a1d..a541cbd 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"الشبكات"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"شهادة عرض شاشة لاسلكي"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"‏تمكين تسجيل Wi‑Fi Verbose"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"‏تسليم Wi-Fi حاد إلى جوّال"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"‏السماح دائمًا بعمليات فحص Wi-Fi للتجوال"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"بيانات الجوّال نشطة دائمًا"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"تسريع الأجهزة للتوصيل"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"أدخل اسم مضيف مزوّد نظام أسماء النطاقات"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"عرض خيارات شهادة عرض شاشة لاسلكي"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏زيادة مستوى تسجيل Wi-Fi، وعرض لكل SSID RSSI في منتقي Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"‏عند تمكينه، سيكون Wi-Fi أكثر حدة في تسليم اتصال البيانات إلى الجوّال، وذلك عندما تكون إشارة WiFi منخفضة"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"‏السماح/عدم السماح بعمليات فحص Wi-Fi للتجوال بناءً على حجم حركة البيانات في الواجهة"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"أحجام ذاكرة التخزين المؤقت للتسجيل"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"حدد أحجامًا أكبر لكل ذاكرة تخزين مؤقت للتسجيل"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 03c9b92..5b4e45c 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Şəbəkələşmə"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Simsiz displey sertifikatlaşması"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi Çoxsözlü Girişə icazə verin"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Möbül ötürücüyə aqressiv Wi‑Fi"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi axtarışlarına həmişə icazə verin"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil data həmişə aktiv"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Birləşmə üçün avadanlıq akselerasiyası"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS provayderinin host adını daxil edin"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz displey sertifikatlaşması üçün seçimləri göstərir"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi giriş səviyyəsini qaldırın, Wi‑Fi seçəndə hər SSID RSSI üzrə göstərin"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Aktiv edildikdə, Wi-Fi siqnalı zəif olan zaman, data bağlantısını mobilə ötürərəkən Wi-Fi daha aqressiv olacaq"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wi‑Fi Axtarışlarına data trafikinə əsasən İcazə verin/Qadağan edin"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger bufer ölçüləri"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Hər jurnal buferinı Logger ölçüsü seçin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 62e4b4c..0545021 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Sertifikacija bežičnog ekrana"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući detaljniju evidenciju za Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresivan prelaz sa Wi‑Fi mreže na mobilnu"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Uvek dozvoli skeniranje Wi‑Fi-ja u romingu"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilni podaci su uvek aktivni"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzanje privezivanja"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite ime hosta dobavljača usluge DNS-a"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaz opcija za sertifikaciju bežičnog ekrana"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećava nivo evidentiranja za Wi‑Fi. Prikaz po SSID RSSI-u u biraču Wi‑Fi mreže"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kad se omogući, Wi‑Fi će biti agresivniji pri prebacivanju mreže za prenos podataka na mobilnu ako je Wi‑Fi signal slab"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/zabrani skeniranje Wi-Fi-ja u romingu na osnovu prisutnog protoka podataka na interfejsu"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera podataka u programu za evidentiranje"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izaberite veličine po baferu evidencije"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index c648ea5..05be2228 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Сеткі"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Сертыфікацыя бесправаднога дысплея"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Уключыць падрабязны журнал Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Інтэнсіўны пераход з Wi‑Fi на маб. сетку"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Заўсёды дазваляць роўмінгавае сканіраванне Wi‑Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мабільная перадача даных заўсёды актыўная"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Апаратнае паскарэнне ў рэжыме мадэма"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Увядзіце імя вузла аператара DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Паказаць опцыі сертыфікацыі бесправаднога дысплея"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Падвыс. узровень дэтал-цыі журнала Wi‑Fi у залежн. ад SSID RSSI у Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Калі гэта функцыя ўключана, Wi-Fi будзе больш інтэнсіўна імкнуцца перайсці на падключ. маб. перад. даных пры слабым сігнале Wi‑Fi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дазволіць/забараніць роўмінгавае сканіраванне Wi‑Fi ў залежнасці ад аб\'ёму трафіку даных у інтэрфейсе"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Памеры буфера для сродку вядзення журнала"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Выберыце памеры сродку вядзення журнала для буфераў журнала"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 83c1ec0..ea67a98 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Мрежи"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Безжичен дисплей"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"„Многословно“ регистр. на Wi‑Fi: Актив."</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi-Fi към моб. мрежи: Агресивно предав."</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Сканирането за роуминг на Wi-Fi да е разрешено винаги"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Винаги активни мобилни данни"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардуерно ускорение за тетъринга"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Въведете името на хоста на DNS доставчика"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показване на опциите за сертифициране на безжичния дисплей"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"По-подробно регистр. на Wi‑Fi – данни за RSSI на SSID в инстр. за избор на Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"При активиране предаването на връзката за данни от Wi-Fi към мобилната мрежа ще е по-агресивно, когато сигналът за Wi-Fi е слаб"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Разрешаване/забраняване на сканирането за роуминг на Wi-Fi въз основа на посочения в интерфейса обем на трафика на данни"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Размери на регистрац. буфери"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Размер на един рег. буфер: Избор"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index baf9e2c..f30ed2b 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"নেটওয়ার্কিং"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ওয়্যারলেস ডিসপ্লে সার্টিফিকেশন"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"ওয়াই-ফাই ভারবোস লগিং সক্ষম করুন"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ওয়াই-ফাই থেকে মোবাইলে তৎপর হস্তান্তর"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"সর্বদা ওয়াই ফাই রোম স্ক্যানকে অনুমতি দিন"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"মোবাইল ডেটা সব সময় সক্রিয় থাক"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"টিথারিং হার্ডওয়্যার অ্যাক্সিলারেশন"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ডিএনএস প্রদানকারীর হোস্টনেম লিখুন"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ওয়্যারলেস প্রদর্শন সার্টিফিকেশন জন্য বিকল্পগুলি দেখান"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ওয়াই-ফাই লগিং স্তর বাড়ান, ওয়াই-ফাই চয়নকারীতে SSID RSSI অনুযায়ী দেখান"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"সক্ষম করা থাকলে, ওয়াই ফাই সিগন্যালের মান খারাপ হলে ডেটা সংযোগ মোবাইলের কাছে হস্তান্তর করার জন্য ওয়াই ফাই আরো বেশি তৎপর হবে।"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ইন্টারফেসে উপস্থিত ডেটা ট্রাফিকের পরিমাণের উপরে ভিত্তি করে ওয়াই-ফাই রোম স্ক্যানকে অনুমোদিত/অননুমোদিত করুন"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"লগার বাফারের আকারগুলি"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"লগ বাফার প্রতি অপেক্ষাকৃত বড় আকারগুলির বেছে নিন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 88b01e9..915eb49 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifikacija bežičnog prikaza"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogućiti Wi-Fi Verbose zapisivanje"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresivni prijenos s Wi-Fi mreže na mob."</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Uvijek dopustiti Wi-Fi lutajuće skeniranje"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilna mreža za prijenos podataka je uvijek aktivna"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzavanje dijeljenja veze"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite naziv host računara pružaoca DNS-a"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži opcije za certifikaciju Bežičnog prikaza"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećajte nivo Wi-Fi zapisivanja, pokazati po SSID RSSI Wi-Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kada je omogućeno, Wi-Fi veza će u slučaju slabog signala agresivnije predavati vezu za prijenos podataka na mobilnu vezu"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dozvoli/Zabrani Wi-Fi lutajuće skeniranje na osnovu količine podatkovnog saobraćaja prisutnog na interfejsu"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine bafera za zapisnik"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Odaberite veličine za Logger prema međumemoriji evidencije"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 48223ce..d8c6693 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Xarxes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificació de pantalla sense fil"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Activa el registre Wi‑Fi detallat"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Transferència agressiva de Wi-Fi a mòbil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permet sempre cerca de Wi-Fi en ininerància"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dades mòbils sempre actives"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Acceleració per maquinari per compartir la xarxa"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introdueix el nom d\'amfitrió del proveïdor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra les opcions de certificació de pantalla sense fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Augmenta nivell de registre Wi‑Fi i mostra\'l per SSID RSSI al Selector de Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Quan s\'activa, la Wi-Fi és més agressiva en transferir la connexió de dades al mòbil quan el senyal de la Wi-Fi sigui dèbil"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permet/No permetis cerques de xarxes Wi-Fi en itinerància basades en la quantitat de dades presents a la interfície"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Mides memòria intermèdia Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Mida Logger per memòria intermèdia"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 88bbaa7..41ff8e2 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Sítě"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifikace bezdrát. displeje"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Podrobné protokolování Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresivní předání z Wi-Fi na mobilní síť"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Vždy povolit Wi-Fi roaming"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilní data jsou vždy aktivní"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwarová akcelerace tetheringu"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Zadejte název hostitele poskytovatele DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobrazit možnosti certifikace bezdrátového displeje"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšit úroveň protokolování Wi‑Fi zobrazenou v SSID a RSSI při výběru sítě Wi‑Fi."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Pokud je tato možnost zapnuta, bude síť Wi-Fi při předávání datového připojení mobilní síti při slabém signálu Wi-Fi agresivnější."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Povolí nebo zakáže Wi-Fi roaming na základě množství datového provozu na rozhraní."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Vyrovnávací paměť protokol. nástroje"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Velikost vyrovnávací paměti protokol. nástroje"</string>
@@ -395,7 +393,7 @@
     <string name="content_description_menu_button" msgid="8182594799812351266">"Nabídka"</string>
     <string name="retail_demo_reset_message" msgid="118771671364131297">"Chcete-li v ukázkovém režimu obnovit zařízení do továrního nastavení, zadejte heslo"</string>
     <string name="retail_demo_reset_next" msgid="8356731459226304963">"Další"</string>
-    <string name="retail_demo_reset_title" msgid="696589204029930100">"Je třeba zadat heslo"</string>
+    <string name="retail_demo_reset_title" msgid="696589204029930100">"Zadejte heslo"</string>
     <string name="active_input_method_subtypes" msgid="3596398805424733238">"Aktivní metody zadávání"</string>
     <string name="use_system_language_to_select_input_method_subtypes" msgid="5747329075020379587">"Použít systémové jazyky"</string>
     <string name="failed_to_open_app_settings_toast" msgid="1251067459298072462">"Nastavení aplikace <xliff:g id="SPELL_APPLICATION_NAME">%1$s</xliff:g> se nepodařilo otevřít"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 6f36bc5..17d1f08 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Netværk"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificering af trådløs skærm"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivér detaljeret Wi-Fi-logføring"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Tvungen skift fra Wi-Fi til mobildata"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Tillad altid scanning af Wi-Fi-roaming"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata er altid aktiveret"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwareacceleration ved netdeling"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Angiv hostname for DNS-udbyder"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis valgmuligheder for certificering af trådløs skærm"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øg mængden af Wi‑Fi-logføring. Vis opdelt efter SSID RSSI i Wi‑Fi-vælgeren"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Når dette er aktiveret, gennemtvinges en overdragelse af dataforbindelsen fra Wi-Fi til mobilnetværk, når Wi-Fi-signalet er svagt"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillad/forbyd scanning i forbindelse med Wi-Fi-roaming afhængigt af mængden af datatrafik i grænsefladen"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Størrelser for Logger-buffer"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Vælg Logger-størrelser pr. logbuffer"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 4d53e2d..2fbac93 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Netzwerke"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Kabellose Übertragung"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Ausführliche WLAN-Protokolle aktivieren"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressives Handover von WLAN an Mobilfunk"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"WLAN-Roamingsuchen immer zulassen"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile Datennutzung immer aktiviert"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwarebeschleunigung für Tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Hostname des DNS-Anbieters eingeben"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Optionen zur Zertifizierung für kabellose Übertragung anzeigen"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Level für WLAN-Protokollierung erhöhen, in WiFi Picker pro SSID-RSSI anzeigen"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Wenn diese Option aktiviert ist, ist das WLAN bei schwachem Signal bei der Übergabe der Datenverbindung an den Mobilfunk aggressiver"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"WLAN-Roamingsuchen je nach Umfang des Datentraffics an der Schnittstelle zulassen bzw. nicht zulassen"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger-Puffergrößen"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Größe pro Protokollpuffer wählen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index e510356..25de914 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Δικτύωση"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Πιστοποίηση ασύρματης οθόνης"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Ενεργοποίηση λεπτομερ. καταγραφής Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Επιθ.μεταβ. Wi-Fi σε δίκτυο κιν.τηλ."</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Να επιτρέπεται πάντα η σάρωση Wi-Fi κατά την περιαγωγή"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Πάντα ενεργά δεδομένα κινητής τηλεφωνίας"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Σύνδεση επιτάχυνσης υλικού"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Εισαγάγετε το όνομα κεντρικού υπολογιστή του παρόχου DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Εμφάνιση επιλογών για πιστοποίηση ασύρματης οθόνης"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Αύξηση επιπέδου καταγ. Wi-Fi, εμφάνιση ανά SSID RSSI στο εργαλείο επιλογής Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Όταν είναι ενεργό, το Wi-Fi θα μεταβιβάζει πιο επιθετικά τη σύνδ.δεδομένων σε δίκτυο κινητής τηλ., όταν το σήμα Wi-Fi είναι χαμηλό"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Να επιτρέπεται/να μην επιτρέπεται η σάρωση Wi-Fi κατά την περιαγωγή, βάσει της ποσότητας επισκεψιμότητας δεδομένων στη διεπαφή"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Μέγεθος προσωρινής μνήμης για τη λειτουργία καταγραφής"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Μέγεθος αρχείων κατ/φής ανά προ/νή μνήμη αρχείου κατ/φής"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index a322388..9f5353f 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressive Wi‑Fi to mobile handover"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Always allow Wi‑Fi Roam Scans"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index a322388..9f5353f 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressive Wi‑Fi to mobile handover"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Always allow Wi‑Fi Roam Scans"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index a322388..9f5353f 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressive Wi‑Fi to mobile handover"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Always allow Wi‑Fi Roam Scans"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index a322388..9f5353f 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Wireless display certification"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Enable Wi‑Fi verbose logging"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressive Wi‑Fi to mobile handover"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Always allow Wi‑Fi Roam Scans"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobile data always active"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering hardware acceleration"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Enter hostname of DNS provider"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Show options for wireless display certification"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger buffer sizes"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Select Logger sizes per log buffer"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 80c2eb8..a8561fa 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‏‏‏‏‎‎Networking‎‏‎‎‏‎"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‎Wireless display certification‎‏‎‎‏‎"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‏‎‎‎‎Enable Wi‑Fi Verbose Logging‎‏‎‎‏‎"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎Aggressive Wi‑Fi to mobile handover‎‏‎‎‏‎"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‎Always allow Wi‑Fi Roam Scans‎‏‎‎‏‎"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‎Mobile data always active‎‏‎‎‏‎"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎‏‏‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‏‏‎‎Tethering hardware acceleration‎‏‎‎‏‎"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‏‎Enter hostname of DNS provider‎‏‎‎‏‎"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎Show options for wireless display certification‎‏‎‎‏‎"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‏‏‎‎Increase Wi‑Fi logging level, show per SSID RSSI in Wi‑Fi Picker‎‏‎‎‏‎"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‎When enabled, Wi‑Fi will be more aggressive in handing over the data connection to mobile, when Wi‑Fi signal is low‎‏‎‎‏‎"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‎‏‏‏‏‎‎‏‎‎‏‏‎‎‏‎‎‎‏‏‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‎Allow/Disallow Wi‑Fi Roam Scans based on the amount of data traffic present at the interface‎‏‎‎‏‎"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎Logger buffer sizes‎‏‎‎‏‎"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎Select Logger sizes per log buffer‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 65000b3..eeaf18f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificación de pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Habilitar registro detallado de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Priorizar cambio de red Wi-Fi a móvil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permitir siempre búsquedas de Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móviles siempre activados"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware de conexión mediante dispositivo portátil"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ingresa el nombre de host del proveedor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones de certificación de pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar nivel de registro Wi-Fi; mostrar por SSID RSSI en el selector de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si habilitas esta opción, se priorizará el cambio de Wi-Fi a datos móviles cuando la señal de Wi-Fi sea débil"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/no permitir las búsquedas de Wi-Fi basadas la cantidad de tráfico de datos presente en la interfaz"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Selecciona el tamaño del Logger por búfer"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 2a5e41b..97d9a10 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificación de pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Habilitar registro Wi-Fi detallado"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Transferencia agresiva de Wi-Fi a móvil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permitir siempre búsquedas de Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móviles siempre activos"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración por hardware para conexión compartida"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce el nombre de host del proveedor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opciones para la certificación de la pantalla inalámbrica"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar el nivel de registro de Wi-Fi, mostrar por SSID RSSI en el selector Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si se activa esta opción, la conexión Wi-Fi será más agresiva al pasar la conexión a datos móviles (si la señal Wi-Fi es débil)"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/No permitir búsquedas de Wi-Fi basadas en la cantidad de tráfico de datos presente en la interfaz"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de registrador"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Elige el tamaño del Logger por búfer"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index a8ac99f..b680b84 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Võrgustik"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Juhtmeta ekraaniühenduse sertifitseerimine"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Luba WiFi paljusõnaline logimine"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agress. üleminek WiFi-lt mobiilsidele"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Luba alati WiFi-rändluse skannimine"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiilne andmeside on alati aktiivne"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Jagamise riistvaraline kiirendus"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Sisestage DNS-i teenusepakkuja hostinimi"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Juhtmeta ekraaniühenduse sertifitseerimisvalikute kuvamine"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Suurenda WiFi logimistaset, kuva WiFi valijas SSID RSSI järgi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kui seade on lubatud, asendatakse nõrga signaaliga WiFi-ühendus agressiivsemalt mobiilse andmesideühendusega"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Luba/keela WiFi-rändluse skannimine liidese andmeliikluse põhjal"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logija puhvri suurused"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Vali logija suur. logipuhvri kohta"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2c81f2e..ca8ea03 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Sareak"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Hari gabeko bistaratze-egiaztatzea"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Gaitu Wi-Fi sareetan saioa hasteko modu xehatua"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Behartu Wi-Fi konexiotik datuenera aldatzera"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Onartu beti ibiltaritzan Wi-Fi sareak bilatzea"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Datu mugikorrak beti aktibo"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Konexioa partekatzeko hardwarearen azelerazioa"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Idatzi DNS hornitzailearen ostalari-izena"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Erakutsi hari gabeko bistaratze-egiaztapenaren aukerak"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Erakutsi datu gehiago Wi-Fi sareetan saioa hasterakoan. Erakutsi sarearen identifikatzailea eta seinalearen indarra Wi‑Fi sareen hautagailuan."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Aukera hori gaituz gero, gailua nahitaez aldatuko da datu mugikorren konexiora Wi-Fi seinalea ahultzen dela nabaritutakoan"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Onartu edo debekatu ibiltaritzan Wi-Fi sareak bilatzea, interfazeko datu-trafikoaren arabera"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Erregistroen buffer-tamainak"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Hautatu erregistroen buffer-tamainak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 4f3c77f..675e382 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"شبکه"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"گواهینامه نمایش بی‌سیم"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"‏فعال کردن گزارش‌گیری طولانی Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"‏Wi‑Fi قوی برای واگذاری به دستگاه همراه"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"‏اسکن‌های رومینگ Wi‑Fi همیشه مجاز است"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"داده تلفن همراه همیشه فعال باشد"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"شتاب سخت‌افزاری اتصال به اینترنت با تلفن همراه"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"‏نام میزبان ارائه‌دهنده DNS خصوصی را وارد کنید"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"نمایش گزینه‌ها برای گواهینامه نمایش بی‌سیم"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏افزایش سطح گزارش‌گیری Wi‑Fi، نمایش به ازای SSID RSSI در انتخاب‌کننده Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"‏زمانی‌که فعال است، درشرایطی که سیگنال Wi-Fi ضعیف باشد، Wi‑Fi برای واگذاری اتصال داده به دستگاه همراه قوی‌تر عمل خواهد کرد."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"‏مجاز/غیرمجاز کردن اسکن‌های رومینگ Wi‑Fi براساس مقدار ترافیک داده موجود در واسط"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"اندازه‌های حافظه موقت ثبت‌کننده"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"انتخاب اندازه‌ ثبت‌کننده در حافظه موقت ثبت"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 5932d00..ba7fcd3 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Yhteysominaisuudet"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Langattoman näytön sertifiointi"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Käytä Wi-Fin laajennettua lokikirjausta"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Vaihda herkästi Wi-Fi mobiiliyhteyteen"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Salli Wi-Fi-verkkovierailuskannaus aina"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiilidata aina käytössä"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Laitteistokiihdytyksen yhteyden jakaminen"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Anna isäntänimi tai DNS-tarjoaja."</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Näytä langattoman näytön sertifiointiin liittyvät asetukset"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Lisää Wi‑Fin lokikirjaustasoa, näytä SSID RSSI -kohtaisesti Wi‑Fi-valitsimessa."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kun asetus on käytössä, datayhteys siirtyy helpommin Wi-Fistä matkapuhelinverkkoon, jos Wi-Fi-signaali on heikko."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Salli/estä Wi-Fi-verkkovierailuskannaus liittymässä esiintyvän dataliikenteen perusteella."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Lokipuskurien koot"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Valitse puskurikohtaiset lokikoot"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index c0c29d3..f266e2d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Réseautage"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certification de l\'affichage sans fil"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Autoriser enreg. données Wi-Fi détaillées"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Passage forcé du Wi-Fi aux données cell."</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Toujours autoriser la détection de réseaux Wi-Fi en itinérance"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Données cellulaires toujours actives"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Accélération matérielle pour le partage de connexion"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Entrez le nom d\'hôte du fournisseur DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options pour la certification d\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler davantage les données Wi-Fi, afficher par SSID RSSI dans sélect. Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données cellulaires est forcé lorsque le signal Wi-Fi est faible"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Autoriser ou non la détection de réseaux Wi-Fi en itinérance en fonction de l\'importance du transfert de données dans l\'interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tailles des mémoires tampons d\'enregistreur"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Tailles enreg. par tampon journal"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 50c99e2..87f00d1 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Mise en réseau"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certification affichage sans fil"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Autoriser enreg. infos Wi-Fi détaillées"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Passage forcé Wi-Fi vers données mobiles"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Toujours autoriser la détection de réseaux Wi-Fi en itinérance"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Données mobiles toujours actives"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Accélération matérielle pour le partage de connexion"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Saisissez le nom d\'hôte du fournisseur DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afficher les options de la certification de l\'affichage sans fil"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Détailler plus infos Wi-Fi, afficher par RSSI de SSID dans outil sélection Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Si cette option est activée, le passage du Wi-Fi aux données mobiles est forcé en cas de signal Wi-Fi faible."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Autoriser ou non la détection de réseaux Wi-Fi en itinérance en fonction de l\'importance du trafic de données dans l\'interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tailles mémoires tampons enregistr."</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Tailles enreg. par tampon journal"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 775bbf1..9026aab 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificado de visualización sen fíos"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Activar rexistro detallado da wifi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Transferencia agresiva de wifi a móbil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permitir sempre buscas de itinerancia da wifi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Datos móbiles sempre activados"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleración de hardware para conexión compartida"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduce o nome de host de provedor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opcións para o certificado de visualización sen fíos"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nivel de rexistro da wifi, mostrar por SSID RSSI no selector de wifi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Cando estea activada esta función, a wifi será máis agresiva ao transferir a conexión de datos ao móbil cando o sinal wifi sexa feble"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/Non permitir buscas de itinerancia da wifi baseadas na cantidade de tráfico de datos presente na interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamaños de búfer de rexistrador"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Seleccionar tamaños por búfer"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 712e57e..b1ee90b 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"નેટવર્કિંગ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"વાયરલેસ ડિસ્પ્લે પ્રમાણન"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"વાઇ-ફાઇ વર્બોઝ લૉગિંગ સક્ષમ કરો"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"સશક્ત Wi‑Fiથી મોબાઇલ પર હૅન્ડઓવર"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"હંમેશા વાઇ-ફાઇ રોમ સ્કૅન્સને મંજૂરી આપો"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"મોબાઇલ ડેટા હંમેશાં સક્રિય"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ટિથરિંગ માટે હાર્ડવેર ગતિવૃદ્ધિ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS પ્રદાતાના હોસ્ટનું નામ દાખલ કરો"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"વાયરલેસ ડિસ્પ્લે પ્રમાણપત્ર માટેના વિકલ્પો બતાવો"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"વાઇ-ફાઇ લોગિંગ સ્તર વધારો, વાઇ-ફાઇ પીકરમાં SSID RSSI દીઠ બતાવો"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"જ્યારે સક્ષમ કરેલ હોય, ત્યારે વાઇ-ફાઇ સિગ્નલ નબળું હોવા પર, વાઇ-ફાઇ વધુ ઝડપથી ડેટા કનેક્શનને મોબાઇલ પર મોકલશે"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ઇન્ટરફેસ પર હાજર ડેટા ટ્રાફિકના પ્રમાણનાં આધારે વાઇ-ફાઇ રોમ સ્કૅન્સને મંજૂરી આપો/નામંજૂર કરો"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"લોગર બફર કદ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"લૉગ દીઠ લૉગર કદ બફર પસંદ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index a88abb5..e235fc5 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस दिखाई देने के लिए प्रमाणन"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"वाई-फ़ाई वर्बोस लॉगिंग चालू करें"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"वाई-फ़ाई से मोबाइल पर ज़्यादा तेज़ी से हैंडओवर"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"हमेशा वाई-फ़ाई रोम स्कैन करने दें"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा हमेशा सक्रिय"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"हार्डवेयर से तेज़ी लाने के लिए टेदर करें"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS सेवा देने वाले का होस्टनाम डालें"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस दिखाई देने के लिए प्रमाणन विकल्प दिखाएं"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाई-फ़ाई प्रवेश स्तर बढ़ाएं, वाई-फ़ाई पिकर में प्रति SSID RSSI दिखाएं"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"इसके सक्षम होने पर, जब वाई-फ़ाई संकेत कमज़ोर हों तो वाई-फ़ाई, डेटा कनेक्शन को मोबाइल पर ज़्यादा तेज़ी से भेजेगा"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"इंटरफ़ेस पर वर्तमान में मौजूद डेटा ट्रैफ़िक के आधार पर वाई-फ़ाई रोम स्कैन करने देता/नहीं देता है"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफ़र आकार"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"प्रति लॉग बफ़र लॉगर आकार चुनें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index a417cc1..a854987 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Umrežavanje"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifikacija bežičnog prikaza"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogući opširnu prijavu na Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aktivni prijelaz s Wi‑Fi na mob. mrežu"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Uvijek dopusti slobodno traženje Wi-Fi mreže"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilni podaci uvijek aktivni"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardversko ubrzanje za modemsko povezivanje"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Unesite naziv hosta davatelja usluge DNS-a"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Prikaži opcije za certifikaciju bežičnog prikaza"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povećana razina prijave na Wi‑Fi, prikaz po SSID RSSI-ju u Biraču Wi‑Fi-ja"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Ako je omogućeno, Wi-Fi će aktivno prebacivati podatkovnu vezu mobilnoj mreži kada je Wi-Fi signal slab."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Dopustite ili blokirajte slobodno traženje Wi-Fi mreža na temelju količine podatkovnog prometa na sučelju."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Veličine međuspremnika zapisnika"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Odaberite veličinu međuspremnika zapisnika"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index b6f1460..553ed88 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Hálózatok"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Vezeték nélküli kijelző tanúsítványa"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Részletes Wi-Fi-naplózás engedélyezése"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresszív Wi‑Fi–mobilhálózat átadás"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi-roaming ellenőrzésének engedélyezése mindig"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"A mobilhálózati kapcsolat mindig aktív"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Internetmegosztás hardveres gyorsítása"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Adja meg a DNS-szolgáltató gazdagépnevét"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vezeték nélküli kijelző tanúsítványával kapcsolatos lehetőségek megjelenítése"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-naplózási szint növelése, RSSI/SSID megjelenítése a Wi‑Fi-választóban"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Ha engedélyezi, a Wi-Fi agresszívebben fogja átadni az adatkapcsolatot a mobilhálózatnak gyenge Wi-Fi-jel esetén"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"A Wi-Fi-roaming ellenőrzésének engedélyezése vagy letiltása az interfészen jelen lévő adatforgalom mennyiségétől függően"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Naplózási puffer mérete"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Naplózási pufferméret kiválasztása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 3ba3f45..dced49f5 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Ցանց"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Անլար էկրանի վկայագրում"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Միացնել Wi‑Fi մանրամասն գրանցամատյանները"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi-Fi-ից կտրուկ անցում բջջային ինտերնետի"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Միշտ թույլատրել Wi‑Fi ռոումինգի որոնումը"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Բջջային ինտերնետը միշտ ակտիվ է"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Սարքակազմի արագացման միացում"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Մուտքագրեք DNS ծառայության մատակարարի խնամորդի անունը"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ցույց տալ անլար էկրանի հավաստագրման ընտրանքները"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Բարձրացնել մակարդակը, Wi‑Fi ընտրիչում ամեն մի SSID-ի համար ցույց տալ RSSI"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Եթե այս գործառույթը միացված է, Wi-Fi-ի թույլ ազդանշանի դեպքում Wi‑Fi ինտերնետից բջջային ինտերնետի անցումը ավելի կտրուկ կկատարվի"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Թույլատրել/արգելել Wi‑Fi ռոումինգի որոնումը՝ կախված միջերեսում տվյալների երթևեկի ծավալից"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Տեղեկամատյանի պահնակի չափերը"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Ընտրեք տեղեկամատյանի չափը մեկ պահնակի համար"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 2bb9ae0..5c06766 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Jaringan"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Sertifikasi layar nirkabel"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktifkan Pencatatan Log Panjang Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Pengalihan Wi-Fi Agresif ke seluler"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Selalu izinkan Pemindaian Roaming Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Data seluler selalu aktif"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Akselerasi hardware tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Masukkan hostname penyedia DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tampilkan opsi untuk sertifikasi layar nirkabel"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan level pencatatan log Wi-Fi, tampilkan per SSID RSSI di Pemilih Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Jika diaktifkan, Wi-Fi akan menjadi lebih agresif dalam mengalihkan sambungan data ke seluler saat sinyal Wi-Fi lemah"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Izinkan/Larang Pemindaian Roaming Wi-Fi berdasarkan jumlah lalu lintas data yang ada di antarmuka"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Ukuran penyangga pencatat log"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Ukuran Pencatat Log per penyangga log"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 311227f..21b8b0e 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Netkerfi"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Vottun þráðlausra skjáa"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Kveikja á ítarlegri skráningu Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Hröð skipti úr Wi‑Fi í farsímagögn"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Leyfa alltaf reikileit með Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Alltaf kveikt á farsímagögnum"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Vélbúnaðarhröðun fyrir tjóðrun"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Slá inn hýsilheiti DNS-veitu"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Sýna valkosti fyrir vottun þráðlausra skjáa"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Auka skráningarstig Wi-Fi, sýna RSSI fyrir hvert SSID í Wi-Fi vali"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Þegar þetta er virkt mun Wi-Fi skipta hraðar yfir í farsímagagnatengingu þegar Wi-Fi-tenging er léleg"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Leyfa/banna reikileit með Wi-Fi á grunni þess hversu mikil gagnaumferð er fyrir hendi í viðmótinu"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Annálsritastærðir biðminna"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Veldu annálsritastærðir á biðminni"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index b63232d..7408b25 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Reti"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificazione display wireless"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Attiva registrazione dettagliata Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi‑Fi aggressivo per passaggio a cellulare"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Consenti sempre scansioni roaming Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dati mobili sempre attivi"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering accelerazione hardware"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Inserisci il nome host del provider DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostra opzioni per la certificazione display wireless"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumenta il livello di registrazione Wi-Fi, mostrando il SSID RSSI nel selettore Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Con questa impostazione attivata, il Wi-Fi è più aggressivo nel passare la connessione dati al cellulare, con segnale Wi-Fi basso"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Consenti/vieta scansioni roaming Wi-Fi basate sulla quantità di traffico dati presente a livello di interfaccia"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Dimensioni buffer Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Seleziona dimensioni Logger per buffer log"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 538da91..fceaacc 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"תקשורת רשתות"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"‏אישור של תצוגת WiFi"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"‏הפעל רישום מפורט של Wi‑Fi ביומן"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"‏העברה אגרסיבית מ-Wi‑Fi לרשת סלולרית"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"‏התר תמיד סריקות נדידה של Wi‑Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"חבילת הגלישה פעילה תמיד"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"שיפור מהירות באמצעות חומרה לצורך שיתוף אינטרנט בין ניידים"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"‏צריך להזין את שם המארח של ספק DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"‏הצג אפשרויות עבור אישור של תצוגת WiFi"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏העלה את רמת הרישום של Wi‑Fi ביומן, הצג לכל SSID RSSI ב-Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"‏כשאפשרות זו מופעלת, Wi-Fi יתנהג בצורה אגרסיבית יותר בעת העברת חיבור הנתונים לרשת הסלולרית כשאות ה-Wi-Fi חלש."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"‏התר/מנע סריקות נדידה של Wi-Fi בהתבסס על נפח תנועת הנתונים הקיימת בממשק"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"גדלי מאגר של יומן רישום"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"בחר גדלים של יוצר יומן לכל מאגר יומן"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index db1baa1..671320b 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ネットワーク"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ワイヤレスディスプレイ認証"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi詳細ログの有効化"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi-Fi を強制的にモバイル接続に切り替える"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fiローミングスキャンを常に許可する"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"モバイルデータを常に ON にする"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"テザリング時のハードウェア アクセラレーション"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS プロバイダのホスト名を入力"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ワイヤレスディスプレイ認証のオプションを表示"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fiログレベルを上げて、Wi-Fi選択ツールでSSID RSSIごとに表示します"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ON にすると、Wi-Fi の電波強度が弱い場合は強制的にモバイルデータ接続に切り替わるようになります"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"インターフェースのデータトラフィック量に基づいたWi-Fiローミングスキャンを許可するかしないかを設定できます"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ログバッファのサイズ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"各ログバッファのログサイズを選択"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 00eaf84..1948460 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ქსელი"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"უსადენო ეკრანის სერტიფიცირება"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi-ს დაწვრილებითი აღრიცხვის ჩართვა"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi‑Fi-ს მობ. ინტერნეტზე აგრესიული გადართვა"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi Roam სკანირების მუდამ დაშვება"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"მობილური ინტერნეტის ყოველთვის გააქტიურება"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ტეტერინგის აპარატურული აჩქარება"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"შეიყვანეთ DNS პროვაიდერის სერვერის სახელი"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"უსადენო ეკრანის სერტიფიცირების ვარიანტების ჩვენება"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi-ს აღრიცხვის დონის გაზრდა, Wi‑Fi ამომრჩეველში ყოველ SSID RSSI-ზე ჩვენება"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ჩართვის შემთხვევაში, Wi‑Fi უფრო აქტიურად შეეცდება მობილურ ინტერნეტზე გადართვას, როცა Wi‑Fi სიგნალი სუსტია"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wifi Roam სკანირების დაშვება/აკრძალვა, ინტერფეისზე არსებული მონაცემთა ტრაფიკზე დაფუძნებით"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ჟურნალიზაციის ბუფერის ზომები"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"აირჩიეთ ჟურნ. ზომა / ჟურნ. ბუფერზე"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index a318bd8..2d03f7f 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Желі орнату"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Сымсыз дисплей сертификаты"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi егжей-тегжейлі журналға тір. қосу"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi-Fi желісінен мобильдік желіге ауысу"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi роумингін іздеулерге әрқашан рұқсат ету"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мобильдік деректер әрқашан қосулы"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Тетерингтің аппараттық жеделдетуі"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS провайдерінің хост атауын енгізіңіз"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Сымсыз дисплей растау опцияларын көрсету"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi жур. тір. дең. арт., Wi‑Fi желісін таңдағышта әр SSID RSSI бойынша көрсету"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Wi‑Fi сигналы әлсіз болғанда, деректер байланысы мәжбүрлі түрде мобильдік желіге ауысады"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфейсте бар деректер трафигінің мөлшерінің негізінде Wi-Fi роумингін іздеулерге рұқсат ету/тыйым салу"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Журналға тіркеуші буферінің өлшемдері"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Әр журнал буфері үшін журналға тіркеуші өлшемдерін таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index ae47318..1d2476c 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ការភ្ជាប់បណ្ដាញ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"បង្ហាញ​ការ​កំណត់​រចនាសម្ព័ន្ធ​ឥត​ខ្សែ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"បើក​កំណត់ហេតុ​រៀបរាប់​វ៉ាយហ្វាយ"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ប្តូរទៅប្រើបណ្តាញចល័តពេល Wi‑Fi មានរលកសញ្ញាខ្លាំងពេក"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"តែងតែ​អនុញ្ញាត​​​ការវិភាគ​រ៉ូម​វ៉ាយហ្វាយ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"ទិន្នន័យទូរសព្ទចល័តដំណើរការជានិច្ច"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ការ​បង្កើនល្បឿន​ផ្នែករឹងសម្រាប់​ការភ្ជាប់"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"បញ្ចូលឈ្មោះម៉ាស៊ីនរបស់ក្រុមហ៊ុនផ្ដល់សេវា DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"បង្ហាញ​ជម្រើស​សម្រាប់​វិញ្ញាបនបត្រ​បង្ហាញ​ឥត​ខ្សែ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"បង្កើនកម្រិតកំណត់ហេតុវ៉ាយហ្វាយបង្ហាញក្នុង SSID RSSI ក្នុងកម្មវិធីជ្រើស​វ៉ាយហ្វាយ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"នៅពេលដែលបើក នោះ Wi‑Fi នឹងផ្តល់ការតភ្ជាប់ទិន្នន័យយ៉ាងគំហុកទៅបណ្តាញទូរសព្ទចល័ត នៅពេលរលកសញ្ញា Wi‑Fi ចុះខ្សោយ។"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"អនុញ្ញាត/មិន​អនុញ្ញាត​ការ​វិភាគ​រ៉ូម​​វ៉ាយហ្វាយ​ផ្អែក​លើ​​​ចំនួន​ការ​បង្ហាញ​ចរាចរណ៍​ទិន្នន័យ​​នៅ​ចំណុច​ប្រទាក់"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ទំហំ buffer របស់ Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ជ្រើស​ទំហំ Logger per log buffer"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index fbabb62..385308a 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ನೆಟ್‌ವರ್ಕಿಂಗ್"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ವೈರ್‌ಲೆಸ್ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi ವೆರ್ಬೋಸ್ ಲಾಗಿಂಗ್ ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ವೈ-ಫೈನಿಂದ ಮೊಬೈಲ್‌ಗೆ ಆಕ್ರಮಣಕಾರಿ ಹಸ್ತಾಂತರ"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ವೈ-ಫೈ ರೋಮ್ ಸ್ಕ್ಯಾನ್‌ಗಳನ್ನು ಯಾವಾಗಲೂ ಅನುಮತಿಸಿ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"ಮೊಬೈಲ್ ಡೇಟಾ ಯಾವಾಗಲೂ ಸಕ್ರಿಯ"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ಹಾರ್ಡ್‌ವೇರ್‌ನ ವೇಗವರ್ಧನೆಯನ್ನು ಟೆಥರಿಂಗ್ ಮಾಡಿ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ಪೂರೈಕೆದಾರರ ಹೋಸ್ಟ್‌ಹೆಸರನ್ನು ನಮೂದಿಸಿ"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ವೈರ್‌ಲೆಸ್‌‌‌ ಪ್ರದರ್ಶನ ಪ್ರಮಾಣೀಕರಣಕ್ಕಾಗಿ ಆಯ್ಕೆಗಳನ್ನು ತೋರಿಸು"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ಲಾಗಿಂಗ್ ಮಟ್ಟನ್ನು ಹೆಚ್ಚಿಸಿ, Wi‑Fi ಆಯ್ಕೆಯಲ್ಲಿ ಪ್ರತಿಯೊಂದು SSID RSSI ತೋರಿಸಿ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ಇದು ಸಕ್ರಿಯಗೊಂಡರೆ, ವೈ-ಫೈ ಸಿಗ್ನಲ್ ದುರ್ಬಲವಾಗಿದ್ದಾಗ, ಮೊಬೈಲ್‌ಗೆ ಡೇಟಾ ಸಂಪರ್ಕವನ್ನು ಹಸ್ತಾಂತರಿಸುವಲ್ಲಿ ವೈ-ಫೈ ಹೆಚ್ಚು ಆಕ್ರಮಣಕಾರಿಯಾಗಿರುತ್ತದೆ"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ಇಂಟರ್‌ಫೇಸ್‌ನಲ್ಲಿ ಲಭ್ಯವಿರುವ ಡೇಟಾ ಟ್ರಾಫಿಕ್ ಆಧಾರದ ಮೇಲೆ Wi‑Fi ರೋಮ್ ಸ್ಕ್ಯಾನ್‌ಗಳನ್ನು ಅನುಮತಿಸಿ/ನಿರಾಕರಿಸಿ"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ಲಾಗರ್ ಬಫರ್ ಗಾತ್ರಗಳು"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ಪ್ರತಿ ಲಾಗ್ ಬಫರ್‌ಗೆ ಲಾಗರ್ ಗಾತ್ರಗಳನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 4b7d0a4..e1be8dc 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"네트워크"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"무선 디스플레이 인증서"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi 상세 로깅 사용"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"적극적인 Wi-Fi-모바일 핸드오버"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi 로밍 스캔 항상 허용"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"항상 모바일 데이터 활성화"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"테더링 하드웨어 가속"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS 제공업체의 호스트 이름 입력"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"무선 디스플레이 인증서 옵션 표시"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi 로깅 수준을 높이고, Wi‑Fi 선택도구에서 SSID RSSI당 값을 표시합니다."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"사용 설정하면 Wi-Fi 신호가 약할 때 데이터 연결을 Wi-Fi에서 모바일 네트워크로 더욱 적극적으로 핸드오버합니다."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"인터페이스에 표시되는 데이터 트래픽의 양을 기반으로 Wi-Fi 로밍 스캔을 허용하거나 허용하지 않습니다."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"로거 버퍼 크기"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"로그 버퍼당 로거 크기 선택"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 98ca36a..d323df2 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Тармактык байланыштарды кеңейтүү"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Зымсыз дисплейди аныктоо"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi дайын-даректүү протоколун иштетүү"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi‑Fi начар болсо, мобилдик Инт-ке өтсүн"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi-Fi Роуминг Скандоо мүмкүнчүлүгүнө ар дайым уруксат берилсин"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилдик Интернет иштей берсин"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Тетерингдин иштешин тездетүү"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS түйүндүн аталышын киргизиңиз"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Зымсыз дисплейди сертификатто мүмкүнчүлүктөрүн көргөзүү"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi Кармагычта Wi‑Fi протокол деңгээлин жогорулатуу жана ар бир SSID RSSI үчүн көрсөтүү."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Иштетилсе, Wi-Fi байланышы үзүл-кесил болуп жатканда, Wi-Fi тармагы туташууну мобилдик Интернетке өжөрлүк менен өткөрүп берет"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфейстеги дайындар трафигинин көлөмүнө жараша Wi-Fi Роуминг скандоо мүмкүнчүлүгүн иштетүү/өчүрүү"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Каттагыч буферлеринин өлчөмдөрү"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Каттоо буфери үчүн Каттагычтын көлөмүн тандаңыз"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 6de2a2e..1e58af1 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ການ​ສ້າງເຄືອຂ່າຍ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ສະແດງການຮັບຮອງຂອງລະບົບໄຮ້ສາຍ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"​ເປີດ​ນຳ​ໃຊ້ການ​ເກັບ​ປະ​ຫວັດ​ Verbose Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ສະຫຼັບເປັນ Wi-Fi ເມື່ອມືຖືສັນຍານອ່ອນ"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ອະ​ນຸ​ຍາດ​ການ​ສະ​ແກນ​ການ​ໂຣມ Wi‑Fi ​ສະ​ເໝີ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"ເປີດໃຊ້ອິນເຕີເນັດມືຖືຕະຫຼອດເວລາ"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ເປີດໃຊ້ການເລັ່ງຄວາມໄວດ້ວຍຮາດແວ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ລະບຸຊື່ໂຮສຂອງຜູ້ໃຫ້ບໍລິການ DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ສະແດງໂຕເລືອກສຳລັບການສະແດງການຮັບຮອງລະບົບໄຮ້ສາຍ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ເພີ່ມ​ລະ​ດັບ​ການ​ເກັບ​ປະ​ຫວັດ Wi‑Fi, ສະ​ແດງ​ຕໍ່ SSID RSSI ​ໃນ​ Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ເມື່ອເປີດໃຊ້ແລ້ວ, Wi-Fi ຈະສົ່ງຜ່ານການເຊື່ອມຕໍ່ຂໍ້ມູນໄປຫາເຄືອຂ່າຍມືຖືເມື່ອສັນຍານ Wi-Fi ອ່ອນ"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"​ອະ​ນຸ​ຍາດ/ບໍ່​ອະ​ນຸ​ຍາດການ​ສະ​ແກນ​ການ​ໂຣມ Wi-Fi ອີງ​ຕາມ​ຈຳ​ນວນ​ຂໍ້​ມູນທີ່​ເກີດ​ຂຶ້ນ​ໃນ​ລະ​ດັບ​ສ່ວນ​ຕິດ​ຕໍ່"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ຂະ​ໜາດ​​ບັບ​ເຟີໂຕ​ລັອກ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ເລືອກ​ຂະ​ໜາດ​ລັອກ​ຕໍ່​ບັບ​ເຟີ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 66fa62a..a35cd3c 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Tinklai"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Belaidžio rodymo sertifikavimas"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Įgal. „Wi‑Fi“ daugiaž. įraš. į žurnalą"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agres. „Wi‑Fi“ perd. į mob. r. tinklą"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Visada leisti „Wi-Fi“ tarptiklinio ryšio nuskaitymą"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiliojo ryšio duomenys visada suaktyvinti"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Įrenginio kaip modemo naudojimo aparatinės įrangos spartinimas"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Įveskite DNS teikėjo prieglobos serverio pavadinimą"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rodyti belaidžio rodymo sertifikavimo parinktis"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Padidinti „Wi‑Fi“ įrašymo į žurnalą lygį, rodyti SSID RSSI „Wi-Fi“ rinkiklyje"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Jei ši parinktis įgalinta, „Wi‑Fi“ agresyviau perduos duomenų ryšiu į mobiliojo ryšio tinklą, kai „Wi‑Fi“ signalas silpnas"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Leisti / neleisti „Wi‑Fi“ tarptinklinio ryšio nuskaitymo, atsižvelgiant į sąsajos duomenų srauto kiekį"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Registruotuvo buferio dydžiai"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pasir. registr. dydž. žurn. bufer."</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index f32accd..8239d9f 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Tīklošana"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Bezvadu attēlošanas sertifikācija"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Iespējot Wi‑Fi detalizēto reģistrēšanu"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresīva pāreja no Wi‑Fi uz mobilo tīklu"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Vienmēr atļaut Wi‑Fi meklēšanu"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Vienmēr aktīvs mobilo datu savienojums"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Paātrināta aparatūras darbība piesaistei"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ievadiet DNS pakalpojumu sniedzēja saimniekdatora nosaukumu"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Rādīt bezvadu attēlošanas sertifikācijas iespējas"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Palieliniet Wi‑Fi reģistrēšanas līmeni; rādīt katram SSID RSSI Wi‑Fi atlasītājā."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Ja opcija ir iespējota un Wi‑Fi signāls ir vājš, datu savienojuma pāreja no Wi-Fi uz mobilo tīklu tiks veikta agresīvāk."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Atļaujiet/neatļaujiet Wi‑Fi meklēšanu, pamatojoties uz saskarnē saņemto datplūsmas apjomu."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Reģistrētāja buferu lielumi"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Atlasīt reģistrētāja bufera liel."</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 3151f56..eaa5539 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Вмрежување"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Приказ на сертификација на безжична мрежа"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Овозможи преопширно пријавување Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Агресивно предавање од Wi‑Fi на мобилен"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Секогаш дозволувај Wi‑Fi скенирање во роаминг"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилниот интернет е секогаш активен"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардверско забрзување за врзување"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Внесете име на хост на операторот на DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Покажи ги опциите за безжичен приказ на сертификат"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Зголеми Wi‑Fi ниво на пријавување, прикажи по SSID RSSI во Wi‑Fi бирач"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Кога е овозможено, Wi-Fi ќе биде поагресивна при предавање на интернет-врската на мобилната мрежа при слаб сигнал на Wi-Fi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволи/Забрани Wi‑Fi скенирање во роаминг според количината на постоечкиот податочен сообраќај на интерфејсот."</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Величини на меѓумеморија на забележувач"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Величина/меѓумеморија на дневник"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 2b86e50..0ddfc79 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"നെറ്റ്‍വര്‍ക്കിംഗ്"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"വയർലെസ് ഡിസ്‌പ്ലേ സർട്ടിഫിക്കേഷൻ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"വൈഫൈ വെർബോസ് ലോഗിംഗ് പ്രവർത്തനക്ഷമമാക്കുക"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"മൊബൈൽ ഹാൻഡ്ഓവറിലേക്ക് വൈഫൈ സക്രിയമാക്കുക"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"എപ്പോഴും വൈഫൈ റോം സ്‌‌കാൻ അനുവദിക്കൂ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"മൊബൈൽ ഡാറ്റ എല്ലായ്‌പ്പോഴും സജീവം"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ടെതറിംഗ് ഹാർഡ്‌വെയർ ത്വരിതപ്പെടുത്തൽ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ദാതാവിന്‍റെ ഹോസ്റ്റുനാമം നൽകുക"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"വയർലെസ് ഡിസ്‌പ്ലേ സർട്ടിഫിക്കേഷനായി ഓപ്‌ഷനുകൾ ദൃശ്യമാക്കുക"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"വൈഫൈ പിക്കറിൽ ഓരോ SSID RSSI പ്രകാരം കാണിക്കാൻ വൈഫൈ ലോഗിംഗ് നില വർദ്ധിപ്പിക്കുക"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"പ്രവർത്തനക്ഷമമായിരിക്കുമ്പോൾ, വൈഫൈ സിഗ്‌നൽ കുറവായിരിക്കുന്ന സമയത്ത് മൊബൈലിലേക്ക് ഡാറ്റ കണക്ഷൻ വഴി കൈമാറുന്നതിൽ വൈഫൈ കൂടുതൽ സക്രിയമായിരിക്കും"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ഇന്റർഫേസിലെ ഡാറ്റ ട്രാഫിക്ക് സാന്നിദ്ധ്യത്തിന്റെ കണക്ക് അടിസ്ഥാനമാക്കി വൈഫൈ റോം സ്‌കാനുകൾ അനുവദിക്കുക/അനുവദിക്കാതിരിക്കുക"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ലോഗർ ബഫർ വലുപ്പം"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ഓരോ ലോഗ് ബഫറിനും വലുപ്പം തിരഞ്ഞെടുക്കൂ"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 152408a..67c6495 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Сүлжээ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Утасгүй дэлгэцийн сертификат"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi Verbose лог-г идэвхжүүлэх"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Идэвхтэй Wi‑Fi-с мобайл сүлжээнд"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi Роум сканыг байнга зөвшөөрөх"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мобайл дата байнга идэвхтэй"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Модем болгох хардвер хурдасгуур"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS-н үйлчилгээ үзүүлэгчийн хостын нэрийг оруулах"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Утасгүй дэлгэцийн сертификатын сонголтыг харуулах"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi лог-н түвшинг нэмэгдүүлэх, Wi‑Fi Сонгогч дээрх SSID-д ногдох RSSI-г харуулах"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Идэвхжүүлсэн үед Wi‑Fi холболт сул байх үед дата холболтыг мобайлд шилжүүлэхэд илүү идэвхтэй байх болно"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Интерфэйс дээрх дата трафикын хэмжээнээс хамааран Wi‑Fi Роум Скан-г зөвшөөрөх/үл зөвшөөрөх"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Логгерын буферын хэмжээ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Лог буфер бүрт ногдох логгерын хэмжээг сонгоно уу"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 7402d29..eeb0a16 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किंग"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"वायरलेस डिस्प्ले प्रमाणीकरण"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"वाय-फाय व्हर्बोझ लॉगिंग सक्षम करा"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"मोबाइलकडे सोपवण्यासाठी अॅग्रेसिव्ह वाय-फाय"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"वाय-फाय रोम स्‍कॅनला नेहमी अनुमती द्या"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा नेहमी सक्रिय"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"टेदरिंग हार्डवेअर प्रवेग"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS पुरवठादाराचे होस्टनाव टाका"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"वायरलेस डिस्प्ले प्रमाणिकरणाचे पर्याय दाखवा"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"वाय-फाय लॉगिंग स्‍तर वाढवा, वाय-फाय सिलेक्टरमध्‍ये प्रति SSID RSSI दर्शवा"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"सक्षम केले असताना, वाय-फाय सिग्‍नल कमी असताना, मोबाइलकडे डेटा कनेक्‍शन सोपवण्यासाठी वाय-फाय अधिक अॅग्रेसिव्ह असेल."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"वाय-फाय रोम स्‍कॅनला इंटरफेसवर उपस्‍थित असलेल्‍या रहदारी डेटाच्या प्रमाणावर आधारित अनुमती द्या/अनुमती देऊ नका"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"लॉगर बफर आकार"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"प्रति लॉग बफर लॉगर आकार निवडा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 51ab84e..a69024c 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Perangkaian"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Pensijilan paparan wayarles"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Dayakan Pengelogan Berjela-jela Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Penyerahan Wi-Fi ke mudah alih agresif"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Sentiasa benarkan Imbasan Perayauan Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Data mudah alih sentiasa aktif"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Pecutan perkakasan penambatan"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Masukkan nama hos pembekal DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Tunjukkan pilihan untuk pensijilan paparan wayarles"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tingkatkan tahap pengelogan Wi-Fi, tunjuk setiap SSID RSSI dalam Pemilih Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Apabila didayakan, Wi-Fi akan menjadi lebih agresif dalam menyerahkan sambungan data ke mudah alih, apabila isyarat Wi-Fi rendah"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Benarkan/Jangan benarkan Imbasan Perayauan Wi-Fi berdasarkan jumlah trafik data yang ada pada antara muka"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Saiz penimbal pengelog"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pilih saiz Pengelog bagi setiap penimbal log"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index c643c59..5e78249 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ချိတ်ဆက်ဆောင်ရွက်ခြင်း"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ကြိုးမဲ့ပြသမှု အသိအမှတ်ပြုလက်မှတ်"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi Verbose မှတ်တမ်းတင်ခြင်းအား ဖွင့်မည်"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi‑Fi မှ မိုဘိုင်းသို့ လွှဲပြောင်းရန်"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi ရွမ်းရှာဖွေမှုကို အမြဲတမ်း ခွင့်ပြုမည်"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"မိုဘိုင်းဒေတာကို အမြဲဖွင့်ထားရန်"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ဖုန်းကို မိုဒမ်အဖြစ်အသုံးပြုမှု စက်ပစ္စည်းဖြင့် အရှိန်မြှင့်တင်ခြင်း"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ဝန်ဆောင်မှုပေးသူ၏ အင်တာနက်လက်ခံဝန်ဆောင်ပေးသူအမည်ကို ထည့်ပါ"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ကြိုးမဲ့ အခင်းအကျင်း အသိအမှတ်ပြုလက်မှတ်အတွက် ရွေးချယ်စရာများပြရန်"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi မှတ်တမ်းတင်ခြင်း နှုန်းအားမြင့်ကာ၊ Wi‑Fi ရွေးရာတွင် SSID RSSI ဖြင့်ပြပါ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ဖွင့်ထားပါက Wi‑Fi လွှင့်အား နည်းချိန်တွင် Wi‑Fi မှ မိုဘိုင်းသို့ ဒေတာချိတ်ဆက်မှုကို လွှဲပြောင်းရာ၌ ပိုမိုထိရောက်ပါသည်"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"မျက်နှာပြင်တွင် ဖော်ပြသည့် အချက်လက် အသွားအလာ ပမာဏပေါ်တွင် အခြေခံ၍ WIFI ရွမ်းရှာဖွေမှုအား ဖွင့်/ပိတ်မည်"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"လော့ဂါး ဘာဖား ဆိုက်များ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"လော့ ဘာဖားတွက် လော့ဂါးဆိုက် ရွေး"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0cea1a7..d45bbc3 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Nettverk"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Trådløs skjermsertifisering"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktiver detaljert Wi-Fi-loggføring"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressiv overføring fra Wi-Fi til mobil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Tillat alltid skanning for Wi-Fi-roaming"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata er alltid aktiv"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Maskinvareakselerasjon for internettdeling"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Skriv inn vertsnavnet til DNS-leverandøren"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Vis alternativer for sertifisering av trådløs skjerm"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Øk Wi-Fi-loggenivå – vis per SSID RSSI i Wi-Fi-velgeren"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Hvis dette slås på, overfører Wi-Fi-nettverket datatilkoblingen til mobil mer aggressivt når Wi-Fi-signalet er svakt"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillat / ikke tillat skanning for Wi-Fi-roaming basert på mengden datatrafikk til stede i grensesnittet"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Bufferstørrelser for logg"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Velg loggstørrelse per loggbuffer"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index d0ec0f5..d26541c 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"नेटवर्किङ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ताररहित प्रदर्शन प्रमाणीकरण"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi-Fi वर्बोज लग सक्षम पार्नुहोस्"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"आक्रामक ढंगले Wi‑Fi बाट मोबाइलमा हस्तान्तरण"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi-Fi घुम्ने स्क्यान गर्न सधैँ अनुमति दिनुहोस्"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"मोबाइल डेटा सधैँ सक्रिय राख्नुहोस्"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"टेदरिङको लागि हार्डवेयरको प्रवेग"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS प्रदायकको होस्टनाम प्रविष्ट गर्नुहोस्"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ताररहित प्रदर्शन प्रमाणीकरणका लागि विकल्पहरू देखाउनुहोस्"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi लग स्तर बढाउनुहोस्, Wi-Fi चयनकर्तामा प्रति SSID RSSI देखाइन्छ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"सक्षम गरिएको अवस्थामा, Wi-Fi सिग्नल न्यून हुँदा, Wi-Fi ले बढी आक्रामक ढंगले मोबाइलमा डेटा जडान हस्तान्तरण गर्नेछ"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Wi-Fi घुम्ने स्क्यान इन्टरफेसमा रहेको डेटा यातायातको मात्रामा आधारित अनुमति दिनुहोस्/नदिनुहोस्"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"लगर बफर आकारहरू"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"लग बफर प्रति लगर आकार चयन गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 87d20c2..ce25cc7 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Netwerken"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificering van draadloze weergave"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Uitgebreide wifi-logregistratie insch."</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agressieve handover van wifi naar mobiel"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Altijd roamingscans voor wifi toestaan"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobiele data altijd actief"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardwareversnelling voor tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Geef hostnaam van DNS-provider op"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Opties weergeven voor certificering van draadloze weergave"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Logniveau voor wifi verhogen, weergeven per SSID RSSI in wifi-kiezer"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Indien ingeschakeld, is wifi agressiever bij het overgeven van de gegevensverbinding aan mobiel wanneer het wifi-signaal zwak is"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Roamingscans voor wifi (niet) toestaan op basis van de hoeveelheid dataverkeer die aanwezig is bij de interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Logger-buffergrootten"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Kies Logger-grootten per logbuffer"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index d870a99..d64c30f 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ਨੈੱਟਵਰਕਿੰਗ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"ਵਾਈ-ਫਾਈ ਵਰਬੋਸ ਲੌਗਿੰਗ ਚਾਲੂ ਕਰੋ"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ਆਕਰਮਣਸ਼ੀਲ ਵਾਈ‑ਫਾਈ ਤੋਂ ਮੋਬਾਈਲ ਹੈਂਡਓਵਰ"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ਹਮੇਸ਼ਾਂ ਵਾਈ‑ਫਾਈ ਰੋਮ ਸਕੈਨਾਂ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"ਮੋਬਾਈਲ ਡਾਟਾ ਹਮੇਸ਼ਾਂ ਕਿਰਿਆਸ਼ੀਲ"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ਟੈਦਰਿੰਗ ਹਾਰਡਵੇਅਰ ਐਕਸੈੱਲਰੇਸ਼ਨ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ਪ੍ਰਦਾਨਕ ਦਾ ਹੋਸਟਨਾਮ ਦਾਖਲ ਕਰੋ"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"ਵਾਇਰਲੈੱਸ ਡਿਸਪਲੇ ਪ੍ਰਮਾਣੀਕਰਨ ਲਈ ਚੋਣਾਂ ਪ੍ਰਦਰਸ਼ਿਤ ਕਰੋ"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"ਵਾਈ‑ਫਾਈ ਲੌਗਿੰਗ ਪੱਧਰ ਵਧਾਓ, ਵਾਈ‑ਫਾਈ Picker ਵਿੱਚ ਪ੍ਰਤੀ SSID RSSI ਦਿਖਾਓ"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ਜਦੋਂ ਯੋਗ ਬਣਾਇਆ ਹੋਵੇ, ਤਾਂ ਵਾਈ‑ਫਾਈ ਸਿਗਨਲ ਘੱਟ ਹੋਣ \'ਤੇ ਵਾਈ‑ਫਾਈ ਡਾਟਾ ਕਨੈਕਸ਼ਨ ਮੋਬਾਈਲ ਨੂੰ ਹੈਂਡ ਓਵਰ ਕਰਨ ਵਿੱਚ ਵੱਧ ਆਕਰਮਣਸ਼ੀਲ ਹੋਵੇਗਾ।"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ਇੰਟਰਫੇਸ ਤੇ ਮੌਜੂਦ ਡਾਟਾ ਟ੍ਰੈਫਿਕ ਦੀ ਮਾਤਰਾ ਦੇ ਆਧਾਰ ਤੇ ਵਾਈ-ਫਾਈ ਰੋਮ ਸਕੈਨ ਦੀ ਆਗਿਆ ਦਿਓ/ਅਸਵੀਕਾਰ ਕਰੋ"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ਲੌਗਰ ਬਫ਼ਰ ਆਕਾਰ"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ ਲੌਗਰ ਆਕਾਰ ਚੁਣੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 7b66aa5..d50528d 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Sieci"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Wyświetlacz bezprzewodowy"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Szczegółowy dziennik Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Przełączaj z Wi-Fi na sieć komórkową"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Zawsze szukaj Wi-Fi w roamingu"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilna transmisja danych zawsze aktywna"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Akceleracja sprzętowa tetheringu"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Wpisz nazwę hosta dostawcy DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaż opcje certyfikacji wyświetlacza bezprzewodowego"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zwiększ poziom rejestrowania Wi‑Fi, pokazuj według RSSI SSID w selektorze Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Po włączeniu połączenie danych będzie bardziej agresywnie przełączać się z Wi-Fi na sieć komórkową przy słabym sygnale Wi-Fi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Zezwalaj/nie zezwalaj na wyszukiwanie sieci Wi-Fi w roamingu w zależności od natężenia ruchu"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Rozmiary bufora Rejestratora"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Wybierz rozmiary Rejestratora/bufor dziennika"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index d99097a..67fda03 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar registro extenso de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Mudança agressiva de Wi-Fi para móvel"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Sempre permitir verif. de roaming de Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware de tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Informe o nome do host do provedor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro do Wi-Fi; mostrar conforme o RSSI de SSID na Seleção de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Quando ativada, o Wi-Fi será mais agressivo em passar a conexão de dados para móvel, quando o sinal de Wi-Fi estiver fraco"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/proibir verificações de roaming de Wi-Fi com base no volume do tráfego de dados presente na interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 8c7fbba..d7496454c4 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de display sem fios"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar o registo verboso de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Mudança brusca de Wi‑Fi para rede móvel"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Permitir sempre a deteção de Wi-Fi em roaming"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware para ligação (à Internet) via telemóvel"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduza o nome de anfitrião do fornecedor DNS."</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções da certificação de display sem fios"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de reg. de Wi-Fi, mostrar por RSSI de SSID no Selec. de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Se estiver ativado, o Wi-Fi será mais agressivo ao transmitir a lig. de dados para a rede móvel quando o sinal Wi-Fi estiver fraco"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/impedir a deteção de Wi-Fi em roaming com base na quantidade de tráfego de dados presente na interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos da memória intermédia do registo"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Selec. tam. reg. p/ mem. int. reg."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index d99097a..67fda03 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Redes"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Ativar registro extenso de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Mudança agressiva de Wi-Fi para móvel"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Sempre permitir verif. de roaming de Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dados móveis sempre ativos"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Aceleração de hardware de tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Informe o nome do host do provedor de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Mostrar opções de certificação de Display sem fio"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Aumentar o nível de registro do Wi-Fi; mostrar conforme o RSSI de SSID na Seleção de Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Quando ativada, o Wi-Fi será mais agressivo em passar a conexão de dados para móvel, quando o sinal de Wi-Fi estiver fraco"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permitir/proibir verificações de roaming de Wi-Fi com base no volume do tráfego de dados presente na interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Tamanhos de buffer de logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Sel. tam. de logger/buffer de log"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1c67550..1e91d82 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Conectare la rețele"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certificare Ecran wireless"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Înregistrare prin Wi-Fi de volume mari de date"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Predare agresivă de la Wi-Fi la mobilă"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Se permite întotdeauna scanarea traficului Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Date mobile permanent active"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Accelerare hardware pentru tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Introduceți numele de gazdă al furnizorului de DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Măriți niv. de înr. prin Wi‑Fi, afișați în fcț. de SSID RSSI în Selectorul Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Când este activată, Wi-Fi va fi mai agresivă la predarea conexiunii de date către rețeaua mobilă când semnalul Wi-Fi este slab"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Permiteți/Nu permiteți scanarea traficului Wi-Fi în funcție de traficul de date din interfață"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Dimensiunile tamponului jurnalului"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Dimensiuni jurnal / tampon jurnal"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 4b11594..693332f 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Сети"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Серт. беспроводн. мониторов"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Подробный журнал Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Переключаться на мобильную сеть"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Всегда включать поиск сетей Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Не отключать мобильный Интернет"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Аппаратное ускорение в режиме модема"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Введите имя хоста поставщика услуг DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показывать параметры сертификации беспроводных мониторов"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"При выборе Wi‑Fi указывать в журнале RSSI для каждого SSID"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Принудительно переключаться на мобильную сеть, если сигнал Wi-Fi слабый"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Включать или отключать поиск сетей Wi-Fi во время передачи данных в зависимости от объема трафика"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Размер буфера журнала"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Выберите размер буфера журнала"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 2ffe814d..6abd14d 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"ජාලකරණය"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"නොරැහැන් සංදර්ශක සහතිකය"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"විස්තරාත්මක Wi‑Fi ලොග් කිරීම සබල කරන්න"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ආක්‍රමණික Wi‑Fi සිට ජංගම බාර දීම"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi රෝම් පරිලෝකන වෙතට සැමවිට අවසර දෙන්න"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"ජංගම දත්ත සැමවිට ක්‍රියාකාරීය"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ටෙදරින් දෘඪාංග ත්වරණය"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS සැපයුම්කරුගේ සත්කාරක නම ඇතුළු කරන්න"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"නොරැහැන් සංදර්ශක සහතිකය සඳහා විකල්ප පෙන්වන්න"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ලොග් මට්ටම වැඩි කරන්න, Wi‑Fi තෝරනයෙහි SSID RSSI අනුව පෙන්වන්න"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"සබල විට Wi‑Fi සිග්නලය අඩු විට Wi‑Fi දත්ත සම්බන්ධතාවය ජංගම වෙත භාර දීමට වඩා ආක්‍රමණික වේ"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"අතුරු මුහුණතෙහි ඇති දත්ත තදබදය අනුව Wi‑Fi රෝම් පරිලෝකන වෙත ඉඩ දෙන්න/නොදෙන්න"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ලෝගයේ අන්තරාවක ප්‍රමාණය"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"ලොග අන්තරාවකට ලෝගයේ ප්‍රමාණය තෝරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 0cefd71..8cc2dac 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Siete"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifikácia bezdrôtového zobrazenia"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Podrobné denníky Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agres. odovzdávať Wi‑Fi na mobilnú sieť"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Vždy povoliť funkciu Wi‑Fi Roam Scans"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobilné dáta ponechať vždy aktívne"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardvérovú akcelerácia pre tethering"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Zadajte názov hostiteľa poskytovateľa DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Zobraziť možnosti certifikácie bezdrôtového zobrazenia"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Zvýšiť úroveň denníkov Wi‑Fi, zobrazovať podľa SSID RSSI pri výbere siete Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Keď túto možnosť zapnete, Wi‑Fi bude agresívnejšie odovzdávať dátové pripojenie na mobilnú sieť vtedy, keď bude slabý signál Wi‑Fi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Povoliť alebo zakázať funkciu Wifi Roam Scans na základe objemu prenosu údajov v rozhraní"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Vyrovnávacia pamäť nástroja denníkov"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Veľkosť vyrovnávacej pamäte nástroja denníkov"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 0864d5b..af8eb6e 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Omrežja"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Potrdilo brezžičnega zaslona"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Omogoči podrob. zapis. dnevnika za Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Odločen prehod iz Wi-Fi-ja v mobil. omr."</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Vedno omogoči iskanje omrežij Wi-Fi za gostovanje"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Prenos podatkov v mobilnem omrežju je vedno aktiven"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Strojno pospeševanje za internetno povezavo prek mobilnega telefona"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Vnesite ime gostitelja pri ponudniku strežnika DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Pokaži možnosti za potrdilo brezžičnega zaslona"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Povečaj raven zapis. dnev. za Wi-Fi; v izbir. Wi‑Fi-ja pokaži glede na SSID RSSI"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Če je ta možnost omogočena, Wi-Fi odločneje preda podatkovno povezavo mobilnemu omrežju, ko je signal Wi-Fi šibek."</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Omogoči/onemogoči iskanje omrežij Wi-Fi za gostovanje glede na količino podatkovnega prometa pri vmesniku"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Velikosti medpomn. zapisov. dnevnika"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Izberite velikost medpomnilnika dnevnika"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 63da6ff..88b10577 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Rrjetet"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifikimi i ekranit valor"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivizo hyrjen Wi-Fi Verbose"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Dorëzimi agresiv i Wi‑Fi te rrjeti celular"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Lejo gjithmonë skanimet për Wi-Fi edhe kur je në lëvizje"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Të dhënat celulare gjithmonë aktive"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Përshpejtimi i harduerit për ndarjen"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Fut emrin e pritësit të ofruesit të DNS-së"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Shfaq opsionet për certifikimin e ekranit valor"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Rrit nivelin regjistrues të Wi‑Fi duke shfaqur SSID RSSI-në te Zgjedhësi i Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kur ky funksion aktivizohet, Wi‑Fi bëhet më agresiv në kalimin e lidhjes së të dhënave te rrjeti celular, në rastet kur sinjali Wi‑Fi është i dobët"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Lejo/Ndalo skanimet për Wi‑Fi në roaming, bazuar në sasinë e trafikut të të dhënave të pranishme në ndërfaqe"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Madhësitë e regjistruesit"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Përzgjidh madhësitë e regjistruesit"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 4c7aed8..362fc1f 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Умрежавање"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Сертификација бежичног екрана"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Омогући детаљнију евиденцију за Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Агресиван прелаз са Wi‑Fi мреже на мобилну"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Увек дозволи скенирање Wi‑Fi-ја у ромингу"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Мобилни подаци су увек активни"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Хардверско убрзање привезивања"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Унесите име хоста добављача услуге DNS-а"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Приказ опција за сертификацију бежичног екрана"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Повећава ниво евидентирања за Wi‑Fi. Приказ по SSID RSSI-у у бирачу Wi‑Fi мреже"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Кад се омогући, Wi‑Fi ће бити агресивнији при пребацивању мреже за пренос података на мобилну ако је Wi‑Fi сигнал слаб"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволи/забрани скенирање Wi-Fi-ја у ромингу на основу присутног протока података на интерфејсу"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Величине бафера података у програму за евидентирање"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Изаберите величине по баферу евиденције"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index e44dcd8..230dfee 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Nätverk"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certifiering för Wi-Fi-skärmdelning"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Aktivera utförlig loggning för Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Aggressiv överlämning fr. Wi-Fi t. mobil"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Tillåt alltid sökning efter Wi-Fi-roaming"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobildata alltid aktiverad"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Maskinvaruacceleration för internetdelning"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ange värdnamn för DNS-leverantör"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Visa certifieringsalternativ för Wi-Fi-skärmdelning"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Öka loggningsnivån för Wi-Fi, visa per SSID RSSI i Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"När funktionen har aktiverats kommer dataanslutningen lämnas över från Wi-Fi till mobilen på ett aggressivare sätt när Wi-Fi-signalen är svag"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Tillåt/tillåt inte sökning efter Wi-Fi-roaming utifrån mängden datatrafik i gränssnittet"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Buffertstorlekar för logg"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Välj loggstorlekar per loggbuffert"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 66cfda8..1d46260 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Mtandao"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Chaguo za cheti cha kuonyesha pasiwaya"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Washa Uwekaji kumbukumbu za WiFi kutumia Sauti"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Ukabidhi hima kutoka Wifi kwenda mtandao wa simu"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Ruhusu Uchanganuzi wa Matumizi ya Mitandao mingine"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Iendelee kutumia data ya simu"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Kuongeza kasi kwa kutumia maunzi ili kusambaza mtandao"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Weka jina la mpangishi wa huduma za DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Onyesha chaguo za cheti cha kuonyesha pasiwaya"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Ongeza hatua ya uwekaji kumbukumbu ya Wi-Fi, onyesha kwa kila SSID RSSI kwenye Kichukuzi cha Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Ikiwashwa, Wi-Fi itakabidhi kwa hima muunganisho wa data kwa mtandao wa simu, wakati mtandao wa Wi-Fi si thabiti"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Ruhusu au Zuia Uchanganuzi wa Matumizi ya Mitandao mingine ya Wifi kulingana na kiasi cha trafiki ya data kilicho kwenye kiolesura"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Ukubwa wa kiweka bafa ya kumbukumbu"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Chagua ukubwa wa kila Kumbukumbu"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index dd218c6..e9fd79b 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"நெட்வொர்க்கிங்"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"வயர்லெஸ் காட்சிக்கான சான்றிதழ்"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"வைஃபை அதிவிவர நுழைவை இயக்கு"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"ஒத்துழைக்காத வைஃபையிலிருந்து மொபைல் தரவிற்கு மாறு"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"எப்போதும் வைஃபை ரோமிங் ஸ்கேன்களை அனுமதி"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"மொபைல் டேட்டாவை எப்போதும் இயக்கத்திலேயே வை"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"வன்பொருள் விரைவுப்படுத்துதல் இணைப்பு முறை"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS வழங்குநரின் ஹோஸ்ட் பெயரை உள்ளிடவும்"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"வயர்லெஸ் காட்சி சான்றுக்கான விருப்பங்களைக் காட்டு"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wifi நுழைவு அளவை அதிகரித்து, வைஃபை தேர்வியில் ஒவ்வொன்றிற்கும் SSID RSSI ஐ காட்டுக"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"இயக்கப்பட்டதும், வைஃபை சிக்னல் குறையும் போது, வைஃபை முழுமையாக ஒத்துழைக்காமல் இருந்தால் மொபைல் தரவிற்கு மாறும்"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"இடைமுகத்தில் உள்ள ட்ராஃபிக் தரவின் அளவைப் பொறுத்து வைஃபை ரோமிங் ஸ்கேன்களை அனுமதி/அனுமதிக்காதே"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"லாகர் பஃபர் அளவுகள்"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"லாக் பஃபர் ஒன்றிற்கு லாகர் அளவுகளைத் தேர்வுசெய்க"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index b21eb8c..c2b738a 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"నెట్‌వర్కింగ్"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"వైర్‌లెస్ ప్రదర్శన ప్రమాణీకరణ"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Wi‑Fi విశదీకృత లాగింగ్‌ను ప్రారంభించండి"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"మొబైల్‌కి మార్చేలా చురుకైన Wi‑Fi"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi‑Fi సంచార స్కాన్‌లను ఎల్లప్పుడూ అనుమతించు"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"మొబైల్ డేటాని ఎల్లప్పుడూ సక్రియంగా ఉంచు"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"టెథెరింగ్ హార్డ్‌వేర్ వేగవృద్ధి"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS ప్రదాత యొక్క హోస్ట్‌పేరును నమోదు చేయండి"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"వైర్‌లెస్ ప్రదర్శన సర్టిఫికెట్ కోసం ఎంపికలను చూపు"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi‑Fi ఎంపికలో SSID RSSI ప్రకారం చూపబడే Wi‑Fi లాగింగ్ స్థాయిని పెంచండి"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"ప్రారంభించబడినప్పుడు, Wi‑Fi సిగ్నల్ బలహీనంగా ఉంటే డేటా కనెక్షన్‌ను మొబైల్‌కి మార్చేలా Wi‑Fi చురుగ్గా వ్యవహరిస్తుంది"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"ఇంటర్‌ఫేస్‌లో ఉండే డేటా ట్రాఫిక్ పరిమాణం ఆధారంగా Wi‑Fi సంచార స్కాన్‌లను అనుమతించు/నిరాకరించు"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"లాగర్ బఫర్ పరిమాణాలు"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"లాగ్ బఫర్‌కి లాగర్ పరిమా. ఎంచుకోండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 447b188..57b12a5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"เครือข่าย"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"การรับรองการแสดงผลแบบไร้สาย"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"เปิดใช้การบันทึกรายละเอียด Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"สลับ Wi‑Fi เป็นมือถือเมื่อสัญญาณอ่อน"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"ใช้การสแกน Wi-Fi ข้ามเครือข่ายเสมอ"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"เปิดใช้อินเทอร์เน็ตมือถือเสมอ"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"การเร่งฮาร์ดแวร์การเชื่อมต่ออินเทอร์เน็ตผ่านมือถือ"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"ป้อนชื่อโฮสต์ของผู้ให้บริการ DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"แสดงตัวเลือกสำหรับการรับรองการแสดงผล แบบไร้สาย"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"เมื่อเปิดใช้แล้ว Wi-Fi จะส่งผ่านการเชื่อมต่อข้อมูลไปยังเครือข่ายมือถือเมื่อสัญญาณ Wi-Fi อ่อน"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"อนุญาต/ไม่อนุญาตการสแกน Wi-Fi ข้ามเครือข่าย ตามปริมาณข้อมูลการเข้าชมที่ปรากฏในอินเทอร์เฟซ"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"ขนาดบัฟเฟอร์ของ Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"เลือกขนาด Logger ต่อบัฟเฟอร์ไฟล์บันทึก"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index a5e0b89..72261af 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Networking"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Certification ng wireless display"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"I-enable ang Pagla-log sa Wi‑Fi Verbose"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Agresibong paglipat ng Wi‑Fi sa mobile"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Palaging payagan ang Mga Pag-scan sa Roaming ng Wi‑Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Palaging aktibo ang mobile data"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Hardware acceleration para sa pag-tether"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Ilagay ang hostname ng DNS provider"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Ipakita ang mga opsyon para sa certification ng wireless display"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Pataasin ang antas ng Wi‑Fi logging, ipakita sa bawat SSID RSSI sa Wi‑Fi Picker"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Kapag na-enable, magiging mas agresibo ang Wi‑Fi sa paglipat sa koneksyon ng mobile data kapag mahina ang signal ng Wi‑Fi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Payagan/Huwag payagan ang Mga Pag-scan sa Roaming ng Wi‑Fi batay sa dami ng trapiko ng data na mayroon sa interface"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Mga laki ng buffer ng Logger"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Pumili ng mga laki ng Logger bawat log buffer"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e6bba5b..948e4e5 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Ağ işlemleri"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Kablosuz ekran sertifikası"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Kablosuz Ayrıntılı Günlük Kaydını etkinleştir"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Kablosuzdan mobil ağa agresif geçiş"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Kablosuz Dolaşım Taramalarına daima izin ver"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil veri her zaman etkin"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tethering donanım hızlandırıcısı"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS sağlayıcının ana makine adını gir"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Kablosuz ekran sertifikası seçeneklerini göster"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Kablosuz günlük kaydı seviyesini artır. Kablosuz Seçici\'de her bir SSID RSSI için göster."</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Etkinleştirildiğinde, kablosuz ağ sinyali zayıfken veri bağlantısının mobil ağa geçirilmesinde daha agresif olunur"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Arayüzde mevcut veri trafiği miktarına bağlı olarak Kablosuz Dolaşım Taramalarına İzin Verin/Vermeyin"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Günlük Kaydedici arabellek boyutları"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Gün. arabel. başına Gün. Kayd. boyutunu seç"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 65f39ed..664fda7 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Мережі"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Сертифікація бездрот. екрана"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Докладний запис у журнал Wi-Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Перемикатися з Wi-Fi на мобільну мережу"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Завжди шукати мережі Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Не вимикати мобільне передавання даних"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Апаратне прискорення під час використання телефона в режимі модема"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Введіть ім’я хосту постачальника послуг DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Показати параметри сертифікації бездротового екрана"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Показувати в журналі RSSI для кожного SSID під час вибору Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Примусово перемикатися на мобільну мережу, коли сигнал Wi-Fi слабкий"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Дозволити чи заборонити Wi-Fi шукати роумінг на основі обсягу трафіку даних в інтерфейсі"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Розміри буфера журналу"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Виберіть розміри буфера журналу"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 7830bb8..226b862 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"نیٹ ورکنگ"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"وائرلیس ڈسپلے سرٹیفیکیشن"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"‏Wi‑Fi وربوس لاگنگ فعال کریں"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"‏Wi‑Fi سے موبائل کو جارحانہ ہینڈ اوور"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"‏ہمیشہ Wi‑Fi روم اسکینز کی اجازت دیں"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"موبائل ڈیٹا ہمیشہ فعال رکھیں"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"ٹیدرنگ ہارڈویئر سرعت کاری"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"‏DNS فراہم کنندہ کے میزبان کا نام درج کریں"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"وائرلیس ڈسپلے سرٹیفیکیشن کیلئے اختیارات دکھائیں"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"‏Wi‑Fi لاگنگ لیول میں اضافہ کریں، Wi‑Fi منتخب کنندہ میں فی SSID RSSI دکھائیں"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"‏فعال کئے جانے پر، جب Wi‑Fi سگنل کمزور ہوگا، تو Wi‑Fi موبائل پر ڈیٹا کنکشن بھیجنے کیلئے مزید جارحانہ کارروائی کرے گا"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"‏انٹرفیس پر موجود ڈیٹا ٹریفک کی مقدار کی بنیاد پر Wi‑Fi روم اسکینز کی اجازت دیں/اجازت نہ دیں"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"لاگر بفر کے سائز"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"فی لاگ بفر لاگر کے سائز منتخب کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index acaaaf1..fdb3ef4 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Tarmoqlar"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Simsiz monitor sertifikatlari"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Batafsil Wi-Fi jurnali"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Mobil internetga o‘tish"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Wi-Fi tarmoqlarini qidirishga doim ruxsat"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Mobil internet doim yoniq tursin"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Modem rejimida apparatli tezlashtirish"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"DNS provayderining host nomini kiriting"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Simsiz monitorlarni sertifikatlash parametrini ko‘rsatish"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Wi-Fi ulanishini tanlashda har bir SSID uchun jurnalda ko‘rsatilsin"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Agar ushbu funksiya yoqilsa, Wi-Fi signali past bo‘lganda internetga ulanish majburiy ravishda mobil internetga o‘tkaziladi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Ma’lumotlarni uzatish vaqtida  trafik hajmiga qarab Wi-Fi tarmoqlarni qidirish funksiyasini yoqish yoki o‘chirish"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Jurnal buferi hajmi"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Jurnal xotirasi hajmini tanlang"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 7732b54..a7a5855 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Mạng"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Chứng nhận hiển thị không dây"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Bật ghi nhật ký chi tiết Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Chuyển vùng Wi‑Fi tích cực sang mạng DĐ"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Luôn cho phép quét chuyển vùng Wi‑Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Dữ liệu di động luôn hiện hoạt"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"Tăng tốc phần cứng cho chia sẻ kết nối"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Nhập tên máy chủ của nhà cung cấp DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Hiển thị tùy chọn chứng nhận hiển thị không dây"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"Tăng mức ghi nhật ký Wi‑Fi, hiển thị mỗi SSID RSSI trong bộ chọn Wi‑Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Khi được bật, Wi‑Fi sẽ tích cực hơn trong việc chuyển vùng kết nối dữ liệu sang mạng di động khi tín hiệu Wi‑Fi yếu"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Cho phép/Không cho phép quét chuyển vùng Wi‑Fi dựa trên lưu lượng truy cập dữ liệu có tại giao diện"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Kích cỡ tải trình ghi"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Chọn kích thước Trình ghi/lần tải nhật ký"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index f036eae..5a1d067 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"网络"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"无线显示认证"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"启用WLAN详细日志记录功能"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"主动从 WLAN 网络切换到移动数据网络"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"一律允许WLAN漫游扫描"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"始终开启移动数据网络"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"网络共享硬件加速"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"输入 DNS 提供商的主机名"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"显示无线显示认证选项"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"提升WLAN日志记录级别(在WLAN选择器中显示每个SSID的RSSI)"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"开启此设置后,系统会在 WLAN 信号较弱时,主动将网络模式从 WLAN 网络切换到移动数据网络"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根据接口中目前的数据流量允许/禁止WLAN漫游扫描"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"日志记录器缓冲区大小"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"选择每个日志缓冲区的日志记录器大小"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index d57a8fd..acc3bb8 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"網絡"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"無線螢幕分享認證"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"啟用 Wi‑Fi 詳細記錄"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"加強 Wi-Fi 至流動數據轉換"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"永遠允許 Wi-Fi 漫遊掃描"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"一律保持啟用流動數據"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"網絡共享硬件加速"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入網域名稱系統 (DNS) 供應商的主機名稱"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用後,Wi-Fi 連線會在訊號不穩定的情況下更積極轉換成流動數據連線"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根據介面中目前的數據流量允許/禁止 WiFi 漫遊掃描"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"記錄器緩衝區空間"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"選取每個記錄緩衝區的記錄器空間"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 5a329fa..775b928 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"網路連線"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"無線螢幕分享認證"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"啟用 Wi‑Fi 詳細紀錄設定"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Wi-Fi 至行動數據轉換強化"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"一律允許 Wi-Fi 漫遊掃描"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"行動數據連線一律保持啟用狀態"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"數據連線硬體加速"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"輸入 DNS 供應商的主機名稱"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"顯示無線螢幕分享認證的選項"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"讓 Wi‑Fi 記錄功能升級,在 Wi‑Fi 選擇器中依每個 SSID RSSI 顯示 Wi‑Fi 詳細紀錄"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"啟用時,Wi-Fi 連線在訊號不穩的情況下會更積極轉換成行動數據連線"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"根據介面中目前的數據流量允許/禁止 Wi-Fi 漫遊掃描"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"紀錄器緩衝區空間"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"選取每個紀錄緩衝區的紀錄器空間"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 3649ba2..13b1603 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -196,7 +196,6 @@
     <string name="debug_networking_category" msgid="7044075693643009662">"Ukunethiwekha"</string>
     <string name="wifi_display_certification" msgid="8611569543791307533">"Ukunikezwa isitifiketi sokubukeka okungenantambo"</string>
     <string name="wifi_verbose_logging" msgid="4203729756047242344">"Nika amandlaukungena kwe-Wi-Fi Verbose"</string>
-    <string name="wifi_aggressive_handover" msgid="5309131983693661320">"Ukudluliselwa okunamandla kakhulu kwe-Wi-Fi ukuya kuselula"</string>
     <string name="wifi_allow_scan_with_traffic" msgid="3601853081178265786">"Vumela njalo ukuskena kokuzula kwe-Wi-Fi"</string>
     <string name="mobile_data_always_on" msgid="8774857027458200434">"Idatha yeselula ihlala isebenza"</string>
     <string name="tethering_hardware_offload" msgid="7470077827090325814">"I-Tethering hardware acceleration"</string>
@@ -223,7 +222,6 @@
     <string name="private_dns_mode_provider_hostname_hint" msgid="2487492386970928143">"Faka igama lomsingathi womhlinzeki we-DNS"</string>
     <string name="wifi_display_certification_summary" msgid="1155182309166746973">"Bonisa izinketho zokunikeza isitifiketi ukubukeka okungenantambo"</string>
     <string name="wifi_verbose_logging_summary" msgid="6615071616111731958">"khuphula izinga lokungena le-Wi-Fi, bonisa nge-SSID RSSI engayodwana kusikhethi se-Wi-Fi"</string>
-    <string name="wifi_aggressive_handover_summary" msgid="7266329646559808827">"Uma inikwe amandla, i-Wi-Fi izoba namandla kakhulu ekudluliseleni ukuxhumeka kwedatha kuselula, uma isignali ye-Wi-Fi iphansi"</string>
     <string name="wifi_allow_scan_with_traffic_summary" msgid="2575101424972686310">"Vumela/Ungavumeli ukuskena kokuzula kwe-Wi-Fi okususelwa kunani ledatha yethrafikhi ekhona ekusebenzisaneni"</string>
     <string name="select_logd_size_title" msgid="7433137108348553508">"Amasayizi weloga ngebhafa"</string>
     <string name="select_logd_size_dialog_title" msgid="1206769310236476760">"Khetha amasayizi weloga ngebhafa ngayinye yelogu"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index bd963e9..ddb49b6 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -74,4 +74,13 @@
          fraction of a pixel.-->
     <fraction name="battery_subpixel_smoothing_left">0%</fraction>
     <fraction name="battery_subpixel_smoothing_right">0%</fraction>
+
+    <!-- Zen mode panel: condition item button padding -->
+    <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
+    <!-- Zen mode panel: spacing between condition items -->
+    <dimen name="zen_mode_condition_detail_item_spacing">12dp</dimen>
+    <!-- Zen mode panel: spacing between two-line condition upper and lower lines -->
+    <dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
+    <!-- Zen mode panel: bottom padding, a bit less than qs_panel_padding -->
+    <dimen name="zen_mode_condition_detail_bottom_padding">4dp</dimen>
 </resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f6541bb..5dcc927 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -498,6 +498,8 @@
     <string name="wifi_display_certification">Wireless display certification</string>
     <!-- Setting Checkbox title whether to enable WiFi Verbose Logging. [CHAR LIMIT=40] -->
     <string name="wifi_verbose_logging">Enable Wi\u2011Fi Verbose Logging</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization">Connected MAC Randomization</string>
     <!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
     <string name="mobile_data_always_on">Mobile data always active</string>
     <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
@@ -552,6 +554,8 @@
     <string name="wifi_display_certification_summary">Show options for wireless display certification</string>
     <!-- Setting Checkbox summary whether to enable Wifi verbose Logging [CHAR LIMIT=80] -->
     <string name="wifi_verbose_logging_summary">Increase Wi\u2011Fi logging level, show per SSID RSSI in Wi\u2011Fi Picker</string>
+    <!-- Setting Checkbox title whether to enable connected MAC randomization -->
+    <string name="wifi_connected_mac_randomization_summary">Randomize MAC address when connecting to Wi\u2011Fi networks</string>
     <!-- UI debug setting: limit size of Android logger buffers -->
     <string name="select_logd_size_title">Logger buffer sizes</string>
     <!-- UI debug setting: limit size of Android logger buffers [CHAR LIMIT=59] -->
@@ -1021,4 +1025,9 @@
         <item quantity="other">%1$d devices connected</item>
     </plurals>
 
+    <!-- Content description of zen mode time condition plus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_manual_zen_more_time">More time.</string>
+    <!-- Content description of zen mode time condition minus button (not shown on the screen). [CHAR LIMIT=NONE] -->
+    <string name="accessibility_manual_zen_less_time">Less time.</string>
+
 </resources>
diff --git a/packages/SettingsLib/res/values/styles.xml b/packages/SettingsLib/res/values/styles.xml
index 3f312f4..bae8387 100644
--- a/packages/SettingsLib/res/values/styles.xml
+++ b/packages/SettingsLib/res/values/styles.xml
@@ -21,4 +21,10 @@
     <style name="TextAppearanceMedium">
         <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
     </style>
+
+    <style name="BorderlessButton">
+        <item name="android:padding">12dp</item>
+        <item name="android:background">@drawable/btn_borderless_rect</item>
+        <item name="android:gravity">center</item>
+    </style>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 3c46d99..d001e66 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -21,10 +21,9 @@
 import android.os.UserManager;
 import android.print.PrintManager;
 import android.provider.Settings;
-
 import com.android.internal.util.UserIcons;
 import com.android.settingslib.drawable.UserIconDrawable;
-
+import com.android.settingslib.wrapper.LocationManagerWrapper;
 import java.text.NumberFormat;
 
 public class Utils {
@@ -45,6 +44,24 @@
         com.android.internal.R.drawable.ic_wifi_signal_4
     };
 
+    public static void updateLocationEnabled(Context context, boolean enabled, int userId) {
+        Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
+
+        final int oldMode = Settings.Secure.getIntForUser(context.getContentResolver(),
+                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId);
+        final int newMode = enabled
+                ? Settings.Secure.LOCATION_MODE_HIGH_ACCURACY
+                : Settings.Secure.LOCATION_MODE_OFF;
+        intent.putExtra(CURRENT_MODE_KEY, oldMode);
+        intent.putExtra(NEW_MODE_KEY, newMode);
+        context.sendBroadcastAsUser(
+                intent, UserHandle.of(userId), android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        LocationManager locationManager =
+                (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+        LocationManagerWrapper wrapper = new LocationManagerWrapper(locationManager);
+        wrapper.setLocationEnabledForUser(enabled, UserHandle.of(userId));
+    }
+
     public static boolean updateLocationMode(Context context, int oldMode, int newMode, int userId) {
         Intent intent = new Intent(LocationManager.MODE_CHANGING_ACTION);
         intent.putExtra(CURRENT_MODE_KEY, oldMode);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenRadioLayout.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
rename to packages/SettingsLib/src/com/android/settingslib/notification/ZenRadioLayout.java
index 360907b..1140028 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenRadioLayout.java
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenRadioLayout.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2018 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
@@ -12,7 +12,7 @@
  * permissions and limitations under the License.
  */
 
-package com.android.systemui.volume;
+package com.android.settingslib.notification;
 
 import android.content.Context;
 import android.util.AttributeSet;
@@ -22,7 +22,7 @@
 
 /**
  * Specialized layout for zen mode that allows the radio buttons to reside within
- * a RadioGroup, but also makes sure that all the heights off the radio buttons align
+ * a RadioGroup, but also makes sure that all the heights of the radio buttons align
  * with the corresponding content in the second child of this view.
  */
 public class ZenRadioLayout extends LinearLayout {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
new file mode 100644
index 0000000..1a268a6
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/wrapper/LocationManagerWrapper.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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.wrapper;
+
+import android.location.LocationManager;
+import android.os.UserHandle;
+
+/**
+ * This class replicates some methods of android.location.LocationManager that are new and not
+ * yet available in our current version of Robolectric. It provides a thin wrapper to call the real
+ * methods in production and a mock in tests.
+ */
+public class LocationManagerWrapper {
+
+    private LocationManager mLocationManager;
+
+    public LocationManagerWrapper(LocationManager locationManager) {
+        mLocationManager = locationManager;
+    }
+
+    /** Returns the real {@code LocationManager} object */
+    public LocationManager getLocationManager() {
+        return mLocationManager;
+    }
+
+    /** Wraps {@code LocationManager.isProviderEnabled} method */
+    public boolean isProviderEnabled(String provider) {
+        return mLocationManager.isProviderEnabled(provider);
+    }
+
+    /** Wraps {@code LocationManager.setProviderEnabledForUser} method */
+    public void setProviderEnabledForUser(String provider, boolean enabled, UserHandle userHandle) {
+        mLocationManager.setProviderEnabledForUser(provider, enabled, userHandle);
+    }
+
+    /** Wraps {@code LocationManager.isLocationEnabled} method */
+    public boolean isLocationEnabled() {
+        return mLocationManager.isLocationEnabled();
+    }
+
+    /** Wraps {@code LocationManager.isLocationEnabledForUser} method */
+    public boolean isLocationEnabledForUser(UserHandle userHandle) {
+        return mLocationManager.isLocationEnabledForUser(userHandle);
+    }
+
+    /** Wraps {@code LocationManager.setLocationEnabledForUser} method */
+    public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+        mLocationManager.setLocationEnabledForUser(enabled, userHandle);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index 976bbee..327c1c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -15,28 +15,8 @@
  */
 package com.android.settingslib;
 
-import android.app.ActivityManager;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.location.LocationManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.provider.Settings.Secure;
-import android.text.TextUtils;
-import java.util.HashMap;
-import java.util.Map;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
-import org.mockito.ArgumentMatchers;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
@@ -44,7 +24,28 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.ActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
 import android.content.res.Resources;
+import android.location.LocationManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.Secure;
+import android.text.TextUtils;
+import com.android.settingslib.wrapper.LocationManagerWrapper;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 import org.robolectric.annotation.Implementation;
 import org.robolectric.annotation.Implements;
 import org.robolectric.shadows.ShadowSettings;
@@ -53,7 +54,9 @@
 @Config(
         manifest = TestConfig.MANIFEST_PATH,
         sdk = TestConfig.SDK_VERSION,
-        shadows = {UtilsTest.ShadowSecure.class})
+        shadows = {
+            UtilsTest.ShadowSecure.class,
+            UtilsTest.ShadowLocationManagerWrapper.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
     private static final String PERCENTAGE_0 = "0%";
@@ -63,10 +66,14 @@
     private static final String PERCENTAGE_100 = "100%";
 
     private Context mContext;
+    @Mock
+    private LocationManager mLocationManager;
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mContext = spy(RuntimeEnvironment.application);
+        when(mContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
         ShadowSecure.reset();
     }
 
@@ -86,6 +93,17 @@
     }
 
     @Test
+    public void testUpdateLocationEnabled_sendBroadcast() {
+        int currentUserId = ActivityManager.getCurrentUser();
+        Utils.updateLocationEnabled(mContext, true, currentUserId);
+
+        verify(mContext).sendBroadcastAsUser(
+            argThat(actionMatches(LocationManager.MODE_CHANGING_ACTION)),
+            ArgumentMatchers.eq(UserHandle.of(currentUserId)),
+            ArgumentMatchers.eq(WRITE_SECURE_SETTINGS));
+    }
+
+    @Test
     public void testFormatPercentage_RoundTrue_RoundUpIfPossible() {
         final String[] expectedPercentages = {PERCENTAGE_0, PERCENTAGE_0, PERCENTAGE_1,
                 PERCENTAGE_1, PERCENTAGE_49, PERCENTAGE_49, PERCENTAGE_50, PERCENTAGE_50,
@@ -137,8 +155,26 @@
             return true;
         }
 
+        @Implementation
+        public static int getIntForUser(ContentResolver cr, String name, int def, int userHandle) {
+            if (map.containsKey(name)) {
+                return map.get(name);
+            } else {
+                return def;
+            }
+        }
+
         public static void reset() {
             map.clear();
         }
     }
+
+    @Implements(value = LocationManagerWrapper.class)
+    public static class ShadowLocationManagerWrapper {
+
+        @Implementation
+        public void setLocationEnabledForUser(boolean enabled, UserHandle userHandle) {
+            // Do nothing
+        }
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index fc765f4..cfd33a1 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.providers.settings;
 
+import android.os.Process;
 import com.android.internal.R;
 import com.android.internal.app.LocalePicker;
 import com.android.internal.annotations.VisibleForTesting;
@@ -288,12 +289,12 @@
         }
         final String GPS = LocationManager.GPS_PROVIDER;
         boolean enabled =
-                GPS.equals(value) ||
+            GPS.equals(value) ||
                 value.startsWith(GPS + ",") ||
                 value.endsWith("," + GPS) ||
                 value.contains("," + GPS + ",");
-        Settings.Secure.setLocationProviderEnabled(
-                mContext.getContentResolver(), GPS, enabled);
+        LocationManager lm = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+        lm.setProviderEnabledForUser(GPS, enabled, Process.myUserHandle());
     }
 
     private void setSoundEffects(boolean enable) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 87ed7eb..55f7a0a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1128,6 +1128,9 @@
         dumpSetting(s, p,
                 Settings.Global.SHOW_FIRST_CRASH_DIALOG,
                 GlobalSettingsProto.SHOW_FIRST_CRASH_DIALOG);
+        dumpSetting(s, p,
+                    Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED,
+                    GlobalSettingsProto.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED);
     }
 
     /** Dump a single {@link SettingsState.Setting} to a proto buf */
@@ -1220,9 +1223,6 @@
         dumpSetting(s, p,
                 Settings.Secure.LOCATION_MODE,
                 SecureSettingsProto.LOCATION_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.LOCATION_PREVIOUS_MODE,
-                SecureSettingsProto.LOCATION_PREVIOUS_MODE);
         // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 80ac825..4b2e62c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -323,7 +323,7 @@
         </activity>
 
         <!-- Springboard for launching the share activity -->
-        <receiver android:name=".screenshot.GlobalScreenshot$ShareReceiver"
+        <receiver android:name=".screenshot.GlobalScreenshot$ScreenshotActionReceiver"
             android:process=":screenshot"
             android:exported="false" />
 
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
new file mode 100644
index 0000000..ed637a7
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_close_animation.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2017 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.
+-->
+<animator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="133"
+    android:valueType="intType"
+    android:valueFrom="@dimen/car_user_switcher_container_height"
+    android:valueTo="0"
+    android:interpolator="@android:interpolator/fast_out_slow_in" />
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
new file mode 100644
index 0000000..227c981
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_close_icon_animation.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="167"
+        android:propertyName="rotation"
+        android:valueType="floatType"
+        android:valueFrom="180"
+        android:valueTo="0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
new file mode 100644
index 0000000..5901ff4
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_close_name_animation.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="83"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="0"
+        android:valueTo="1" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
new file mode 100644
index 0000000..41cbe4b
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_close_pages_animation.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="83"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="1"
+        android:valueTo="0" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
new file mode 100644
index 0000000..341e7e0
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_close_pod_animation.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:ordering="together" >
+
+    <objectAnimator
+        android:duration="50"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="1"
+        android:valueTo="0" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
new file mode 100644
index 0000000..6ae7413
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_open_animation.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2017 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.
+-->
+<animator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="200"
+    android:valueType="intType"
+    android:valueFrom="0"
+    android:valueTo="@dimen/car_user_switcher_container_height"
+    android:interpolator="@android:interpolator/fast_out_slow_in" />
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
new file mode 100644
index 0000000..06ac9e3
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_open_icon_animation.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="167"
+        android:propertyName="rotation"
+        android:valueType="floatType"
+        android:valueFrom="0"
+        android:valueTo="180"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
new file mode 100644
index 0000000..4baefb8
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_open_name_animation.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="83"
+        android:startOffset="83"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="1"
+        android:valueTo="0" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
new file mode 100644
index 0000000..2d0deb9
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_open_pages_animation.xml
@@ -0,0 +1,24 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <objectAnimator
+        android:duration="83"
+        android:startOffset="83"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="0"
+        android:valueTo="1" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml b/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
new file mode 100644
index 0000000..3315220
--- /dev/null
+++ b/packages/SystemUI/res/anim/car_user_switcher_open_pod_animation.xml
@@ -0,0 +1,33 @@
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:ordering="together" >
+
+    <objectAnimator
+        android:duration="167"
+        android:startOffset="67"
+        android:propertyName="translationY"
+        android:valueType="floatType"
+        android:valueFrom="@dimen/car_user_switcher_container_anim_height"
+        android:valueTo="0"
+        android:interpolator="@android:interpolator/fast_out_slow_in" />
+    <objectAnimator
+        android:duration="83"
+        android:startOffset="117"
+        android:propertyName="alpha"
+        android:valueType="floatType"
+        android:valueFrom="0"
+        android:valueTo="1" />
+</set>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow.xml b/packages/SystemUI/res/drawable/car_ic_arrow.xml
index 2c5ad27..d400ed8 100644
--- a/packages/SystemUI/res/drawable/car_ic_arrow.xml
+++ b/packages/SystemUI/res/drawable/car_ic_arrow.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2015 The Android Open Source Project
+  ~ Copyright (C) 2017 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.
diff --git a/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml b/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
new file mode 100644
index 0000000..33a512e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/car_ic_arrow_drop_up.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2015 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48.0dp"
+        android:height="48.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M14 28l10-10 10 10z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
index 2f16516..0ee40d7 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod.xml
@@ -15,27 +15,40 @@
      limitations under the License.
 -->
 
-<LinearLayout
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
+    android:clipChildren="false"
+    android:alpha="0"
     android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center" >
+    android:layout_height="@dimen/car_fullscreen_user_pod_height"
+    android:layout_gravity="center_horizontal|bottom" >
 
     <ImageView android:id="@+id/user_avatar"
-        android:layout_gravity="center"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_image_top"
         android:layout_width="@dimen/car_fullscreen_user_pod_image_avatar_width"
-        android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height" />
+        android:layout_height="@dimen/car_fullscreen_user_pod_image_avatar_height"
+        android:layout_above="@id/user_name" />
 
     <TextView android:id="@+id/user_name"
         android:layout_width="@dimen/car_fullscreen_user_pod_width"
         android:layout_height="wrap_content"
         android:layout_marginTop="@dimen/car_fullscreen_user_pod_margin_name_top"
         android:layout_marginBottom="@dimen/car_fullscreen_user_pod_margin_name_bottom"
-        android:textSize="@dimen/car_fullscreen_user_pod_text_size"
+        android:textSize="@dimen/car_fullscreen_user_pod_name_text_size"
         android:textColor="@color/qs_user_detail_name"
         android:ellipsize="end"
         android:singleLine="true"
         android:gravity="center_horizontal"
-        android:layout_gravity="center_horizontal" />
-</LinearLayout>
+        android:layout_above="@id/device_name" />
+
+    <TextView android:id="@+id/device_name"
+        android:layout_width="@dimen/car_fullscreen_user_pod_width"
+        android:layout_height="wrap_content"
+        android:textSize="@dimen/car_fullscreen_user_pod_device_text_size"
+        android:textColor="@color/qs_user_detail_name"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:gravity="center_horizontal"
+        android:layout_alignParentBottom="true" />
+</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml b/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml
index 99d010f..d666a20 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_pod_container.xml
@@ -16,10 +16,10 @@
 -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    android:clipChildren="false"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:gravity="center"
-    android:layout_gravity="center" >
+    android:gravity="center" >
 
     <!-- car_fullscreen_user_pods will be dynamically added here. -->
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 257e281..478b656 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -54,13 +54,13 @@
             android:layout_height="wrap_content"
             android:layout_marginLeft="@dimen/car_margin"
             android:layout_marginRight="@dimen/car_margin"
+            android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
             android:layout_centerInParent="true" />
 
         <com.android.systemui.statusbar.car.PageIndicator
             android:id="@+id/user_switcher_page_indicator"
             android:layout_width="match_parent"
             android:layout_height="@dimen/car_page_indicator_dot_diameter"
-            android:layout_marginTop="@dimen/car_page_indicator_margin_top"
             android:layout_below="@+id/user_grid" />
 
         <Button
diff --git a/packages/SystemUI/res/layout/car_qs_footer.xml b/packages/SystemUI/res/layout/car_qs_footer.xml
index 044090b..3afd4ea 100644
--- a/packages/SystemUI/res/layout/car_qs_footer.xml
+++ b/packages/SystemUI/res/layout/car_qs_footer.xml
@@ -35,7 +35,6 @@
         android:layout_centerVertical="true"
         android:layout_width="@dimen/car_qs_footer_icon_width"
         android:layout_height="@dimen/car_qs_footer_icon_height"
-        android:layout_marginRight="@dimen/car_qs_footer_user_switch_margin_right"
         android:background="@drawable/ripple_drawable"
         android:focusable="true">
 
@@ -47,6 +46,18 @@
             android:scaleType="fitCenter"/>
     </com.android.systemui.statusbar.phone.MultiUserSwitch>
 
+    <ImageView
+        android:id="@+id/user_switch_expand_icon"
+        android:layout_height="match_parent"
+        android:layout_width="@dimen/car_qs_footer_user_switch_icon_width"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@+id/multi_user_switch"
+        android:layout_marginLeft="@dimen/car_qs_footer_user_switch_icon_margin"
+        android:layout_marginRight="@dimen/car_qs_footer_user_switch_icon_margin"
+        android:src="@drawable/car_ic_arrow_drop_up"
+        android:scaleType="fitCenter">
+    </ImageView>
+
     <TextView android:id="@+id/user_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
@@ -54,7 +65,7 @@
         android:textColor="@color/car_qs_footer_user_name_color"
         android:gravity="start|center_vertical"
         android:layout_centerVertical="true"
-        android:layout_toEndOf="@id/multi_user_switch" />
+        android:layout_toEndOf="@id/user_switch_expand_icon" />
 
     <com.android.systemui.statusbar.phone.SettingsButton
         android:id="@+id/settings_button"
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index 4cb0fd5..7844cac 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -16,6 +16,7 @@
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/quick_settings_container"
+    android:clipChildren="false"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:background="@color/car_qs_background_primary"
@@ -26,10 +27,32 @@
     <include layout="@layout/car_status_bar_header"/>
     <include layout="@layout/car_qs_footer"/>
 
-    <com.android.systemui.statusbar.car.UserGridView
-        android:id="@+id/user_grid"
+    <RelativeLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/user_switcher_container"
+        android:clipChildren="false"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginLeft="@dimen/car_margin"
-        android:layout_marginRight="@dimen/car_margin" />
+        android:layout_height="@dimen/car_user_switcher_container_height"
+        android:layout_gravity="center_horizontal" >
+
+        <com.android.systemui.statusbar.car.UserGridView
+            android:id="@+id/user_grid"
+            android:clipChildren="false"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="@dimen/car_margin"
+            android:layout_marginRight="@dimen/car_margin"
+            android:layout_marginBottom="@dimen/car_user_grid_margin_bottom"
+            android:layout_above="@id/user_switcher_page_indicator" />
+
+        <com.android.systemui.statusbar.car.PageIndicator
+            android:id="@+id/user_switcher_page_indicator"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/car_page_indicator_dot_diameter"
+            android:layout_marginBottom="@dimen/car_page_indicator_margin_bottom"
+            android:alpha="0"
+            android:layout_alignParentBottom="true" />
+
+    </RelativeLayout>
+
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index 3826bdd..5862413 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -93,7 +93,7 @@
 
         </RelativeLayout>
 
-        <com.android.systemui.volume.ZenRadioLayout
+        <com.android.settingslib.notification.ZenRadioLayout
             android:id="@+id/zen_conditions"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
@@ -111,7 +111,7 @@
                 android:layout_width="fill_parent"
                 android:layout_height="fill_parent"
                 android:orientation="vertical"/>
-        </com.android.systemui.volume.ZenRadioLayout>
+        </com.android.settingslib.notification.ZenRadioLayout>
 
         <TextView
             android:id="@+id/zen_alarm_warning"
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index 8853587..f3c9f89 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -18,20 +18,23 @@
 <resources>
     <dimen name="car_margin">148dp</dimen>
 
+    <dimen name="car_fullscreen_user_pod_margin_image_top">24dp</dimen>
     <dimen name="car_fullscreen_user_pod_margin_name_top">24dp</dimen>
-    <dimen name="car_fullscreen_user_pod_margin_name_bottom">64dp</dimen>
+    <dimen name="car_fullscreen_user_pod_margin_name_bottom">20dp</dimen>
     <dimen name="car_fullscreen_user_pod_margin_between">24dp</dimen>
     <dimen name="car_fullscreen_user_pod_icon_text_size">96dp</dimen>
     <dimen name="car_fullscreen_user_pod_image_avatar_width">192dp</dimen>
     <dimen name="car_fullscreen_user_pod_image_avatar_height">192dp</dimen>
     <dimen name="car_fullscreen_user_pod_width">264dp</dimen>
-    <dimen name="car_fullscreen_user_pod_text_size">40sp</dimen> <!-- B1 -->
+    <dimen name="car_fullscreen_user_pod_height">356dp</dimen>
+    <dimen name="car_fullscreen_user_pod_name_text_size">40sp</dimen> <!-- B1 -->
+    <dimen name="car_fullscreen_user_pod_device_text_size">@dimen/car_body2_size</dimen>
 
     <dimen name="car_navigation_button_width">64dp</dimen>
     <dimen name="car_navigation_bar_width">760dp</dimen>
 
     <dimen name="car_page_indicator_dot_diameter">12dp</dimen>
-    <dimen name="car_page_indicator_margin_top">32dp</dimen>
+    <dimen name="car_page_indicator_margin_bottom">24dp</dimen>
 
     <dimen name="car_user_switcher_progress_bar_height">6dp</dimen>
     <dimen name="car_user_switcher_progress_bar_margin_top">@dimen/status_bar_height</dimen>
@@ -47,8 +50,14 @@
     <dimen name="car_qs_footer_padding_start">46dp</dimen>
     <dimen name="car_qs_footer_icon_width">56dp</dimen>
     <dimen name="car_qs_footer_icon_height">56dp</dimen>
-    <dimen name="car_qs_footer_user_switch_margin_right">46dp</dimen>
+    <dimen name="car_qs_footer_user_switch_icon_margin">5dp</dimen>
+    <dimen name="car_qs_footer_user_switch_icon_width">36dp</dimen>
     <dimen name="car_qs_footer_user_name_text_size">@dimen/car_body2_size</dimen>
 
+    <dimen name="car_user_switcher_container_height">420dp</dimen>
+    <!-- This must be the negative of car_user_switcher_container_height for the animation. -->
+    <dimen name="car_user_switcher_container_anim_height">-420dp</dimen>
+    <dimen name="car_user_grid_margin_bottom">28dp</dimen>
+
     <dimen name="car_body2_size">26sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/integers_car.xml b/packages/SystemUI/res/values/integers_car.xml
index 320ee9f..f84dd4b 100644
--- a/packages/SystemUI/res/values/integers_car.xml
+++ b/packages/SystemUI/res/values/integers_car.xml
@@ -19,4 +19,5 @@
     <integer name="car_user_switcher_timeout_ms">15000</integer>
     <!-- This values less than ProgressBar.PROGRESS_ANIM_DURATION for a smooth animation. -->
     <integer name="car_user_switcher_anim_update_ms">60</integer>
+    <integer name="car_user_switcher_anim_cascade_delay_ms">27</integer>
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ab83bcf..edf19fd 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -675,6 +675,15 @@
     <string name="quick_settings_bluetooth_off_label">Bluetooth Off</string>
     <!-- QuickSettings: Bluetooth detail panel, text when there are no items [CHAR LIMIT=NONE] -->
     <string name="quick_settings_bluetooth_detail_empty_text">No paired devices available</string>
+    <!-- QuickSettings: Bluetooth secondary label for the battery level of a connected device [CHAR LIMIT=20]-->
+    <string name="quick_settings_bluetooth_secondary_label_battery_level"><xliff:g id="battery_level_as_percentage">%s</xliff:g> battery</string>
+    <!-- QuickSettings: Bluetooth secondary label for an audio device being connected [CHAR LIMIT=20]-->
+    <string name="quick_settings_bluetooth_secondary_label_audio">Audio</string>
+    <!-- QuickSettings: Bluetooth secondary label for a headset being connected [CHAR LIMIT=20]-->
+    <string name="quick_settings_bluetooth_secondary_label_headset">Headset</string>
+    <!-- QuickSettings: Bluetooth secondary label for an input/IO device being connected [CHAR LIMIT=20]-->
+    <string name="quick_settings_bluetooth_secondary_label_input">Input</string>
+
     <!-- QuickSettings: Brightness [CHAR LIMIT=NONE] -->
     <string name="quick_settings_brightness_label">Brightness</string>
     <!-- QuickSettings: Rotation Unlocked [CHAR LIMIT=NONE] -->
@@ -755,6 +764,11 @@
     <string name="quick_settings_tethering_label">Tethering</string>
     <!-- QuickSettings: Hotspot. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_hotspot_label">Hotspot</string>
+    <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] -->
+    <plurals name="quick_settings_hotspot_num_devices">
+        <item quantity="one">%d device</item>
+        <item quantity="other">%d devices</item>
+    </plurals>
     <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] -->
     <string name="quick_settings_notifications_label">Notifications</string>
     <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index d80a336..9ff6815 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -333,6 +333,7 @@
      */
     private class KeyguardSliceButton extends Button {
 
+        private static final float SEPARATOR_HEIGHT = 0.7f;
         private final Paint mPaint;
         private boolean mHasDivider;
 
@@ -369,7 +370,9 @@
             super.onDraw(canvas);
             if (mHasDivider) {
                 final int lineX = getLayoutDirection() == LAYOUT_DIRECTION_RTL ? 0 : getWidth();
-                canvas.drawLine(lineX, 0, lineX, getHeight(), mPaint);
+                final int height = (int) (getHeight() * SEPARATOR_HEIGHT);
+                final int startY = getHeight() / 2 - height / 2;
+                canvas.drawLine(lineX, startY, lineX, startY + height, mPaint);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 2873afb..2b656c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -171,15 +171,16 @@
     }
 
     private void onSliceContentChanged(boolean hasHeader) {
-        final float clockScale = hasHeader ? mSmallClockScale : 1;
+        final boolean smallClock = hasHeader || mPulsing;
+        final float clockScale = smallClock ? mSmallClockScale : 1;
         float translation = (mClockView.getHeight() - (mClockView.getHeight() * clockScale)) / 2f;
-        if (hasHeader) {
+        if (smallClock) {
             translation -= mWidgetPadding;
         }
         mClockView.setTranslationY(translation);
         mClockView.setScaleX(clockScale);
         mClockView.setScaleY(clockScale);
-        mClockSeparator.setVisibility(hasHeader ? VISIBLE : GONE);
+        mClockSeparator.setVisibility(hasHeader && !mPulsing ? VISIBLE : GONE);
     }
 
     @Override
@@ -329,6 +330,8 @@
 
     public void setPulsing(boolean pulsing) {
         mPulsing = pulsing;
+        mKeyguardSlice.setVisibility(pulsing ? GONE : VISIBLE);
+        onSliceContentChanged(mKeyguardSlice.hasHeader());
         updateDozeVisibleViews();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 2b48e0f..c0fed34 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -42,9 +42,8 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
-
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.policy.PipSnapAlgorithm;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -63,10 +62,6 @@
     // Allow the PIP to be flung from anywhere on the screen to the bottom to be dismissed.
     private static final boolean ENABLE_FLING_DISMISS = false;
 
-    // These values are used for metrics and should never change
-    private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
-    private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
-
     private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
 
     // Allow dragging the PIP to a location to close it
@@ -163,8 +158,7 @@
         @Override
         public void onPipDismiss() {
             mMotionHelper.dismissPip();
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                    METRIC_VALUE_DISMISSED_BY_TAP);
+            MetricsLoggerWrapper.logPictureInPictureDismissByTap(mContext);
         }
 
         @Override
@@ -463,8 +457,7 @@
             return;
         }
         if (mIsMinimized != isMinimized) {
-            MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MINIMIZED,
-                    isMinimized);
+            MetricsLoggerWrapper.logPictureInPictureMinimize(mContext, isMinimized);
         }
         mIsMinimized = isMinimized;
         mSnapAlgorithm.setMinimized(isMinimized);
@@ -537,8 +530,7 @@
         mMenuState = menuState;
         updateMovementBounds(menuState);
         if (menuState != MENU_STATE_CLOSE) {
-            MetricsLogger.visibility(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_MENU,
-                    menuState == MENU_STATE_FULL);
+            MetricsLoggerWrapper.logPictureInPictureMenuVisible(mContext, menuState == MENU_STATE_FULL);
         }
     }
 
@@ -670,9 +662,7 @@
                 if (mMotionHelper.shouldDismissPip() || isFlingToBot) {
                     mMotionHelper.animateDismiss(mMotionHelper.getBounds(), vel.x,
                         vel.y, mUpdateScrimListener);
-                    MetricsLogger.action(mContext,
-                            MetricsEvent.ACTION_PICTURE_IN_PICTURE_DISMISSED,
-                            METRIC_VALUE_DISMISSED_BY_DRAG);
+                    MetricsLoggerWrapper.logPictureInPictureDismissByDrag(mContext);
                     return true;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
index 142aab2..23d3ebbb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFooter.java
@@ -47,7 +47,7 @@
     private MultiUserSwitch mMultiUserSwitch;
     private TextView mUserName;
     private ImageView mMultiUserAvatar;
-    private UserGridView mUserGridView;
+    private CarQSFragment.UserSwitchCallback mUserSwitchCallback;
 
     public CarQSFooter(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -63,15 +63,15 @@
         mUserInfoController = Dependency.get(UserInfoController.class);
 
         mMultiUserSwitch.setOnClickListener(v -> {
-            if (mUserGridView == null) {
+            if (mUserSwitchCallback == null) {
                 Log.e(TAG, "CarQSFooter not properly set up; cannot display user switcher.");
                 return;
             }
 
-            if (!mUserGridView.isShowing()) {
-                mUserGridView.show();
+            if (!mUserSwitchCallback.isShowing()) {
+                mUserSwitchCallback.show();
             } else {
-                mUserGridView.hide();
+                mUserSwitchCallback.hide();
             }
         });
 
@@ -102,8 +102,8 @@
         }
     }
 
-    public void setUserGridView(UserGridView view) {
-        mUserGridView = view;
+    public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
+        mUserSwitchCallback = callback;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
index 13298d3..0ee6d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarQSFragment.java
@@ -13,6 +13,12 @@
  */
 package com.android.systemui.qs.car;
 
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
 import android.app.Fragment;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
@@ -26,18 +32,29 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.QSFooter;
+import com.android.systemui.statusbar.car.PageIndicator;
 import com.android.systemui.statusbar.car.UserGridView;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A quick settings fragment for the car. For auto, there is no row for quick settings or ability
  * to expand the quick settings panel. Instead, the only thing is that displayed is the
  * status bar, and a static row with access to the user switcher and settings.
  */
 public class CarQSFragment extends Fragment implements QS {
+    private ViewGroup mPanel;
     private View mHeader;
+    private View mUserSwitcherContainer;
     private CarQSFooter mFooter;
+    private View mFooterUserName;
+    private View mFooterExpandIcon;
     private UserGridView mUserGridView;
+    private PageIndicator mPageIndicator;
+    private AnimatorSet mAnimatorSet;
+    private UserSwitchCallback mUserSwitchCallback;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -48,14 +65,26 @@
     @Override
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
+        mPanel = (ViewGroup) view;
         mHeader = view.findViewById(R.id.header);
         mFooter = view.findViewById(R.id.qs_footer);
+        mFooterUserName = mFooter.findViewById(R.id.user_name);
+        mFooterExpandIcon = mFooter.findViewById(R.id.user_switch_expand_icon);
+
+        mUserSwitcherContainer = view.findViewById(R.id.user_switcher_container);
+
+        updateUserSwitcherHeight(0);
 
         mUserGridView = view.findViewById(R.id.user_grid);
         mUserGridView.init(null, Dependency.get(UserSwitcherController.class),
-                false /* showInitially */);
+                false /* overrideAlpha */);
 
-        mFooter.setUserGridView(mUserGridView);
+        mPageIndicator = view.findViewById(R.id.user_switcher_page_indicator);
+        mPageIndicator.setupWithViewPager(mUserGridView);
+
+        mUserSwitchCallback = new UserSwitchCallback();
+        mFooter.setUserSwitchCallback(mUserSwitchCallback);
+        mUserGridView.setUserSwitchCallback(mUserSwitchCallback);
     }
 
     @Override
@@ -82,11 +111,13 @@
     @Override
     public void setHeaderListening(boolean listening) {
         mFooter.setListening(listening);
+        mUserGridView.setListening(listening);
     }
 
     @Override
     public void setListening(boolean listening) {
         mFooter.setListening(listening);
+        mUserGridView.setListening(listening);
     }
 
     @Override
@@ -171,4 +202,126 @@
     public void setExpandClickListener(OnClickListener onClickListener) {
         // No ability to expand the quick settings.
     }
+
+    public class UserSwitchCallback {
+        private boolean mShowing;
+
+        public boolean isShowing() {
+            return mShowing;
+        }
+
+        public void show() {
+            mShowing = true;
+            animateHeightChange(true /* opening */);
+        }
+
+        public void hide() {
+            mShowing = false;
+            animateHeightChange(false /* opening */);
+        }
+
+        public void resetShowing() {
+            if (mShowing) {
+                for (int i = 0; i < mUserGridView.getChildCount(); i++) {
+                    ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
+                    // Need to bring the last child to the front to maintain the order in the pod
+                    // container. Why? ¯\_(ツ)_/¯
+                    if (podContainer.getChildCount() > 0) {
+                        podContainer.getChildAt(podContainer.getChildCount() - 1).bringToFront();
+                    }
+                    // The alpha values are default to 0, so if the pods have been refreshed, they
+                    // need to be set to 1 when showing.
+                    for (int j = 0; j < podContainer.getChildCount(); j++) {
+                        podContainer.getChildAt(j).setAlpha(1f);
+                    }
+                }
+            }
+        }
+    }
+
+    private void updateUserSwitcherHeight(int height) {
+        ViewGroup.LayoutParams layoutParams = mUserSwitcherContainer.getLayoutParams();
+        layoutParams.height = height;
+        mUserSwitcherContainer.requestLayout();
+    }
+
+    private void animateHeightChange(boolean opening) {
+        // Animation in progress; cancel it to avoid contention.
+        if (mAnimatorSet != null){
+            mAnimatorSet.cancel();
+        }
+
+        List<Animator> allAnimators = new ArrayList<>();
+        ValueAnimator heightAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(getContext(),
+                opening ? R.anim.car_user_switcher_open_animation
+                        : R.anim.car_user_switcher_close_animation);
+        heightAnimator.addUpdateListener(valueAnimator -> {
+            updateUserSwitcherHeight((Integer) valueAnimator.getAnimatedValue());
+        });
+        allAnimators.add(heightAnimator);
+
+        // The user grid contains pod containers that each contain a number of pods.  Animate
+        // all pods to avoid any discrepancy/race conditions with possible changes during the
+        // animation.
+        int cascadeDelay = getResources().getInteger(
+                R.integer.car_user_switcher_anim_cascade_delay_ms);
+        for (int i = 0; i < mUserGridView.getChildCount(); i++) {
+            ViewGroup podContainer = (ViewGroup) mUserGridView.getChildAt(i);
+            for (int j = 0; j < podContainer.getChildCount(); j++) {
+                View pod = podContainer.getChildAt(j);
+                Animator podAnimator = AnimatorInflater.loadAnimator(getContext(),
+                        opening ? R.anim.car_user_switcher_open_pod_animation
+                                : R.anim.car_user_switcher_close_pod_animation);
+                // Add the cascading delay between pods
+                if (opening) {
+                    podAnimator.setStartDelay(podAnimator.getStartDelay() + j * cascadeDelay);
+                }
+                podAnimator.setTarget(pod);
+                allAnimators.add(podAnimator);
+            }
+        }
+
+        Animator nameAnimator = AnimatorInflater.loadAnimator(getContext(),
+                opening ? R.anim.car_user_switcher_open_name_animation
+                        : R.anim.car_user_switcher_close_name_animation);
+        nameAnimator.setTarget(mFooterUserName);
+        allAnimators.add(nameAnimator);
+
+        Animator iconAnimator = AnimatorInflater.loadAnimator(getContext(),
+                opening ? R.anim.car_user_switcher_open_icon_animation
+                        : R.anim.car_user_switcher_close_icon_animation);
+        iconAnimator.setTarget(mFooterExpandIcon);
+        allAnimators.add(iconAnimator);
+
+        Animator pageAnimator = AnimatorInflater.loadAnimator(getContext(),
+                opening ? R.anim.car_user_switcher_open_pages_animation
+                        : R.anim.car_user_switcher_close_pages_animation);
+        pageAnimator.setTarget(mPageIndicator);
+        allAnimators.add(pageAnimator);
+
+        mAnimatorSet = new AnimatorSet();
+        mAnimatorSet.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                mAnimatorSet = null;
+            }
+        });
+        mAnimatorSet.playTogether(allAnimators.toArray(new Animator[0]));
+
+        // Setup all values to the start values in the animations, since there are delays, but need
+        // to have all values start at the beginning.
+        setupInitialValues(mAnimatorSet);
+
+        mAnimatorSet.start();
+    }
+
+    private void setupInitialValues(Animator anim) {
+        if (anim instanceof AnimatorSet) {
+            for (Animator a : ((AnimatorSet) anim).getChildAnimations()) {
+                setupInitialValues(a);
+            }
+        } else if (anim instanceof ObjectAnimator) {
+            ((ObjectAnimator) anim).setCurrentFraction(0.0f);
+        }
+    }
 }
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 fff9f8e..2607ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -18,7 +18,9 @@
 
 import static com.android.settingslib.graph.BluetoothDeviceLayerDrawable.createLayerDrawable;
 
+import android.annotation.Nullable;
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
@@ -26,7 +28,6 @@
 import android.graphics.drawable.Drawable;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.Switch;
@@ -35,6 +36,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.graph.BluetoothDeviceLayerDrawable;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.ActivityStarter;
@@ -126,21 +128,25 @@
         }
         state.slash.isSlashed = !enabled;
         state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
+
         if (enabled) {
             if (connected) {
                 state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected);
-                state.secondaryLabel = mController.getLastDeviceName();
-                CachedBluetoothDevice lastDevice = mController.getLastDevice();
+                state.contentDescription = mContext.getString(
+                        R.string.accessibility_bluetooth_name, state.label);
+
+                final CachedBluetoothDevice lastDevice = mController.getLastDevice();
                 if (lastDevice != null) {
-                    int batteryLevel = lastDevice.getBatteryLevel();
+                    final int batteryLevel = lastDevice.getBatteryLevel();
                     if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
-                        state.icon = new BluetoothBatteryTileIcon(lastDevice,
+                        state.icon = new BluetoothBatteryTileIcon(
+                                batteryLevel,
                                 mContext.getResources().getFraction(
                                         R.fraction.bt_battery_scale_fraction, 1, 1));
                     }
                 }
-                state.contentDescription = mContext.getString(
-                        R.string.accessibility_bluetooth_name, state.secondaryLabel);
+
+                state.label = mController.getLastDeviceName();
             } else if (state.isTransient) {
                 state.icon = ResourceIcon.get(R.drawable.ic_bluetooth_transient_animation);
                 state.contentDescription = mContext.getString(
@@ -159,11 +165,53 @@
             state.state = Tile.STATE_INACTIVE;
         }
 
+        state.secondaryLabel = getSecondaryLabel(enabled, connected);
+
         state.dualLabelContentDescription = mContext.getResources().getString(
                 R.string.accessibility_quick_settings_open_settings, getTileLabel());
         state.expandedAccessibilityClassName = Switch.class.getName();
     }
 
+    /**
+     * Returns the secondary label to use for the given bluetooth connection in the form of the
+     * battery level or bluetooth profile name. If the bluetooth is disabled, there's no connected
+     * devices, or we can't map the bluetooth class to a profile, this instead returns {@code null}.
+     *
+     * @param enabled whether bluetooth is enabled
+     * @param connected whether there's a device connected via bluetooth
+     */
+    @Nullable
+    private String getSecondaryLabel(boolean enabled, boolean connected) {
+        final CachedBluetoothDevice lastDevice = mController.getLastDevice();
+
+        if (enabled && connected && lastDevice != null) {
+            final int batteryLevel = lastDevice.getBatteryLevel();
+
+            if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+                return mContext.getString(
+                        R.string.quick_settings_bluetooth_secondary_label_battery_level,
+                        Utils.formatPercentage(batteryLevel));
+
+            } else {
+                final BluetoothClass bluetoothClass = lastDevice.getBtClass();
+                if (bluetoothClass != null) {
+                    if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_A2DP)) {
+                        return mContext.getString(
+                                R.string.quick_settings_bluetooth_secondary_label_audio);
+                    } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HEADSET)) {
+                        return mContext.getString(
+                                R.string.quick_settings_bluetooth_secondary_label_headset);
+                    } else if (bluetoothClass.doesClassMatch(BluetoothClass.PROFILE_HID)) {
+                        return mContext.getString(
+                                R.string.quick_settings_bluetooth_secondary_label_input);
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.QS_BLUETOOTH;
@@ -207,20 +255,29 @@
         return new BluetoothDetailAdapter();
     }
 
+    /**
+     * Bluetooth icon wrapper for Quick Settings with a battery indicator that reflects the
+     * connected device's battery level. This is used instead of
+     * {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to use a context
+     * that reflects dark/light theme attributes.
+     */
     private class BluetoothBatteryTileIcon extends Icon {
+        private int mBatteryLevel;
         private float mIconScale;
-        private CachedBluetoothDevice mDevice;
 
-        BluetoothBatteryTileIcon(CachedBluetoothDevice device, float iconScale) {
+        BluetoothBatteryTileIcon(int batteryLevel, float iconScale) {
+            mBatteryLevel = batteryLevel;
             mIconScale = iconScale;
-            mDevice = device;
         }
 
         @Override
         public Drawable getDrawable(Context context) {
             // This method returns Pair<Drawable, String> while first value is the drawable
-            return com.android.settingslib.bluetooth.Utils.getBtClassDrawableWithDescription(
-                    context, mDevice, mIconScale).first;
+            return BluetoothDeviceLayerDrawable.createLayerDrawable(
+                    context,
+                    R.drawable.ic_qs_bluetooth_connected,
+                    mBatteryLevel,
+                    mIconScale);
         }
     }
 
@@ -302,8 +359,7 @@
                         item.iconResId = R.drawable.ic_qs_bluetooth_connected;
                         int batteryLevel = device.getBatteryLevel();
                         if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
-                            item.icon = new BluetoothBatteryTileIcon(device,
-                                    1 /* iconScale */);
+                            item.icon = new BluetoothBatteryTileIcon(batteryLevel,1 /* iconScale */);
                             item.line2 = mContext.getString(
                                     R.string.quick_settings_connected_battery_level,
                                     Utils.formatPercentage(batteryLevel));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 910b6b1..e1b58fe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.qs.tiles;
 
+import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -37,7 +38,7 @@
 /** Quick settings tile: Hotspot **/
 public class HotspotTile extends QSTileImpl<AirplaneBooleanState> {
     static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName(
-             "com.android.settings", "com.android.settings.TetherSettings"));
+            "com.android.settings", "com.android.settings.TetherSettings"));
 
     private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
     private final Icon mUnavailable = ResourceIcon.get(R.drawable.ic_hotspot_unavailable);
@@ -115,11 +116,19 @@
         state.label = mContext.getString(R.string.quick_settings_hotspot_label);
 
         checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_TETHERING);
-        if (arg instanceof Boolean) {
-            state.value = (boolean) arg;
+
+        final int numConnectedDevices;
+        if (arg instanceof CallbackInfo) {
+            CallbackInfo info = (CallbackInfo) arg;
+            state.value = info.enabled;
+            numConnectedDevices = info.numConnectedDevices;
         } else {
             state.value = mController.isHotspotEnabled();
+            numConnectedDevices = mController.getNumConnectedDevices();
         }
+
+        state.secondaryLabel = getSecondaryLabel(state.value, numConnectedDevices);
+
         state.icon = mEnabledStatic;
         state.isAirplaneMode = mAirplaneMode.getValue() != 0;
         state.isTransient = mController.isHotspotTransient();
@@ -133,6 +142,18 @@
                 : state.value || state.isTransient ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
     }
 
+    @Nullable
+    private String getSecondaryLabel(boolean enabled, int numConnectedDevices) {
+        if (numConnectedDevices > 0 && enabled) {
+            return mContext.getResources().getQuantityString(
+                    R.plurals.quick_settings_hotspot_num_devices,
+                    numConnectedDevices,
+                    numConnectedDevices);
+        }
+
+        return null;
+    }
+
     @Override
     public int getMetricsCategory() {
         return MetricsEvent.QS_HOTSPOT;
@@ -148,9 +169,30 @@
     }
 
     private final class Callback implements HotspotController.Callback {
+        final CallbackInfo mCallbackInfo = new CallbackInfo();
+
         @Override
-        public void onHotspotChanged(boolean enabled) {
-            refreshState(enabled);
+        public void onHotspotChanged(boolean enabled, int numConnectedDevices) {
+            mCallbackInfo.enabled = enabled;
+            mCallbackInfo.numConnectedDevices = numConnectedDevices;
+            refreshState(mCallbackInfo);
         }
-    };
+    }
+
+    /**
+     * Holder for any hotspot state info that needs to passed from the callback to
+     * {@link #handleUpdateState(State, Object)}.
+     */
+    protected static final class CallbackInfo {
+        boolean enabled;
+        int numConnectedDevices;
+
+        @Override
+        public String toString() {
+            return new StringBuilder("CallbackInfo[")
+                    .append("enabled=").append(enabled)
+                    .append(",numConnectedDevices=").append(numConnectedDevices)
+                    .append(']').toString();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 675aa8f..0132fa8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -185,7 +185,7 @@
 
         // The public notification will show similar info but with the actual screenshot omitted
         mPublicNotificationBuilder =
-                new Notification.Builder(context, NotificationChannels.SCREENSHOTS)
+                new Notification.Builder(context, NotificationChannels.SCREENSHOTS_HEADSUP)
                         .setContentTitle(r.getString(R.string.screenshot_saving_title))
                         .setContentText(r.getString(R.string.screenshot_saving_text))
                         .setSmallIcon(R.drawable.stat_notify_image)
@@ -196,7 +196,8 @@
                                 com.android.internal.R.color.system_notification_accent_color));
         SystemUI.overrideNotificationAppName(context, mPublicNotificationBuilder);
 
-        mNotificationBuilder = new Notification.Builder(context, NotificationChannels.SCREENSHOTS)
+        mNotificationBuilder = new Notification.Builder(context,
+                NotificationChannels.SCREENSHOTS_HEADSUP)
             .setTicker(r.getString(R.string.screenshot_saving_ticker)
                     + (mTickerAddSpace ? " " : ""))
             .setContentTitle(r.getString(R.string.screenshot_saving_title))
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 172c62a..3ec8913 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -53,7 +53,7 @@
         mParent = containerStub.inflate();
         mContainer = mParent.findViewById(R.id.container);
         mUserGridView = mContainer.findViewById(R.id.user_grid);
-        mUserGridView.init(statusBar, mUserSwitcherController, true /* showInitially */);
+        mUserGridView.init(statusBar, mUserSwitcherController, true /* overrideAlpha */);
         mUserGridView.setUserSelectionListener(record -> {
             if (!record.isCurrent) {
                 toggleSwitchInProgress(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
index e551801..1bd820d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridView.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.statusbar.car;
 
-import android.animation.Animator;
-import android.animation.Animator.AnimatorListener;
-import android.animation.ValueAnimator;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -29,62 +26,110 @@
 import android.graphics.drawable.GradientDrawable;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
-import android.support.v4.view.animation.FastOutSlowInInterpolator;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Vector;
+
 /**
  * Displays a ViewPager with icons for the users in the system to allow switching between users.
  * One of the uses of this is for the lock screen in auto.
  */
-public class UserGridView extends ViewPager {
-    private static final int EXPAND_ANIMATION_TIME_MS = 200;
-    private static final int HIDE_ANIMATION_TIME_MS = 133;
-
+public class UserGridView extends ViewPager implements
+        UserInfoController.OnUserInfoChangedListener {
     private StatusBar mStatusBar;
     private UserSwitcherController mUserSwitcherController;
     private Adapter mAdapter;
     private UserSelectionListener mUserSelectionListener;
-    private ValueAnimator mHeightAnimator;
-    private int mTargetHeight;
-    private int mHeightChildren;
-    private boolean mShowing;
+    private UserInfoController mUserInfoController;
+    private Vector mUserContainers;
+    private int mContainerWidth;
+    private boolean mOverrideAlpha;
+    private CarQSFragment.UserSwitchCallback mUserSwitchCallback;
 
     public UserGridView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
     public void init(StatusBar statusBar, UserSwitcherController userSwitcherController,
-            boolean showInitially) {
+            boolean overrideAlpha) {
         mStatusBar = statusBar;
         mUserSwitcherController = userSwitcherController;
         mAdapter = new Adapter(mUserSwitcherController);
-        addOnLayoutChangeListener(mAdapter);
+        mUserInfoController = Dependency.get(UserInfoController.class);
+        mOverrideAlpha = overrideAlpha;
+        // Whenever the container width changes, the containers must be refreshed. Instead of
+        // doing an initial refreshContainers() to populate the containers, this listener will
+        // refresh them on layout change because that affects how the users are split into
+        // containers. Furthermore, at this point, the container width is unknown, so
+        // refreshContainers() cannot populate any containers.
+        addOnLayoutChangeListener(
+                (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                    int newWidth = Math.max(left - right, right - left);
+                    if (mContainerWidth != newWidth) {
+                        mContainerWidth = newWidth;
+                        refreshContainers();
+                    }
+                });
+    }
+
+    private void refreshContainers() {
+        mUserContainers = new Vector();
+
+        Context context = getContext();
+        LayoutInflater inflater = LayoutInflater.from(context);
+
+        for (int i = 0; i < mAdapter.getCount(); i++) {
+            ViewGroup pods = (ViewGroup) inflater.inflate(
+                    R.layout.car_fullscreen_user_pod_container, null);
+
+            int iconsPerPage = mAdapter.getIconsPerPage();
+            int limit = Math.min(mUserSwitcherController.getUsers().size(), (i + 1) * iconsPerPage);
+            for (int j = i * iconsPerPage; j < limit; j++) {
+                View v = mAdapter.makeUserPod(inflater, context, j, pods);
+                if (mOverrideAlpha) {
+                    v.setAlpha(1f);
+                }
+                pods.addView(v);
+                // This is hacky, but the dividers on the pod container LinearLayout don't seem
+                // to work for whatever reason.  Instead, set a right margin on the pod if it's not
+                // the right-most pod and there is more than one pod in the container.
+                if (i < limit - 1 && limit > 1) {
+                    ViewGroup.MarginLayoutParams params =
+                            (ViewGroup.MarginLayoutParams) v.getLayoutParams();
+                    params.setMargins(0, 0, getResources().getDimensionPixelSize(
+                            R.dimen.car_fullscreen_user_pod_margin_between), 0);
+                    v.setLayoutParams(params);
+                }
+            }
+            mUserContainers.add(pods);
+        }
+
+        mAdapter = new Adapter(mUserSwitcherController);
         setAdapter(mAdapter);
-        mShowing = showInitially;
     }
 
-    public boolean isShowing() {
-        return mShowing;
+    @Override
+    public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
+        refreshContainers();
     }
 
-    public void show() {
-        mShowing = true;
-        animateHeightChange(getMeasuredHeight(), mHeightChildren);
-    }
-
-    public void hide() {
-        mShowing = false;
-        animateHeightChange(getMeasuredHeight(), 0);
+    public void setUserSwitchCallback(CarQSFragment.UserSwitchCallback callback) {
+        mUserSwitchCallback = callback;
     }
 
     public void onUserSwitched(int newUserId) {
@@ -96,6 +141,14 @@
         mUserSelectionListener = userSelectionListener;
     }
 
+    public void setListening(boolean listening) {
+        if (listening) {
+            mUserInfoController.addCallback(this);
+        } else {
+            mUserInfoController.removeCallback(this);
+        }
+    }
+
     void showOfflineAuthUi() {
         // TODO: Show keyguard UI in-place.
         mStatusBar.executeRunnableDismissingKeyguard(null, null, true, true, true);
@@ -115,13 +168,6 @@
                 height = Math.max(child.getMeasuredHeight(), height);
             }
 
-            mHeightChildren = height;
-
-            // Override the height if it's not showing.
-            if (!mShowing) {
-                height = 0;
-            }
-
             // Respect the AT_MOST request from parent.
             if (MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST) {
                 height = Math.min(MeasureSpec.getSize(heightMeasureSpec), height);
@@ -132,72 +178,19 @@
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
-    private void animateHeightChange(int oldHeight, int newHeight) {
-        // If there is no change in height or an animation is already in progress towards the
-        // desired height, then there's no need to make any changes.
-        if (oldHeight == newHeight || newHeight == mTargetHeight) {
-            return;
-        }
-
-        // Animation in progress is not going towards the new target, so cancel it.
-        if (mHeightAnimator != null){
-            mHeightAnimator.cancel();
-        }
-
-        mTargetHeight = newHeight;
-        mHeightAnimator = ValueAnimator.ofInt(oldHeight, mTargetHeight);
-        mHeightAnimator.addUpdateListener(valueAnimator -> {
-            ViewGroup.LayoutParams layoutParams = getLayoutParams();
-            layoutParams.height = (Integer) valueAnimator.getAnimatedValue();
-            requestLayout();
-        });
-        mHeightAnimator.addListener(new AnimatorListener() {
-            @Override
-            public void onAnimationStart(Animator animator) {}
-
-            @Override
-            public void onAnimationEnd(Animator animator) {
-                // ValueAnimator does not guarantee that the update listener will get an update
-                // to the final value, so here, the final value is set.  Though the final calculated
-                // height (mTargetHeight) could be set, WRAP_CONTENT is more appropriate.
-                ViewGroup.LayoutParams layoutParams = getLayoutParams();
-                layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-                requestLayout();
-                mHeightAnimator = null;
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animator) {}
-
-            @Override
-            public void onAnimationRepeat(Animator animator) {}
-        });
-
-        mHeightAnimator.setInterpolator(new FastOutSlowInInterpolator());
-        if (oldHeight < newHeight) {
-            // Expanding
-            mHeightAnimator.setDuration(EXPAND_ANIMATION_TIME_MS);
-        } else {
-            // Hiding
-            mHeightAnimator.setDuration(HIDE_ANIMATION_TIME_MS);
-        }
-        mHeightAnimator.start();
-    }
-
     /**
      * This is a ViewPager.PagerAdapter which deletegates the work to a
      * UserSwitcherController.BaseUserAdapter. Java doesn't support multiple inheritance so we have
      * to use composition instead to achieve the same goal since both the base classes are abstract
      * classes and not interfaces.
      */
-    private final class Adapter extends PagerAdapter implements View.OnLayoutChangeListener {
+    private final class Adapter extends PagerAdapter {
         private final int mPodWidth;
         private final int mPodMarginBetween;
         private final int mPodImageAvatarWidth;
         private final int mPodImageAvatarHeight;
 
         private final WrappedBaseUserAdapter mUserAdapter;
-        private int mContainerWidth;
 
         public Adapter(UserSwitcherController controller) {
             super();
@@ -229,30 +222,20 @@
         }
 
         @Override
-        public Object instantiateItem(ViewGroup container, int position) {
-            Context context = getContext();
-            LayoutInflater inflater = LayoutInflater.from(context);
-
-            ViewGroup pods = (ViewGroup) inflater.inflate(
-                    R.layout.car_fullscreen_user_pod_container, null);
-
-            int iconsPerPage = getIconsPerPage();
-            int limit = Math.min(mUserAdapter.getCount(), (position + 1) * iconsPerPage);
-            for (int i = position * iconsPerPage; i < limit; i++) {
-                View v = makeUserPod(inflater, context, i, pods);
-                pods.addView(v);
-                // This is hacky, but the dividers on the pod container LinearLayout don't seem
-                // to work for whatever reason.  Instead, set a right margin on the pod if it's not
-                // the right-most pod and there is more than one pod in the container.
-                if (i < limit - 1 && limit > 1) {
-                    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
-                            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-                    params.setMargins(0, 0, mPodMarginBetween, 0);
-                    v.setLayoutParams(params);
-                }
+        public void finishUpdate(ViewGroup container) {
+            if (mUserSwitchCallback != null) {
+                mUserSwitchCallback.resetShowing();
             }
-            container.addView(pods);
-            return pods;
+        }
+
+        @Override
+        public Object instantiateItem(ViewGroup container, int position) {
+            if (position < mUserContainers.size()) {
+                container.addView((View) mUserContainers.get(position));
+                return mUserContainers.get(position);
+            } else {
+                return null;
+            }
         }
 
         /**
@@ -353,17 +336,10 @@
         public boolean isViewFromObject(View view, Object object) {
             return view == object;
         }
-
-        @Override
-        public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                int oldLeft, int oldTop, int oldRight, int oldBottom) {
-            mContainerWidth = Math.max(left - right, right - left);
-            notifyDataSetChanged();
-        }
     }
 
     private final class WrappedBaseUserAdapter extends UserSwitcherController.BaseUserAdapter {
-        private Adapter mContainer;
+        private final Adapter mContainer;
 
         public WrappedBaseUserAdapter(UserSwitcherController controller, Adapter container) {
             super(controller);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 149ec0b..36f9f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -127,7 +127,7 @@
 
     private final HotspotController.Callback mHotspotCallback = new Callback() {
         @Override
-        public void onHotspotChanged(boolean enabled) {
+        public void onHotspotChanged(boolean enabled, int numDevices) {
             if (mAutoTracker.isAdded(HOTSPOT)) return;
             if (enabled) {
                 mHost.addTile(HOTSPOT);
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 0cc7f5d..66cb59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -72,6 +72,7 @@
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
+import java.util.Collection;
 import java.util.List;
 
 public class NotificationPanelView extends PanelView implements
@@ -2621,8 +2622,10 @@
         }
     }
 
-    public void setPulsing(boolean pulsing) {
-        mKeyguardStatusView.setPulsing(pulsing);
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+        mKeyguardStatusView.setPulsing(pulsing != null);
+        mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
+                + mKeyguardStatusView.getClockBottom());
     }
 
     public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 6857337..20b5018 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -665,7 +665,7 @@
 
     private final HotspotController.Callback mHotspotCallback = new HotspotController.Callback() {
         @Override
-        public void onHotspotChanged(boolean enabled) {
+        public void onHotspotChanged(boolean enabled, int numDevices) {
             mIconController.setIconVisibility(mSlotHotspot, enabled);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
index 6fc5bbd..ee1d088 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickScrubController.java
@@ -28,6 +28,7 @@
 import android.graphics.Rect;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
@@ -131,7 +132,7 @@
         new GestureDetector.SimpleOnGestureListener() {
             @Override
             public boolean onFling(MotionEvent e1, MotionEvent e2, float velX, float velY) {
-                if (mQuickScrubActive) {
+                if (!isQuickScrubEnabled() || mQuickScrubActive) {
                     return false;
                 }
                 float velocityX = mIsRTL ? -velX : velX;
@@ -196,12 +197,13 @@
             case MotionEvent.ACTION_DOWN: {
                 int x = (int) event.getX();
                 int y = (int) event.getY();
-                if (mHomeButtonRect.contains(x, y)) {
+                if (isQuickScrubEnabled() && mHomeButtonRect.contains(x, y)) {
                     mTouchDownX = x;
                     mTouchDownY = y;
                     homeButton.setDelayTouchFeedback(true);
                     mHandler.postDelayed(mLongPressRunnable, LONG_PRESS_DELAY_MS);
                 } else {
+                    homeButton.setDelayTouchFeedback(false);
                     mTouchDownX = mTouchDownY = -1;
                 }
                 break;
@@ -356,6 +358,10 @@
         }
     }
 
+    boolean isQuickScrubEnabled() {
+        return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", false);
+    }
+
     private void startQuickScrub() {
         if (!mQuickScrubActive) {
             mQuickScrubActive = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c30fb22..3b783c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4620,8 +4620,7 @@
                 }
 
                 private void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
-                    mStackScroller.setPulsing(pulsing);
-                    mNotificationPanel.setPulsing(pulsing != null);
+                    mNotificationPanel.setPulsing(pulsing);
                     mVisualStabilityManager.setPulsing(pulsing != null);
                     mIgnoreTouchWhilePulsing = false;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
index 6457209..830b50e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotController.java
@@ -26,7 +26,9 @@
     void setHotspotEnabled(boolean enabled);
     boolean isHotspotSupported();
 
-    public interface Callback {
-        void onHotspotChanged(boolean enabled);
+    int getNumConnectedDevices();
+
+    interface Callback {
+        void onHotspotChanged(boolean enabled, int numDevices);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 1ebb986..8792b4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -23,31 +23,35 @@
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
-import android.os.Handler;
 import android.os.UserManager;
 import android.util.Log;
 
+import com.android.systemui.Dependency;
+
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-public class HotspotControllerImpl implements HotspotController {
+public class HotspotControllerImpl implements HotspotController, WifiManager.SoftApCallback {
 
     private static final String TAG = "HotspotController";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>();
-    private final Receiver mReceiver = new Receiver();
+    private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final WifiStateReceiver mWifiStateReceiver = new WifiStateReceiver();
     private final ConnectivityManager mConnectivityManager;
+    private final WifiManager mWifiManager;
     private final Context mContext;
 
     private int mHotspotState;
+    private int mNumConnectedDevices;
     private boolean mWaitingForCallback;
 
     public HotspotControllerImpl(Context context) {
         mContext = context;
-        mConnectivityManager = (ConnectivityManager) context.getSystemService(
-                Context.CONNECTIVITY_SERVICE);
+        mConnectivityManager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
     }
 
     @Override
@@ -84,7 +88,8 @@
             if (callback == null || mCallbacks.contains(callback)) return;
             if (DEBUG) Log.d(TAG, "addCallback " + callback);
             mCallbacks.add(callback);
-            mReceiver.setListening(!mCallbacks.isEmpty());
+
+            updateWifiStateListeners(!mCallbacks.isEmpty());
         }
     }
 
@@ -94,7 +99,26 @@
         if (DEBUG) Log.d(TAG, "removeCallback " + callback);
         synchronized (mCallbacks) {
             mCallbacks.remove(callback);
-            mReceiver.setListening(!mCallbacks.isEmpty());
+
+            updateWifiStateListeners(!mCallbacks.isEmpty());
+        }
+    }
+
+    /**
+     * Updates the wifi state receiver to either start or stop listening to get updates to the
+     * hotspot status. Additionally starts listening to wifi manager state to track the number of
+     * connected devices.
+     *
+     * @param shouldListen whether we should start listening to various wifi statuses
+     */
+    private void updateWifiStateListeners(boolean shouldListen) {
+        mWifiStateReceiver.setListening(shouldListen);
+        if (shouldListen) {
+            mWifiManager.registerSoftApCallback(
+                    this,
+                    Dependency.get(Dependency.MAIN_HANDLER));
+        } else {
+            mWifiManager.unregisterSoftApCallback(this);
         }
     }
 
@@ -116,20 +140,55 @@
             if (DEBUG) Log.d(TAG, "Starting tethering");
             mConnectivityManager.startTethering(
                     ConnectivityManager.TETHERING_WIFI, false, callback);
-            fireCallback(isHotspotEnabled());
+            fireHotspotChangedCallback(isHotspotEnabled());
         } else {
             mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI);
         }
     }
 
-    private void fireCallback(boolean isEnabled) {
+    @Override
+    public int getNumConnectedDevices() {
+        return mNumConnectedDevices;
+    }
+
+    /**
+     * Sends a hotspot changed callback with the new enabled status. Wraps
+     * {@link #fireHotspotChangedCallback(boolean, int)} and assumes that the number of devices has
+     * not changed.
+     *
+     * @param enabled whether the hotspot is enabled
+     */
+    private void fireHotspotChangedCallback(boolean enabled) {
+        fireHotspotChangedCallback(enabled, mNumConnectedDevices);
+    }
+
+    /**
+     * Sends a hotspot changed callback with the new enabled status & the number of devices
+     * connected to the hotspot. Be careful when calling over multiple threads, especially if one of
+     * them is the main thread (as it can be blocked).
+     *
+     * @param enabled whether the hotspot is enabled
+     * @param numConnectedDevices number of devices connected to the hotspot
+     */
+    private void fireHotspotChangedCallback(boolean enabled, int numConnectedDevices) {
         synchronized (mCallbacks) {
             for (Callback callback : mCallbacks) {
-                callback.onHotspotChanged(isEnabled);
+                callback.onHotspotChanged(enabled, numConnectedDevices);
             }
         }
     }
 
+    @Override
+    public void onStateChanged(int state, int failureReason) {
+        // Do nothing - we don't care about changing anything here.
+    }
+
+    @Override
+    public void onNumClientsChanged(int numConnectedDevices) {
+        mNumConnectedDevices = numConnectedDevices;
+        fireHotspotChangedCallback(isHotspotEnabled(), numConnectedDevices);
+    }
+
     private final class OnStartTetheringCallback extends
             ConnectivityManager.OnStartTetheringCallback {
         @Override
@@ -143,12 +202,15 @@
         public void onTetheringFailed() {
             if (DEBUG) Log.d(TAG, "onTetheringFailed");
             mWaitingForCallback = false;
-            fireCallback(isHotspotEnabled());
+            fireHotspotChangedCallback(isHotspotEnabled());
           // TODO: Show error.
         }
     }
 
-    private final class Receiver extends BroadcastReceiver {
+    /**
+     * Class to listen in on wifi state and update the hotspot state
+     */
+    private final class WifiStateReceiver extends BroadcastReceiver {
         private boolean mRegistered;
 
         public void setListening(boolean listening) {
@@ -170,8 +232,17 @@
             int state = intent.getIntExtra(
                     WifiManager.EXTRA_WIFI_AP_STATE, WifiManager.WIFI_AP_STATE_FAILED);
             if (DEBUG) Log.d(TAG, "onReceive " + state);
+
+            // Update internal hotspot state for tracking before using any enabled/callback methods.
             mHotspotState = state;
-            fireCallback(mHotspotState == WifiManager.WIFI_AP_STATE_ENABLED);
+
+            if (!isHotspotEnabled()) {
+                // Reset num devices if the hotspot is no longer enabled so we don't get ghost
+                // counters.
+                mNumConnectedDevices = 0;
+            }
+
+            fireHotspotChangedCallback(isHotspotEnabled());
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 4ee4ef4..0b666a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -16,11 +16,12 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static com.android.settingslib.Utils.updateLocationEnabled;
+
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -28,19 +29,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
-
-import com.android.systemui.R;
 import com.android.systemui.util.Utils;
-
 import java.util.ArrayList;
 import java.util.List;
 
-import static com.android.settingslib.Utils.updateLocationMode;
-
 /**
  * A controller to manage changes of location related states and update the views accordingly.
  */
@@ -101,32 +97,27 @@
      * @return true if attempt to change setting was successful.
      */
     public boolean setLocationEnabled(boolean enabled) {
+        // QuickSettings always runs as the owner, so specifically set the settings
+        // for the current foreground user.
         int currentUserId = ActivityManager.getCurrentUser();
         if (isUserLocationRestricted(currentUserId)) {
             return false;
         }
-        final ContentResolver cr = mContext.getContentResolver();
         // When enabling location, a user consent dialog will pop up, and the
         // setting won't be fully enabled until the user accepts the agreement.
-        int currentMode = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCATION_MODE, 
-                Settings.Secure.LOCATION_MODE_OFF, currentUserId);
-        int mode = enabled
-                ? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
-        // QuickSettings always runs as the owner, so specifically set the settings
-        // for the current foreground user.
-        return updateLocationMode(mContext, currentMode, mode, currentUserId);
+        updateLocationEnabled(mContext, enabled, currentUserId);
+        return true;
     }
 
     /**
-     * Returns true if location isn't disabled in settings.
+     * Returns true if location is enabled in settings.
      */
     public boolean isLocationEnabled() {
-        ContentResolver resolver = mContext.getContentResolver();
         // QuickSettings always runs as the owner, so specifically retrieve the settings
         // for the current foreground user.
-        int mode = Settings.Secure.getIntForUser(resolver, Settings.Secure.LOCATION_MODE,
-                Settings.Secure.LOCATION_MODE_OFF, ActivityManager.getCurrentUser());
-        return mode != Settings.Secure.LOCATION_MODE_OFF;
+        LocationManager locationManager =
+                (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+        return locationManager.isLocationEnabledForUser(Process.myUserHandle());
     }
 
     @Override
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 443e760..af3d64b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -399,6 +399,7 @@
     private final int mSeparatorWidth;
     private final int mSeparatorThickness;
     private final Rect mTmpRect = new Rect();
+    private int mClockBottom;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -510,7 +511,9 @@
         final int darkTop = (int) (mRegularTopPadding + mSeparatorThickness / 2f);
         final int darkBottom = darkTop + mSeparatorThickness;
 
-        if (mAmbientState.isDark()) {
+        if (mAmbientState.hasPulsingNotifications()) {
+            // TODO draw divider between notification and shelf
+        } else if (mAmbientState.isDark()) {
             // Only draw divider on AOD if we actually have notifications
             if (mFirstVisibleBackgroundChild != null) {
                 canvas.drawRect(darkLeft, darkTop, darkRight, darkBottom, mBackgroundPaint);
@@ -684,7 +687,11 @@
     }
 
     private void updateAlgorithmHeightAndPadding() {
-        mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
+        if (mPulsing != null) {
+            mTopPadding = mClockBottom;
+        } else {
+            mTopPadding = mAmbientState.isDark() ? mDarkTopPadding : mRegularTopPadding;
+        }
         mAmbientState.setLayoutHeight(getLayoutHeight());
         updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
@@ -4322,13 +4329,15 @@
         return mIsExpanded;
     }
 
-    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing) {
+    public void setPulsing(Collection<HeadsUpManager.HeadsUpEntry> pulsing, int clockBottom) {
         if (mPulsing == null && pulsing == null) {
             return;
         }
         mPulsing = pulsing;
+        mClockBottom = clockBottom;
         mAmbientState.setPulsing(pulsing);
         updateNotificationAnimationStates();
+        updateAlgorithmHeightAndPadding();
         updateContentHeight();
         notifyHeightChangeListener(mShelf);
         requestChildrenUpdate();
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index 87bc0e6..14d5c6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -31,7 +31,8 @@
 
 public class NotificationChannels extends SystemUI {
     public static String ALERTS      = "ALR";
-    public static String SCREENSHOTS = "SCN";
+    public static String SCREENSHOTS_LEGACY = "SCN";
+    public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP";
     public static String GENERAL     = "GEN";
     public static String STORAGE     = "DSK";
     public static String TVPIP       = "TPP";
@@ -56,10 +57,6 @@
                         context.getString(R.string.notification_channel_alerts),
                         NotificationManager.IMPORTANCE_HIGH),
                 new NotificationChannel(
-                        SCREENSHOTS,
-                        context.getString(R.string.notification_channel_screenshot),
-                        NotificationManager.IMPORTANCE_LOW),
-                new NotificationChannel(
                         GENERAL,
                         context.getString(R.string.notification_channel_general),
                         NotificationManager.IMPORTANCE_MIN),
@@ -69,9 +66,18 @@
                         isTv(context)
                                 ? NotificationManager.IMPORTANCE_DEFAULT
                                 : NotificationManager.IMPORTANCE_LOW),
+                createScreenshotChannel(
+                        context.getString(R.string.notification_channel_screenshot),
+                        nm.getNotificationChannel(SCREENSHOTS_LEGACY)),
                 batteryChannel
         ));
 
+        // Delete older SS channel if present.
+        // Screenshots promoted to heads-up in P, this cleans up the lower priority channel from O.
+        // This line can be deleted in Q.
+        nm.deleteNotificationChannel(SCREENSHOTS_LEGACY);
+
+
         if (isTv(context)) {
             // TV specific notification channel for TV PIP controls.
             // Importance should be {@link NotificationManager#IMPORTANCE_MAX} to have the highest
@@ -83,6 +89,40 @@
         }
     }
 
+    /**
+     * Set up screenshot channel, respecting any previously committed user settings on legacy
+     * channel.
+     * @return
+     */
+    @VisibleForTesting static NotificationChannel createScreenshotChannel(
+            String name, NotificationChannel legacySS) {
+        NotificationChannel screenshotChannel = new NotificationChannel(SCREENSHOTS_HEADSUP,
+                name, NotificationManager.IMPORTANCE_HIGH); // pop on screen
+
+        screenshotChannel.setSound(Uri.parse(""), // silent
+                new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build());
+
+        if (legacySS != null) {
+            // Respect any user modified fields from the old channel.
+            int userlock = legacySS.getUserLockedFields();
+            if ((userlock & NotificationChannel.USER_LOCKED_IMPORTANCE) != 0) {
+                screenshotChannel.setImportance(legacySS.getImportance());
+            }
+            if ((userlock & NotificationChannel.USER_LOCKED_SOUND) != 0)  {
+                screenshotChannel.setSound(legacySS.getSound(), legacySS.getAudioAttributes());
+            }
+            if ((userlock & NotificationChannel.USER_LOCKED_VIBRATION) != 0)  {
+                screenshotChannel.setVibrationPattern(legacySS.getVibrationPattern());
+            }
+            if ((userlock & NotificationChannel.USER_LOCKED_LIGHTS) != 0)  {
+                screenshotChannel.setLightColor(legacySS.getLightColor());
+            }
+            // skip show_badge, irrelevant for system channel
+        }
+
+        return screenshotChannel;
+    }
+
     @Override
     public void start() {
         createAll(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index e76bf57..385438c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -368,16 +368,16 @@
                 mController.setActiveStream(row.stream);
                 if (row.stream == AudioManager.STREAM_RING) {
                     final boolean hasVibrator = mController.hasVibrator();
-                    if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) {
+                    if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                         if (hasVibrator) {
-                            mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true);
+                            mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
                         } else {
                             final boolean wasZero = row.ss.level == 0;
                             mController.setStreamVolume(stream,
                                     wasZero ? row.lastAudibleLevel : 0);
                         }
                     } else {
-                        mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true);
+                        mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
                         if (row.ss.level == 0) {
                             mController.setStreamVolume(stream, 1);
                         }
@@ -403,15 +403,15 @@
                 return;
             }
             final boolean hasVibrator = mController.hasVibrator();
-            if (mState.ringerModeExternal == AudioManager.RINGER_MODE_NORMAL) {
+            if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                 if (hasVibrator) {
-                    mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, true);
+                    mController.setRingerMode(AudioManager.RINGER_MODE_VIBRATE, false);
                 } else {
                     final boolean wasZero = ss.level == 0;
                     mController.setStreamVolume(AudioManager.STREAM_RING, wasZero ? 1 : 0);
                 }
             } else {
-                mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, true);
+                mController.setRingerMode(AudioManager.RINGER_MODE_NORMAL, false);
                 if (ss.level == 0) {
                     mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                 }
@@ -552,7 +552,7 @@
             if (ss == null) {
                 return;
             }
-            switch (mState.ringerModeExternal) {
+            switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerStatus.setText(R.string.volume_ringer_status_vibrate);
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
@@ -653,9 +653,9 @@
         final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
         final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
         final boolean isRingVibrate = isRingStream
-                && mState.ringerModeExternal == AudioManager.RINGER_MODE_VIBRATE;
+                && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
         final boolean isRingSilent = isRingStream
-                && mState.ringerModeExternal == AudioManager.RINGER_MODE_SILENT;
+                && mState.ringerModeInternal == AudioManager.RINGER_MODE_SILENT;
         final boolean isZenAlarms = mState.zenMode == Global.ZEN_MODE_ALARMS;
         final boolean isZenNone = mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
         final boolean zenMuted = isZenAlarms ? (isRingStream || isSystemStream)
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 859dc2f..f5e079c 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -47,6 +47,7 @@
     <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
     <uses-permission android:name="android.permission.REAL_GET_TASKS" />
     <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" />
+    <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 04bdc04..80dc2c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -26,11 +26,14 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.content.Context;
+import android.net.Uri;
+import android.provider.Settings;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.util.ArraySet;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.NotificationChannels;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -54,7 +57,7 @@
     public void testChannelSetup() {
         Set<String> ALL_CHANNELS = new ArraySet<>(Arrays.asList(
                 NotificationChannels.ALERTS,
-                NotificationChannels.SCREENSHOTS,
+                NotificationChannels.SCREENSHOTS_HEADSUP,
                 NotificationChannels.STORAGE,
                 NotificationChannels.GENERAL,
                 NotificationChannels.BATTERY
@@ -66,4 +69,52 @@
         assertEquals(ALL_CHANNELS.size(), list.size());
         list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId())));
     }
+
+    @Test
+    public void testChannelSetup_noLegacyScreenshot() {
+        // Assert old channel cleaned up.
+        // TODO: remove that code + this test after P.
+        NotificationChannels.createAll(mContext);
+        ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
+        verify(mMockNotificationManager).deleteNotificationChannel(
+                NotificationChannels.SCREENSHOTS_LEGACY);
+    }
+
+    @Test
+    public void testInheritFromLegacy_keepsUserLockedLegacySettings() {
+        NotificationChannel legacyChannel = new NotificationChannel("id", "oldName",
+                NotificationManager.IMPORTANCE_MIN);
+        legacyChannel.setImportance(NotificationManager.IMPORTANCE_NONE);;
+        legacyChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+                legacyChannel.getAudioAttributes());
+        legacyChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE |
+                NotificationChannel.USER_LOCKED_SOUND);
+        NotificationChannel newChannel =
+                NotificationChannels.createScreenshotChannel("newName", legacyChannel);
+        // NONE importance user locked, so don't use HIGH for new channel.
+        assertEquals(NotificationManager.IMPORTANCE_NONE, newChannel.getImportance());
+        assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, newChannel.getSound());
+    }
+
+    @Test
+    public void testInheritFromLegacy_dropsUnlockedLegacySettings() {
+        NotificationChannel legacyChannel = new NotificationChannel("id", "oldName",
+                NotificationManager.IMPORTANCE_MIN);
+        NotificationChannel newChannel =
+                NotificationChannels.createScreenshotChannel("newName", legacyChannel);
+        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals("newName", newChannel.getName());
+        // MIN importance not user locked, so HIGH wins out.
+        assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
+    }
+
+    @Test
+    public void testInheritFromLegacy_noLegacyExists() {
+        NotificationChannel newChannel =
+                NotificationChannels.createScreenshotChannel("newName", null);
+        assertEquals(Uri.EMPTY, newChannel.getSound());
+        assertEquals("newName", newChannel.getName());
+        assertEquals(NotificationManager.IMPORTANCE_HIGH, newChannel.getImportance());
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
index 5491147..016160a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeHotspotController.java
@@ -44,4 +44,9 @@
     public boolean isHotspotSupported() {
         return false;
     }
+
+    @Override
+    public int getNumConnectedDevices() {
+        return 0;
+    }
 }
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
similarity index 65%
copy from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
copy to packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
index f4205ad6..4f3a8b1 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationNarrow
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationNarrowOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
similarity index 64%
rename from packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
index dd43690..71ce6b4 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/AndroidManifest.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/AndroidManifest.xml
@@ -1,7 +1,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.internal.display.cutout.emulation"
-    android:versionCode="1"
-    android:versionName="1.0">
+        package="com.android.internal.display.cutout.emulation.narrow"
+        android:versionCode="1"
+        android:versionName="1.0">
     <overlay android:targetPackage="android" android:priority="1"/>
 
     <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
similarity index 95%
rename from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
index 30e8b68..43bde88 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml
@@ -41,7 +41,7 @@
     </string>
 
     <!-- Whether the display cutout region of the main built-in display should be forced to
-     black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
similarity index 89%
rename from packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
rename to packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
index 5d5c425..4989677 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/strings.xml
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/strings.xml
@@ -18,7 +18,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
 
-    <string name="display_cutout_emulation_overlay">Display Cutout Emulation</string>
+    <string name="display_cutout_emulation_overlay">Narrow display cutout</string>
 
 </resources>
 
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
similarity index 66%
copy from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
copy to packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
index f4205ad6..dac3878 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationTall
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationTallOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..5a93cfb
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.internal.display.cutout.emulation.tall"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android" android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
similarity index 84%
copy from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
copy to packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
index 30e8b68..9cf48d9 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml
@@ -30,18 +30,18 @@
          -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout">
         M 0,0
-        L -24, 0
-        L -21.9940446283, 20.0595537175
-        C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0
-        L 8.8, 32.0
-        C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175
-        L 24, 0
+        L -48, 0
+        L -44.3940446283, 36.0595537175
+        C -43.5582133885, 44.4178661152 -39.6, 48.0 -31.2, 48.0
+        L 31.2, 48.0
+        C 39.6, 48.0 43.5582133885, 44.4178661152 44.3940446283, 36.0595537175
+        L 48, 0
         Z
         @dp
     </string>
 
     <!-- Whether the display cutout region of the main built-in display should be forced to
-     black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
new file mode 100644
index 0000000..6dcbbd9
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Tall display cutout</string>
+
+</resources>
+
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
similarity index 66%
rename from packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
rename to packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
index f4205ad6..f4f250c 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/Android.mk
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/Android.mk
@@ -1,13 +1,13 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_RRO_THEME := DisplayCutoutEmulation
+LOCAL_RRO_THEME := DisplayCutoutEmulationWide
 LOCAL_CERTIFICATE := platform
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
 
-LOCAL_PACKAGE_NAME := DisplayCutoutEmulationOverlay
+LOCAL_PACKAGE_NAME := DisplayCutoutEmulationWideOverlay
 
 include $(BUILD_RRO_PACKAGE)
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml
new file mode 100644
index 0000000..96bd060
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright (C) 2018 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.internal.display.cutout.emulation.wide"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <overlay android:targetPackage="android" android:priority="1"/>
+
+    <application android:label="@string/display_cutout_emulation_overlay" android:hasCode="false"/>
+</manifest>
diff --git a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
similarity index 84%
copy from packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
copy to packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
index 30e8b68..1ce41f0 100644
--- a/packages/overlays/DisplayCutoutEmulationOverlay/res/values/config.xml
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml
@@ -30,18 +30,18 @@
          -->
     <string translatable="false" name="config_mainBuiltInDisplayCutout">
         M 0,0
-        L -24, 0
-        L -21.9940446283, 20.0595537175
-        C -21.1582133885, 28.4178661152 -17.2, 32.0 -8.8, 32.0
-        L 8.8, 32.0
-        C 17.2, 32.0 21.1582133885, 28.4178661152 21.9940446283, 20.0595537175
-        L 24, 0
+        L -72, 0
+        L -69.9940446283, 20.0595537175
+        C -69.1582133885, 28.4178661152 -65.2, 32.0 -56.8, 32.0
+        L 56.8, 32.0
+        C 65.2, 32.0 69.1582133885, 28.4178661152 69.9940446283, 20.0595537175
+        L 72, 0
         Z
         @dp
     </string>
 
     <!-- Whether the display cutout region of the main built-in display should be forced to
-     black in software (to avoid aliasing or emulate a cutout that is not physically existent).
+         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
      -->
     <bool name="config_fillMainBuiltInDisplayCutout">true</bool>
 
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
new file mode 100644
index 0000000..f4b9f7e
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="display_cutout_emulation_overlay">Wide display cutout</string>
+
+</resources>
+
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 9b67f8f..4144bbd 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5149,6 +5149,11 @@
     // OS: P
     FUELGAUGE_RESTRICTED_APP_DETAILS = 1285;
 
+    // OPEN: Settings > Sound & notification > Do Not Disturb > Turn on now
+    // CATEGORY: SETTINGS
+    // OS: P
+    NOTIFICATION_ZEN_MODE_ENABLE_DIALOG = 1286;
+
     // ---- End P Constants, all P constants go above this line ----
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 0a03b7f..0bc95f4 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -37,7 +37,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
 import android.util.TypedValue;
@@ -154,6 +156,12 @@
             MagnificationController magnificationController,
             boolean detectTripleTap,
             boolean detectShortcutTrigger) {
+        if (DEBUG_ALL) {
+            Log.i(LOG_TAG,
+                    "MagnificationGestureHandler(detectTripleTap = " + detectTripleTap
+                            + ", detectShortcutTrigger = " + detectShortcutTrigger + ")");
+        }
+
         mMagnificationController = magnificationController;
 
         mDelegatingState = new DelegatingState();
@@ -581,7 +589,7 @@
 
         @VisibleForTesting boolean mShortcutTriggered;
 
-        Handler mHandler = new Handler(this);
+        @VisibleForTesting Handler mHandler = new Handler(this);
 
         public DetectingState(Context context) {
             mLongTapMinDelay = ViewConfiguration.getLongPressTimeout();
@@ -756,11 +764,14 @@
         @Override
         public void clear() {
             setShortcutTriggered(false);
-            mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
-            mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+            removePendingDelayedMessages();
             clearDelayedMotionEvents();
         }
 
+        private void removePendingDelayedMessages() {
+            mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+            mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
+        }
 
         private void cacheDelayedMotionEvent(MotionEvent event, MotionEvent rawEvent,
                 int policyFlags) {
@@ -811,7 +822,7 @@
         void transitionToDelegatingStateAndClear() {
             transitionTo(mDelegatingState);
             sendDelayedMotionEvents();
-            clear();
+            removePendingDelayedMessages();
         }
 
         private void onTripleTap(MotionEvent up) {
@@ -860,6 +871,7 @@
             if (mShortcutTriggered == state) {
                 return;
             }
+            if (DEBUG_DETECTING) Slog.i(LOG_TAG, "setShortcutTriggered(" + state + ")");
 
             mShortcutTriggered = state;
             mMagnificationController.setForceShowMagnifiableBounds(state);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 978ed253..0e2ca14 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -119,6 +119,7 @@
 
     private final LocalLog mRequestsHistory = new LocalLog(20);
     private final LocalLog mUiLatencyHistory = new LocalLog(20);
+    private final LocalLog mWtfHistory = new LocalLog(50);
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -310,7 +311,8 @@
         AutofillManagerServiceImpl service = mServicesCache.get(resolvedUserId);
         if (service == null) {
             service = new AutofillManagerServiceImpl(mContext, mLock, mRequestsHistory,
-                    mUiLatencyHistory, resolvedUserId, mUi, mDisabledUsers.get(resolvedUserId));
+                    mUiLatencyHistory, mWtfHistory, resolvedUserId, mUi,
+                    mDisabledUsers.get(resolvedUserId));
             mServicesCache.put(userId, service);
         }
         return service;
@@ -878,10 +880,12 @@
                     mUi.dump(pw);
                 }
                 if (showHistory) {
-                    pw.println("Requests history:");
+                    pw.println(); pw.println("Requests history:"); pw.println();
                     mRequestsHistory.reverseDump(fd, pw, args);
-                    pw.println("UI latency history:");
+                    pw.println(); pw.println("UI latency history:"); pw.println();
                     mUiLatencyHistory.reverseDump(fd, pw, args);
+                    pw.println(); pw.println("WTF history:"); pw.println();
+                    mWtfHistory.reverseDump(fd, pw, args);
                 }
             } finally {
                 setDebugLocked(oldDebug);
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 6bcfc4b..07b0b77 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -43,7 +43,6 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.RemoteCallback;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -116,6 +115,7 @@
 
     private final LocalLog mRequestsHistory;
     private final LocalLog mUiLatencyHistory;
+    private final LocalLog mWtfHistory;
     private final FieldClassificationStrategy mFieldClassificationStrategy;
 
     /**
@@ -179,11 +179,13 @@
     private long mLastPrune = 0;
 
     AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
-            LocalLog uiLatencyHistory, int userId, AutoFillUI ui, boolean disabled) {
+            LocalLog uiLatencyHistory, LocalLog wtfHistory, int userId, AutoFillUI ui,
+            boolean disabled) {
         mContext = context;
         mLock = lock;
         mRequestsHistory = requestsHistory;
         mUiLatencyHistory = uiLatencyHistory;
+        mWtfHistory = wtfHistory;
         mUserId = userId;
         mUi = ui;
         mFieldClassificationStrategy = new FieldClassificationStrategy(context, userId);
@@ -484,8 +486,8 @@
         assertCallerLocked(componentName);
 
         final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
-                sessionId, uid, activityToken, appCallbackToken, hasCallback,
-                mUiLatencyHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags);
+                sessionId, uid, activityToken, appCallbackToken, hasCallback, mUiLatencyHistory,
+                mWtfHistory, mInfo.getServiceInfo().getComponentName(), componentName, flags);
         mSessions.put(newSession.id, newSession);
 
         return newSession;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 63f8384..e1fb3a7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -210,6 +210,9 @@
     @GuardedBy("mLock")
     private final LocalLog mUiLatencyHistory;
 
+    @GuardedBy("mLock")
+    private final LocalLog mWtfHistory;
+
     /**
      * Receiver of assist data from the app's {@link Activity}.
      */
@@ -241,7 +244,13 @@
                 // ONE_WAY warning because system_service could block on app calls. We need to
                 // change AssistStructure so it provides a "one-way" writeToParcel() method that
                 // sends all the data
-                structure.ensureData();
+                try {
+                    structure.ensureData();
+                } catch (RuntimeException e) {
+                    wtf(e, "Exception lazy loading assist structure for %s: %s",
+                            structure.getActivityComponent(), e);
+                    return;
+                }
 
                 // Sanitize structure before it's sent to service.
                 final ComponentName componentNameFromApp = structure.getActivityComponent();
@@ -447,6 +456,7 @@
             @NonNull Context context, @NonNull HandlerCaller handlerCaller, int userId,
             @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
             @NonNull IBinder client, boolean hasCallback, @NonNull LocalLog uiLatencyHistory,
+            @NonNull LocalLog wtfHistory,
             @NonNull ComponentName serviceComponentName, @NonNull ComponentName componentName,
             int flags) {
         id = sessionId;
@@ -461,6 +471,7 @@
         mActivityToken = activityToken;
         mHasCallback = hasCallback;
         mUiLatencyHistory = uiLatencyHistory;
+        mWtfHistory = wtfHistory;
         mComponentName = componentName;
         mClient = IAutoFillManagerClient.Stub.asInterface(client);
 
@@ -1102,8 +1113,7 @@
         if (userData != null && fcStrategy != null) {
             logFieldClassificationScoreLocked(fcStrategy, ignoredDatasets, changedFieldIds,
                     changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds,
-                    manuallyFilledIds, userData,
-                    mViewStates.values());
+                    userData, mViewStates.values());
         } else {
             mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds,
                     ignoredDatasets, changedFieldIds, changedDatasetIds,
@@ -1123,7 +1133,6 @@
             @NonNull ArrayList<String> changedDatasetIds,
             @NonNull ArrayList<AutofillId> manuallyFilledFieldIds,
             @NonNull ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-            @NonNull ArrayMap<AutofillId, ArraySet<String>> manuallyFilledIds,
             @NonNull UserData userData, @NonNull Collection<ViewState> viewStates) {
 
         final String[] userValues = userData.getValues();
@@ -1201,8 +1210,7 @@
                     }
                 }
             } catch (ArrayIndexOutOfBoundsException e) {
-                Slog.wtf(TAG, "Error accessing FC score at " + i + " x " + j + ": "
-                        + Arrays.toString(scores.scores), e);
+                wtf(e, "Error accessing FC score at [%d, %d] (%s): %s", i, j, scores, e);
                 return;
             }
 
@@ -2151,9 +2159,10 @@
         final Intent fillInIntent = new Intent();
 
         final FillContext context = getFillContextByRequestIdLocked(requestId);
+
         if (context == null) {
-            Slog.wtf(TAG, "createAuthFillInIntentLocked(): no FillContext. requestId=" + requestId
-                    + "; mContexts= " + mContexts);
+            wtf(null, "createAuthFillInIntentLocked(): no FillContext. requestId=%d; mContexts=%s",
+                    requestId, mContexts);
             return null;
         }
         fillInIntent.putExtra(AutofillManager.EXTRA_ASSIST_STRUCTURE, context.getStructure());
@@ -2418,4 +2427,15 @@
     private void writeLog(int category) {
         mMetricsLogger.write(newLogMaker(category));
     }
+
+    private void wtf(@Nullable Exception e, String fmt, Object...args) {
+        final String message = String.format(fmt, args);
+        mWtfHistory.log(message);
+
+        if (e != null) {
+            Slog.wtf(TAG, message, e);
+        } else {
+            Slog.wtf(TAG, message);
+        }
+    }
 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 3369458..8da6d1e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -32,6 +32,7 @@
     static_libs: [
         "time_zone_distro",
         "time_zone_distro_installer",
+        "android.hardware.authsecret-V1.0-java",
         "android.hardware.broadcastradio-V2.0-java",
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index c1f4b78..5030dce 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -69,6 +69,7 @@
 import android.net.NetworkSpecifier;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
+import android.net.NetworkWatchlistManager;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
@@ -5708,6 +5709,17 @@
                 Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
     }
 
+    @Override
+    public byte[] getNetworkWatchlistConfigHash() {
+        NetworkWatchlistManager nwm = mContext.getSystemService(NetworkWatchlistManager.class);
+        if (nwm == null) {
+            loge("Unable to get NetworkWatchlistManager");
+            return null;
+        }
+        // Redirect it to network watchlist service to access watchlist file and calculate hash.
+        return nwm.getWatchlistConfigHash();
+    }
+
     @VisibleForTesting
     public NetworkMonitor createNetworkMonitor(Context context, Handler handler,
             NetworkAgentInfo nai, NetworkRequest defaultRequest) {
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index ef6bc43..24d493e 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -571,6 +571,8 @@
             mConfig = config;
             mSpi = spi;
             mSocket = socket;
+
+            spi.setOwnedByTransform();
         }
 
         public IpSecConfig getConfig() {
@@ -651,16 +653,6 @@
         /** always guarded by IpSecService#this */
         @Override
         public void freeUnderlyingResources() {
-            if (mOwnedByTransform) {
-                Log.d(TAG, "Cannot release Spi " + mSpi + ": Currently locked by a Transform");
-                // Because SPIs are "handed off" to transform, objects, they should never be
-                // freed from the SpiRecord once used in a transform. (They refer to the same SA,
-                // thus ownership and responsibility for freeing these resources passes to the
-                // Transform object). Thus, we should let the user free them without penalty once
-                // they are applied in a Transform object.
-                return;
-            }
-
             try {
                 mSrvConfig
                         .getNetdInstance()
@@ -694,6 +686,10 @@
             mOwnedByTransform = true;
         }
 
+        public boolean getOwnedByTransform() {
+            return mOwnedByTransform;
+        }
+
         @Override
         public void invalidate() throws RemoteException {
             getUserRecord().removeSpiRecord(mResourceId);
@@ -1107,6 +1103,11 @@
         // Retrieve SPI record; will throw IllegalArgumentException if not found
         SpiRecord s = userRecord.mSpiRecords.getResourceOrThrow(config.getSpiResourceId());
 
+        // Check to ensure that SPI has not already been used.
+        if (s.getOwnedByTransform()) {
+            throw new IllegalStateException("SPI already in use; cannot be used in new Transforms");
+        }
+
         // If no remote address is supplied, then use one from the SPI.
         if (TextUtils.isEmpty(config.getDestinationAddress())) {
             config.setDestinationAddress(s.getDestinationAddress());
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 6c63f43..5a4f7ca 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -16,11 +16,64 @@
 
 package com.android.server;
 
-import android.app.ActivityManager;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
 import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.location.Address;
+import android.location.Criteria;
+import android.location.GeocoderParams;
+import android.location.Geofence;
+import android.location.IBatchedLocationCallback;
+import android.location.IGnssMeasurementsListener;
+import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssStatusListener;
+import android.location.IGnssStatusProvider;
+import android.location.IGpsGeofenceHardware;
+import android.location.ILocationListener;
+import android.location.ILocationManager;
+import android.location.INetInitiatedListener;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.location.LocationRequest;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
-
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -45,60 +98,6 @@
 import com.android.server.location.LocationRequestStatistics.PackageStatistics;
 import com.android.server.location.MockProvider;
 import com.android.server.location.PassiveProvider;
-
-import android.app.AppOpsManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.hardware.location.ActivityRecognitionHardware;
-import android.location.Address;
-import android.location.Criteria;
-import android.location.GeocoderParams;
-import android.location.Geofence;
-import android.location.IBatchedLocationCallback;
-import android.location.IGnssMeasurementsListener;
-import android.location.IGnssStatusListener;
-import android.location.IGnssStatusProvider;
-import android.location.IGpsGeofenceHardware;
-import android.location.IGnssNavigationMessageListener;
-import android.location.ILocationListener;
-import android.location.ILocationManager;
-import android.location.INetInitiatedListener;
-import android.location.Location;
-import android.location.LocationManager;
-import android.location.LocationProvider;
-import android.location.LocationRequest;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Slog;
-
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -1378,10 +1377,7 @@
         if (mDisabledProviders.contains(provider)) {
             return false;
         }
-        // Use system settings
-        ContentResolver resolver = mContext.getContentResolver();
-
-        return Settings.Secure.isLocationProviderEnabledForUser(resolver, provider, mCurrentUserId);
+        return isLocationProviderEnabledForUser(provider, mCurrentUserId);
     }
 
     /**
@@ -1400,6 +1396,23 @@
     }
 
     /**
+     * Returns "true" if access to the specified location provider is allowed by the specified
+     * user's settings. Access to all location providers is forbidden to non-location-provider
+     * processes belonging to background users.
+     *
+     * @param provider the name of the location provider
+     * @param uid      the requestor's UID
+     * @param userId   the user id to query
+     */
+    private boolean isAllowedByUserSettingsLockedForUser(
+            String provider, int uid, int userId) {
+        if (!isCurrentProfile(UserHandle.getUserId(uid)) && !isUidALocationProvider(uid)) {
+            return false;
+        }
+        return isLocationProviderEnabledForUser(provider, userId);
+    }
+
+    /**
      * Returns the permission string associated with the specified resolution level.
      *
      * @param resolutionLevel the resolution level
@@ -1425,10 +1438,10 @@
      */
     private int getAllowedResolutionLevel(int pid, int uid) {
         if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
-                pid, uid) == PackageManager.PERMISSION_GRANTED) {
+                pid, uid) == PERMISSION_GRANTED) {
             return RESOLUTION_LEVEL_FINE;
         } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
-                pid, uid) == PackageManager.PERMISSION_GRANTED) {
+                pid, uid) == PERMISSION_GRANTED) {
             return RESOLUTION_LEVEL_COARSE;
         } else {
             return RESOLUTION_LEVEL_NONE;
@@ -2053,7 +2066,7 @@
         }
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
-                        == PackageManager.PERMISSION_GRANTED;
+                        == PERMISSION_GRANTED;
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
                 callerHasLocationHardwarePermission);
 
@@ -2326,7 +2339,7 @@
         // Require that caller can manage given document
         boolean callerHasLocationHardwarePermission =
                 mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE)
-                        == PackageManager.PERMISSION_GRANTED;
+                        == PERMISSION_GRANTED;
         LocationRequest sanitizedRequest = createSanitizedRequest(request, allowedResolutionLevel,
                 callerHasLocationHardwarePermission);
 
@@ -2476,7 +2489,7 @@
 
         // and check for ACCESS_LOCATION_EXTRA_COMMANDS
         if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
-                != PackageManager.PERMISSION_GRANTED)) {
+                != PERMISSION_GRANTED)) {
             throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
         }
 
@@ -2546,8 +2559,64 @@
         return null;
     }
 
+    /**
+     * Method for enabling or disabling location.
+     *
+     * @param enabled true to enable location. false to disable location
+     * @param userId the user id to set
+     */
+    @Override
+    public void setLocationEnabledForUser(boolean enabled, int userId) {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        // enable all location providers
+        synchronized (mLock) {
+            for(String provider : getAllProviders()) {
+                setProviderEnabledForUser(provider, enabled, userId);
+            }
+        }
+    }
+
+    /**
+     * Returns the current enabled/disabled status of location
+     *
+     * @param userId the user id to query
+     * @return true if location is enabled. false if location is disabled.
+     */
+    @Override
+    public boolean isLocationEnabledForUser(int userId) {
+
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        synchronized (mLock) {
+            for (String provider : getAllProviders()) {
+                if (isProviderEnabledForUser(provider, userId)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
     @Override
     public boolean isProviderEnabled(String provider) {
+        return isProviderEnabledForUser(provider, UserHandle.myUserId());
+    }
+
+    /**
+     * Method for determining if a location provider is enabled.
+     *
+     * @param provider the location provider to query
+     * @param userId the user id to query
+     * @return true if the provider is enabled
+     */
+    @Override
+    public boolean isProviderEnabledForUser(String provider, int userId) {
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
         if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
@@ -2557,7 +2626,8 @@
         try {
             synchronized (mLock) {
                 LocationProviderInterface p = mProvidersByName.get(provider);
-                return p != null && isAllowedByUserSettingsLocked(provider, uid);
+                return p != null
+                    && isAllowedByUserSettingsLockedForUser(provider, uid, userId);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2565,6 +2635,83 @@
     }
 
     /**
+     * Method for enabling or disabling a single location provider.
+     *
+     * @param provider the name of the provider
+     * @param enabled true to enable the provider. false to disable the provider
+     * @param userId the user id to set
+     * @return true if the value was set successfully. false on failure.
+     */
+    @Override
+    public boolean setProviderEnabledForUser(
+            String provider, boolean enabled, int userId) {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
+
+        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
+        checkInteractAcrossUsersPermission(userId);
+
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                // to ensure thread safety, we write the provider name with a '+' or '-'
+                // and let the SettingsProvider handle it rather than reading and modifying
+                // the list of enabled providers.
+                if (enabled) {
+                    provider = "+" + provider;
+                } else {
+                    provider = "-" + provider;
+                }
+                return Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        provider,
+                        userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Read location provider status from Settings.Secure
+     *
+     * @param provider the location provider to query
+     * @param userId the user id to query
+     * @return true if the provider is enabled
+     */
+    private boolean isLocationProviderEnabledForUser(String provider, int userId) {
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // Use system settings
+            ContentResolver cr = mContext.getContentResolver();
+            String allowedProviders = Settings.Secure.getStringForUser(
+                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    /**
+     * Method for checking INTERACT_ACROSS_USERS permission if specified user id is not the same as
+     * current user id
+     *
+     * @param userId the user id to get or set value
+     */
+    private void checkInteractAcrossUsersPermission(int userId) {
+        int uid = Binder.getCallingUid();
+        if (UserHandle.getUserId(uid) != userId) {
+            if (ActivityManager.checkComponentPermission(
+                android.Manifest.permission.INTERACT_ACROSS_USERS, uid, -1, true)
+                != PERMISSION_GRANTED) {
+                throw new SecurityException("Requires INTERACT_ACROSS_USERS permission");
+            }
+        }
+    }
+
+    /**
      * Returns "true" if the UID belongs to a bound location provider.
      *
      * @param uid the uid
@@ -2585,7 +2732,7 @@
 
     private void checkCallerIsProvider() {
         if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
-                == PackageManager.PERMISSION_GRANTED) {
+                == PERMISSION_GRANTED) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 254d49b..1eec982 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -396,6 +396,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.BinderInternal;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.ByteTransferPipe;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.os.ProcessCpuTracker;
@@ -4710,7 +4711,8 @@
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
                 .setProfilerInfo(profilerInfo)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
                 .execute();
 
     }
@@ -4781,7 +4783,8 @@
                     .setResultWho(resultWho)
                     .setRequestCode(requestCode)
                     .setStartFlags(startFlags)
-                    .setMayWait(bOptions, userId)
+                    .setActivityOptions(bOptions)
+                    .setMayWait(userId)
                     .setIgnoreTargetSecurity(ignoreTargetSecurity)
                     .execute();
         } catch (SecurityException e) {
@@ -4817,7 +4820,8 @@
                 .setResultWho(resultWho)
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
                 .setProfilerInfo(profilerInfo)
                 .setWaitResult(res)
                 .execute();
@@ -4841,7 +4845,8 @@
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
                 .setGlobalConfiguration(config)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
                 .execute();
     }
 
@@ -4896,7 +4901,8 @@
                 .setVoiceInteractor(interactor)
                 .setStartFlags(startFlags)
                 .setProfilerInfo(profilerInfo)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
                 .execute();
     }
 
@@ -4911,7 +4917,8 @@
                 .setCallingUid(callingUid)
                 .setCallingPackage(callingPackage)
                 .setResolvedType(resolvedType)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(bOptions)
+                .setMayWait(userId)
                 .execute();
     }
 
@@ -4926,6 +4933,7 @@
             throw new SecurityException(msg);
         }
 
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
         final int recentsUid = mRecentTasks.getRecentsComponentUid();
         final ComponentName recentsComponent = mRecentTasks.getRecentsComponent();
         final String recentsPackage = recentsComponent.getPackageName();
@@ -4956,7 +4964,8 @@
                 return mActivityStartController.obtainStarter(intent, "startRecentsActivity")
                         .setCallingUid(recentsUid)
                         .setCallingPackage(recentsPackage)
-                        .setMayWait(activityOptions, userId)
+                        .setActivityOptions(safeOptions)
+                        .setMayWait(userId)
                         .execute();
             }
         } finally {
@@ -5044,17 +5053,17 @@
         if (intent != null && intent.hasFileDescriptors() == true) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
         }
-        ActivityOptions options = ActivityOptions.fromBundle(bOptions);
+        SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions);
 
         synchronized (this) {
             final ActivityRecord r = ActivityRecord.isInStackLocked(callingActivity);
             if (r == null) {
-                ActivityOptions.abort(options);
+                SafeActivityOptions.abort(options);
                 return false;
             }
             if (r.app == null || r.app.thread == null) {
                 // The caller is not running...  d'oh!
-                ActivityOptions.abort(options);
+                SafeActivityOptions.abort(options);
                 return false;
             }
             intent = new Intent(intent);
@@ -5099,7 +5108,7 @@
 
             if (aInfo == null) {
                 // Nobody who is next!
-                ActivityOptions.abort(options);
+                SafeActivityOptions.abort(options);
                 if (debug) Slog.d(TAG, "Next matching activity: nothing found");
                 return false;
             }
@@ -5161,10 +5170,13 @@
         enforceCallerIsRecentsOrHasPermission(START_TASKS_FROM_RECENTS,
                 "startActivityFromRecents()");
 
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                return mStackSupervisor.startActivityFromRecents(taskId, bOptions);
+                return mStackSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
+                        SafeActivityOptions.fromBundle(bOptions));
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -5181,7 +5193,8 @@
                 userId, false, ALLOW_FULL_ONLY, reason, null);
         // TODO: Switch to user app stacks here.
         int ret = mActivityStartController.startActivities(caller, -1, callingPackage,
-                intents, resolvedTypes, resultTo, bOptions, userId, reason);
+                intents, resolvedTypes, resultTo, SafeActivityOptions.fromBundle(bOptions), userId,
+                reason);
         return ret;
     }
 
@@ -7940,9 +7953,9 @@
         flags &= ~(PendingIntent.FLAG_NO_CREATE|PendingIntent.FLAG_CANCEL_CURRENT
                 |PendingIntent.FLAG_UPDATE_CURRENT);
 
-        PendingIntentRecord.Key key = new PendingIntentRecord.Key(
-                type, packageName, activity, resultWho,
-                requestCode, intents, resolvedTypes, flags, bOptions, userId);
+        PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, activity,
+                resultWho, requestCode, intents, resolvedTypes, flags,
+                SafeActivityOptions.fromBundle(bOptions), userId);
         WeakReference<PendingIntentRecord> ref;
         ref = mIntentSenderRecords.get(key);
         PendingIntentRecord rec = ref != null ? ref.get() : null;
@@ -8391,8 +8404,7 @@
                     stack.setPictureInPictureAspectRatio(aspectRatio);
                     stack.setPictureInPictureActions(actions);
 
-                    MetricsLogger.action(mContext, MetricsEvent.ACTION_PICTURE_IN_PICTURE_ENTERED,
-                            r.supportsEnterPipOnTaskSwitch);
+                    MetricsLoggerWrapper.logPictureInPictureEnter(mContext, r.supportsEnterPipOnTaskSwitch);
                     logPictureInPictureArgs(params);
                 };
 
@@ -10434,9 +10446,13 @@
     @Override
     public void startInPlaceAnimationOnFrontMostApplication(Bundle opts)
             throws RemoteException {
-        final ActivityOptions activityOptions = ActivityOptions.fromBundle(opts);
-        if (activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE ||
-                activityOptions.getCustomInPlaceResId() == 0) {
+        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(opts);
+        final ActivityOptions activityOptions = safeOptions != null
+                ? safeOptions.getOptions(mStackSupervisor)
+                : null;
+        if (activityOptions == null
+                || activityOptions.getAnimationType() != ActivityOptions.ANIM_CUSTOM_IN_PLACE
+                || activityOptions.getCustomInPlaceResId() == 0) {
             throw new IllegalArgumentException("Expected in-place ActivityOption " +
                     "with valid animation");
         }
@@ -10539,16 +10555,17 @@
 
         if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId);
         synchronized(this) {
-            moveTaskToFrontLocked(taskId, flags, bOptions, false /* fromRecents */);
+            moveTaskToFrontLocked(taskId, flags, SafeActivityOptions.fromBundle(bOptions),
+                    false /* fromRecents */);
         }
     }
 
-    void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions, boolean fromRecents) {
-        ActivityOptions options = ActivityOptions.fromBundle(bOptions);
+    void moveTaskToFrontLocked(int taskId, int flags, SafeActivityOptions options,
+            boolean fromRecents) {
 
         if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(),
                 Binder.getCallingUid(), -1, -1, "Task to front")) {
-            ActivityOptions.abort(options);
+            SafeActivityOptions.abort(options);
             return;
         }
         final long origId = Binder.clearCallingIdentity();
@@ -10562,7 +10579,10 @@
                 Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
                 return;
             }
-            mStackSupervisor.findTaskToMoveToFront(task, flags, options, "moveTaskToFront",
+            ActivityOptions realOptions = options != null
+                    ? options.getOptions(mStackSupervisor)
+                    : null;
+            mStackSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
                     false /* forceNonResizable */);
 
             final ActivityRecord topActivity = task.getTopActivity();
@@ -10576,7 +10596,7 @@
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
-        ActivityOptions.abort(options);
+        SafeActivityOptions.abort(options);
     }
 
     /**
@@ -13534,6 +13554,7 @@
 
     @Override
     public boolean convertToTranslucent(IBinder token, Bundle options) {
+        SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(options);
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -13545,7 +13566,7 @@
                 int index = task.mActivities.lastIndexOf(r);
                 if (index > 0) {
                     ActivityRecord under = task.mActivities.get(index - 1);
-                    under.returningOptions = ActivityOptions.fromBundle(options);
+                    under.returningOptions = safeOptions.getOptions(r);
                 }
                 final boolean translucentChanged = r.changeWindowTranslucency(false);
                 if (translucentChanged) {
@@ -24909,7 +24930,8 @@
 
             synchronized (ActivityManagerService.this) {
                 return mActivityStartController.startActivitiesInPackage(packageUid, packageName,
-                        intents, resolvedTypes, /*resultTo*/ null, bOptions, userId);
+                        intents, resolvedTypes, null /* resultTo */,
+                        SafeActivityOptions.fromBundle(bOptions), userId);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 3d8863e..bfb563f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -167,7 +167,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.ReferrerIntent;
-import com.android.internal.logging.MetricsLogger;
+import com.android.internal.os.logging.MetricsLoggerWrapper;
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.LocalServices;
@@ -1591,7 +1591,7 @@
     boolean checkStartAnyActivityPermission(Intent intent, ActivityInfo aInfo,
             String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, boolean ignoreTargetSecurity, ProcessRecord callerApp,
-            ActivityRecord resultRecord, ActivityStack resultStack, ActivityOptions options) {
+            ActivityRecord resultRecord, ActivityStack resultStack) {
         final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid,
                 callingUid);
         if (startAnyPerm == PERMISSION_GRANTED) {
@@ -1645,57 +1645,6 @@
             Slog.w(TAG, message);
             return false;
         }
-        if (options != null) {
-            // If a launch task id is specified, then ensure that the caller is the recents
-            // component or has the START_TASKS_FROM_RECENTS permission
-            if (options.getLaunchTaskId() != INVALID_TASK_ID
-                    && !mRecentTasks.isCallerRecents(callingUid)) {
-                final int startInTaskPerm = mService.checkPermission(START_TASKS_FROM_RECENTS,
-                        callingPid, callingUid);
-                if (startInTaskPerm == PERMISSION_DENIED) {
-                    final String msg = "Permission Denial: starting " + intent.toString()
-                            + " from " + callerApp + " (pid=" + callingPid
-                            + ", uid=" + callingUid + ") with launchTaskId="
-                            + options.getLaunchTaskId();
-                    Slog.w(TAG, msg);
-                    throw new SecurityException(msg);
-                }
-            }
-            // Check if someone tries to launch an activity on a private display with a different
-            // owner.
-            final int launchDisplayId = options.getLaunchDisplayId();
-            if (launchDisplayId != INVALID_DISPLAY && !isCallerAllowedToLaunchOnDisplay(callingPid,
-                    callingUid, launchDisplayId, aInfo)) {
-                final String msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ") with launchDisplayId="
-                        + launchDisplayId;
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-            // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
-            final boolean lockTaskMode = options.getLockTaskMode();
-            if (lockTaskMode && !mService.mLockTaskController.isPackageWhitelisted(
-                    UserHandle.getUserId(callingUid), aInfo.packageName)) {
-                final String msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ") with lockTaskMode=true";
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-
-            // Check permission for remote animations
-            final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
-            if (adapter != null && mService.checkPermission(
-                    CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
-                            != PERMISSION_GRANTED) {
-                final String msg = "Permission Denial: starting " + intent.toString()
-                        + " from " + callerApp + " (pid=" + callingPid
-                        + ", uid=" + callingUid + ") with remoteAnimationAdapter";
-                Slog.w(TAG, msg);
-                throw new SecurityException(msg);
-            }
-        }
 
         return true;
     }
@@ -2166,8 +2115,8 @@
         }
     }
 
-    void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options,
-            String reason, boolean forceNonResizeable) {
+    void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
+            boolean forceNonResizeable) {
         final ActivityStack currentStack = task.getStack();
         if (currentStack == null) {
             Slog.e(TAG, "findTaskToMoveToFront: can't move task="
@@ -2620,8 +2569,7 @@
                 mAllowDockedStackResize = false;
             } else if (inPinnedWindowingMode && onTop) {
                 // Log if we are expanding the PiP to fullscreen
-                MetricsLogger.action(mService.mContext,
-                        ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN);
+                MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext);
             }
 
             // If we are moving from the pinned stack, then the animation takes care of updating
@@ -4547,16 +4495,17 @@
         task.setTaskDockedResizing(true);
     }
 
-    int startActivityFromRecents(int taskId, Bundle bOptions) {
+    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
+            SafeActivityOptions options) {
         final TaskRecord task;
-        final int callingUid;
         final String callingPackage;
         final Intent intent;
         final int userId;
         int activityType = ACTIVITY_TYPE_UNDEFINED;
         int windowingMode = WINDOWING_MODE_UNDEFINED;
-        final ActivityOptions activityOptions = (bOptions != null)
-                ? new ActivityOptions(bOptions) : null;
+        final ActivityOptions activityOptions = options != null
+                ? options.getOptions(this)
+                : null;
         if (activityOptions != null) {
             activityType = activityOptions.getLaunchActivityType();
             windowingMode = activityOptions.getLaunchWindowingMode();
@@ -4604,7 +4553,7 @@
                 sendPowerHintForLaunchStartIfNeeded(true /* forceSend */, targetActivity);
                 mActivityMetricsLogger.notifyActivityLaunching();
                 try {
-                    mService.moveTaskToFrontLocked(task.taskId, 0, bOptions,
+                    mService.moveTaskToFrontLocked(task.taskId, 0, options,
                             true /* fromRecents */);
                 } finally {
                     mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT,
@@ -4623,13 +4572,13 @@
                         task.getStack());
                 return ActivityManager.START_TASK_TO_FRONT;
             }
-            callingUid = task.mCallingUid;
             callingPackage = task.mCallingPackage;
             intent = task.intent;
             intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
             userId = task.userId;
-            int result = mService.getActivityStartController().startActivityInPackage(callingUid,
-                    callingPackage, intent, null, null, null, 0, 0, bOptions, userId, task,
+            int result = mService.getActivityStartController().startActivityInPackage(
+                    task.mCallingUid, callingPid, callingUid, callingPackage, intent, null, null,
+                    null, 0, 0, options, userId, task,
                     "startActivityFromRecents");
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
                 setResizingDuringAnimation(task);
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index aed49e0..f9932b2 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -220,43 +220,44 @@
         }
     }
 
-    final int startActivityInPackage(int uid, String callingPackage,
-            Intent intent, String resolvedType, IBinder resultTo,
-            String resultWho, int requestCode, int startFlags, Bundle bOptions, int userId,
-            TaskRecord inTask, String reason) {
+    final int startActivityInPackage(int uid, int realCallingUid, int realCallingPid,
+            String callingPackage, Intent intent, String resolvedType, IBinder resultTo,
+            String resultWho, int requestCode, int startFlags, SafeActivityOptions options,
+            int userId, TaskRecord inTask, String reason) {
 
-        userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
-                Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, "startActivityInPackage",
-                null);
+        userId = mService.mUserController.handleIncomingUser(realCallingPid, realCallingUid, userId,
+                false, ALLOW_FULL_ONLY, "startActivityInPackage", null);
 
         // TODO: Switch to user app stacks here.
         return obtainStarter(intent, reason)
                 .setCallingUid(uid)
+                .setRealCallingPid(realCallingPid)
+                .setRealCallingUid(realCallingUid)
                 .setCallingPackage(callingPackage)
                 .setResolvedType(resolvedType)
                 .setResultTo(resultTo)
                 .setResultWho(resultWho)
                 .setRequestCode(requestCode)
                 .setStartFlags(startFlags)
-                .setMayWait(bOptions, userId)
+                .setActivityOptions(options)
+                .setMayWait(userId)
                 .setInTask(inTask)
                 .execute();
     }
 
     final int startActivitiesInPackage(int uid, String callingPackage, Intent[] intents,
-            String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId) {
+            String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options, int userId) {
         final String reason = "startActivityInPackage";
         userId = mService.mUserController.handleIncomingUser(Binder.getCallingPid(),
                 Binder.getCallingUid(), userId, false, ALLOW_FULL_ONLY, reason, null);
         // TODO: Switch to user app stacks here.
-        int ret = startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo,
-                bOptions, userId, reason);
-        return ret;
+        return startActivities(null, uid, callingPackage, intents, resolvedTypes, resultTo, options,
+                userId, reason);
     }
 
     int startActivities(IApplicationThread caller, int callingUid, String callingPackage,
-            Intent[] intents, String[] resolvedTypes, IBinder resultTo, Bundle bOptions, int userId,
-            String reason) {
+            Intent[] intents, String[] resolvedTypes, IBinder resultTo, SafeActivityOptions options,
+            int userId, String reason) {
         if (intents == null) {
             throw new NullPointerException("intents is null");
         }
@@ -312,9 +313,9 @@
                                 "FLAG_CANT_SAVE_STATE not supported here");
                     }
 
-                    ActivityOptions options = ActivityOptions.fromBundle(
-                            i == intents.length - 1 ? bOptions : null);
-
+                    final SafeActivityOptions checkedOptions = i == intents.length - 1
+                            ? options
+                            : null;
                     final int res = obtainStarter(intent, reason)
                             .setCaller(caller)
                             .setResolvedType(resolvedTypes[i])
@@ -326,7 +327,7 @@
                             .setCallingPackage(callingPackage)
                             .setRealCallingPid(realCallingPid)
                             .setRealCallingUid(realCallingUid)
-                            .setActivityOptions(options)
+                            .setActivityOptions(checkedOptions)
                             .setComponentSpecified(componentSpecified)
                             .setOutActivity(outActivity)
                             .execute();
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 3a13155..8595aa3 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -74,6 +74,7 @@
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
@@ -298,7 +299,7 @@
         int realCallingPid;
         int realCallingUid;
         int startFlags;
-        ActivityOptions activityOptions;
+        SafeActivityOptions activityOptions;
         boolean ignoreTargetSecurity;
         boolean componentSpecified;
         ActivityRecord[] outActivity;
@@ -306,13 +307,12 @@
         String reason;
         ProfilerInfo profilerInfo;
         Configuration globalConfig;
-        Bundle waitOptions;
         int userId;
         WaitResult waitResult;
 
         /**
          * Indicates that we should wait for the result of the start request. This flag is set when
-         * {@link ActivityStarter#setMayWait(Bundle, int)} is called.
+         * {@link ActivityStarter#setMayWait(int)} is called.
          * {@see ActivityStarter#startActivityMayWait}.
          */
         boolean mayWait;
@@ -353,7 +353,6 @@
             reason = null;
             profilerInfo = null;
             globalConfig = null;
-            waitOptions = null;
             userId = 0;
             waitResult = null;
             mayWait = false;
@@ -388,7 +387,6 @@
             reason = request.reason;
             profilerInfo = request.profilerInfo;
             globalConfig = request.globalConfig;
-            waitOptions = request.waitOptions;
             userId = request.userId;
             waitResult = request.waitResult;
             mayWait = request.mayWait;
@@ -473,7 +471,7 @@
                         mRequest.voiceSession, mRequest.voiceInteractor, mRequest.resultTo,
                         mRequest.resultWho, mRequest.requestCode, mRequest.startFlags,
                         mRequest.profilerInfo, mRequest.waitResult, mRequest.globalConfig,
-                        mRequest.waitOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
+                        mRequest.activityOptions, mRequest.ignoreTargetSecurity, mRequest.userId,
                         mRequest.inTask, mRequest.reason);
             } else {
                 return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
@@ -513,7 +511,7 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
+            SafeActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
             ActivityRecord[] outActivity, TaskRecord inTask, String reason) {
 
         if (TextUtils.isEmpty(reason)) {
@@ -555,8 +553,9 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int callingPid, int callingUid,
             String callingPackage, int realCallingPid, int realCallingUid, int startFlags,
-            ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
-            ActivityRecord[] outActivity, TaskRecord inTask) {
+            SafeActivityOptions options,
+            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
+            TaskRecord inTask) {
         int err = ActivityManager.START_SUCCESS;
         // Pull the optional Ephemeral Installer-only bundle out of the options early.
         final Bundle verificationBundle
@@ -603,7 +602,7 @@
             // Transfer the result target from the source activity to the new
             // one being started, including any failures.
             if (requestCode >= 0) {
-                ActivityOptions.abort(options);
+                SafeActivityOptions.abort(options);
                 return ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT;
             }
             resultRecord = sourceRecord.resultTo;
@@ -691,16 +690,20 @@
                 resultStack.sendActivityResultLocked(
                         -1, resultRecord, resultWho, requestCode, RESULT_CANCELED, null);
             }
-            ActivityOptions.abort(options);
+            SafeActivityOptions.abort(options);
             return err;
         }
 
         boolean abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
-                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity, callerApp,
-                resultRecord, resultStack, options);
+                requestCode, callingPid, callingUid, callingPackage, ignoreTargetSecurity,
+                callerApp, resultRecord, resultStack);
         abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                 callingPid, resolvedType, aInfo.applicationInfo);
 
+        // Merge the two options bundles, while realCallerOptions takes precedence.
+        ActivityOptions checkedOptions = options != null
+                ? options.getOptions(intent, aInfo, callerApp, mSupervisor)
+                : null;
         if (mService.mController != null) {
             try {
                 // The Intent we give to the watcher has the extra data
@@ -715,7 +718,7 @@
 
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
         if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
-                callingUid, options)) {
+                callingUid, checkedOptions)) {
             // activity start was intercepted, e.g. because the target user is currently in quiet
             // mode (turn off work) or the target application is suspended
             intent = mInterceptor.mIntent;
@@ -725,7 +728,7 @@
             inTask = mInterceptor.mInTask;
             callingPid = mInterceptor.mCallingPid;
             callingUid = mInterceptor.mCallingUid;
-            options = mInterceptor.mActivityOptions;
+            checkedOptions = mInterceptor.mActivityOptions;
         }
 
         if (abort) {
@@ -735,7 +738,7 @@
             }
             // We pretend to the caller that it was really started, but
             // they will just get a cancel result.
-            ActivityOptions.abort(options);
+            ActivityOptions.abort(checkedOptions);
             return START_ABORTED;
         }
 
@@ -796,7 +799,7 @@
         ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid,
                 callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
                 resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
-                mSupervisor, options, sourceRecord);
+                mSupervisor, checkedOptions, sourceRecord);
         if (outActivity != null) {
             outActivity[0] = r;
         }
@@ -808,13 +811,16 @@
         }
 
         final ActivityStack stack = mSupervisor.mFocusedStack;
+
+        // If we are starting an activity that is not from the same uid as the currently resumed
+        // one, check whether app switches are allowed.
         if (voiceSession == null && (stack.mResumedActivity == null
-                || stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
+                || stack.mResumedActivity.info.applicationInfo.uid != realCallingUid)) {
             if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,
                     realCallingPid, realCallingUid, "Activity start")) {
                 mController.addPendingActivityLaunch(new PendingActivityLaunch(r,
                         sourceRecord, startFlags, stack, callerApp));
-                ActivityOptions.abort(options);
+                ActivityOptions.abort(checkedOptions);
                 return ActivityManager.START_SWITCHES_CANCELED;
             }
         }
@@ -833,9 +839,10 @@
         mController.doPendingActivityLaunches(false);
 
         return startActivity(r, sourceRecord, voiceSession, voiceInteractor, startFlags,
-                true /* doResume */, options, inTask, outActivity);
+                true /* doResume */, checkedOptions, inTask, outActivity);
     }
 
+
     /**
      * Creates a launch intent for the given auxiliary resolution data.
      */
@@ -900,8 +907,8 @@
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             IBinder resultTo, String resultWho, int requestCode, int startFlags,
             ProfilerInfo profilerInfo, WaitResult outResult,
-            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
-            TaskRecord inTask, String reason) {
+            Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
+            int userId, TaskRecord inTask, String reason) {
         // Refuse possible leaked file descriptors
         if (intent != null && intent.hasFileDescriptors()) {
             throw new IllegalArgumentException("File descriptors passed in Intent");
@@ -953,7 +960,6 @@
         // Collect information about the target of the Intent.
         ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
 
-        ActivityOptions options = ActivityOptions.fromBundle(bOptions);
         synchronized (mService) {
             final int realCallingPid = Binder.getCallingPid();
             final int realCallingUid = Binder.getCallingUid();
@@ -993,7 +999,7 @@
                                 Slog.w(TAG, "Unable to find app for caller " + caller
                                         + " (pid=" + callingPid + ") when starting: "
                                         + intent.toString());
-                                ActivityOptions.abort(options);
+                                SafeActivityOptions.abort(options);
                                 return ActivityManager.START_PERMISSION_DENIED;
                             }
                         }
@@ -1039,12 +1045,10 @@
             }
 
             final ActivityRecord[] outRecord = new ActivityRecord[1];
-            int res = startActivity(caller, intent, ephemeralIntent, resolvedType,
-                    aInfo, rInfo, voiceSession, voiceInteractor,
-                    resultTo, resultWho, requestCode, callingPid,
-                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags,
-                    options, ignoreTargetSecurity, componentSpecified, outRecord, inTask,
-                    reason);
+            int res = startActivity(caller, intent, ephemeralIntent, resolvedType, aInfo, rInfo,
+                    voiceSession, voiceInteractor, resultTo, resultWho, requestCode, callingPid,
+                    callingUid, callingPackage, realCallingPid, realCallingUid, startFlags, options,
+                    ignoreTargetSecurity, componentSpecified, outRecord, inTask, reason);
 
             Binder.restoreCallingIdentity(origId);
 
@@ -1248,7 +1252,7 @@
                     outActivity[0] = reusedActivity;
                 }
 
-                return START_TASK_TO_FRONT;
+                return START_DELIVERED_TO_TOP;
             }
         }
 
@@ -2447,11 +2451,15 @@
         return this;
     }
 
-    ActivityStarter setActivityOptions(ActivityOptions options) {
+    ActivityStarter setActivityOptions(SafeActivityOptions options) {
         mRequest.activityOptions = options;
         return this;
     }
 
+    ActivityStarter setActivityOptions(Bundle bOptions) {
+        return setActivityOptions(SafeActivityOptions.fromBundle(bOptions));
+    }
+
     ActivityStarter setIgnoreTargetSecurity(boolean ignoreTargetSecurity) {
         mRequest.ignoreTargetSecurity = ignoreTargetSecurity;
         return this;
@@ -2487,19 +2495,13 @@
         return this;
     }
 
-    ActivityStarter setWaitOptions(Bundle options) {
-        mRequest.waitOptions = options;
-        return this;
-    }
-
     ActivityStarter setUserId(int userId) {
         mRequest.userId = userId;
         return this;
     }
 
-    ActivityStarter setMayWait(Bundle options, int userId) {
+    ActivityStarter setMayWait(int userId) {
         mRequest.mayWait = true;
-        mRequest.waitOptions = options;
         mRequest.userId = userId;
 
         return this;
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 26d65bc..0da7e0e 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -408,9 +408,11 @@
                         final Set<String> cats = task.intent.getCategories();
                         if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) {
                             mService.getActivityStartController().startActivityInPackage(
-                                    task.mCallingUid, task.mCallingPackage, task.intent, null, null,
-                                    null, 0, 0, ActivityOptions.makeBasic().toBundle(), task.userId,
-                                    null, "AppErrors");
+                                    task.mCallingUid, callingPid, callingUid, task.mCallingPackage,
+                                    task.intent, null, null, null, 0, 0,
+                                    new SafeActivityOptions(ActivityOptions.makeBasic()),
+                                    task.userId, null,
+                                    "AppErrors");
                         }
                     }
                 }
diff --git a/services/core/java/com/android/server/am/AppTaskImpl.java b/services/core/java/com/android/server/am/AppTaskImpl.java
index f821f6b..5f5a504 100644
--- a/services/core/java/com/android/server/am/AppTaskImpl.java
+++ b/services/core/java/com/android/server/am/AppTaskImpl.java
@@ -20,6 +20,7 @@
 import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.IAppTask;
 import android.app.IApplicationThread;
 import android.content.Intent;
@@ -93,10 +94,13 @@
     public void moveToFront() {
         checkCaller();
         // Will bring task to front if it already has a root activity.
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
-                mService.mStackSupervisor.startActivityFromRecents(mTaskId, null);
+                mService.mStackSupervisor.startActivityFromRecents(callingPid, callingUid, mTaskId,
+                        null);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -127,7 +131,8 @@
                 .setCaller(appThread)
                 .setCallingPackage(callingPackage)
                 .setResolvedType(resolvedType)
-                .setMayWait(bOptions, callingUser)
+                .setActivityOptions(bOptions)
+                .setMayWait(callingUser)
                 .setInTask(tr)
                 .execute();
     }
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index c26e770..8e9d85d 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -16,10 +16,12 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.START_SUCCESS;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.content.IIntentSender;
 import android.content.IIntentReceiver;
 import android.app.PendingIntent;
@@ -65,7 +67,7 @@
         final int requestCode;
         final Intent requestIntent;
         final String requestResolvedType;
-        final Bundle options;
+        final SafeActivityOptions options;
         Intent[] allIntents;
         String[] allResolvedTypes;
         final int flags;
@@ -75,7 +77,7 @@
         private static final int ODD_PRIME_NUMBER = 37;
 
         Key(int _t, String _p, ActivityRecord _a, String _w,
-                int _r, Intent[] _i, String[] _it, int _f, Bundle _o, int _userId) {
+                int _r, Intent[] _i, String[] _it, int _f, SafeActivityOptions _o, int _userId) {
             type = _t;
             packageName = _p;
             activity = _a;
@@ -310,17 +312,16 @@
                 if (userId == UserHandle.USER_CURRENT) {
                     userId = owner.mUserController.getCurrentOrTargetUserId();
                 }
-                int res = 0;
+                int res = START_SUCCESS;
                 switch (key.type) {
                     case ActivityManager.INTENT_SENDER_ACTIVITY:
-                        if (options == null) {
-                            options = key.options;
-                        } else if (key.options != null) {
-                            Bundle opts = new Bundle(key.options);
-                            opts.putAll(options);
-                            options = opts;
-                        }
                         try {
+                            SafeActivityOptions mergedOptions = key.options;
+                            if (mergedOptions == null) {
+                                mergedOptions = SafeActivityOptions.fromBundle(options);
+                            } else {
+                                mergedOptions.setCallerOptions(ActivityOptions.fromBundle(options));
+                            }
                             if (key.allIntents != null && key.allIntents.length > 1) {
                                 Intent[] allIntents = new Intent[key.allIntents.length];
                                 String[] allResolvedTypes = new String[key.allIntents.length];
@@ -332,14 +333,14 @@
                                 }
                                 allIntents[allIntents.length-1] = finalIntent;
                                 allResolvedTypes[allResolvedTypes.length-1] = resolvedType;
-                                owner.getActivityStartController().startActivitiesInPackage(uid,
-                                        key.packageName, allIntents, allResolvedTypes, resultTo,
-                                        options, userId);
+                                res = owner.getActivityStartController().startActivitiesInPackage(
+                                        uid, key.packageName, allIntents, allResolvedTypes,
+                                        resultTo, mergedOptions, userId);
                             } else {
-                                owner.getActivityStartController().startActivityInPackage(uid,
-                                        key.packageName, finalIntent, resolvedType, resultTo,
-                                        resultWho, requestCode, 0, options, userId, null,
-                                        "PendingIntentRecord");
+                                res = owner.getActivityStartController().startActivityInPackage(uid,
+                                        callingPid, callingUid, key.packageName, finalIntent,
+                                        resolvedType, resultTo, resultWho, requestCode, 0,
+                                        mergedOptions, userId, null, "PendingIntentRecord");
                             }
                         } catch (RuntimeException e) {
                             Slog.w(TAG, "Unable to send startActivity intent", e);
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
new file mode 100644
index 0000000..d08111e
--- /dev/null
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.view.Display.INVALID_DISPLAY;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
+
+import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.view.RemoteAnimationAdapter;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Wraps {@link ActivityOptions}, records binder identity, and checks permission when retrieving
+ * the inner options. Also supports having two set of options: Once from the original caller, and
+ * once from the caller that is overriding it, which happens when sending a {@link PendingIntent}.
+ */
+class SafeActivityOptions {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "SafeActivityOptions" : TAG_AM;
+
+    private final int mOriginalCallingPid;
+    private final int mOriginalCallingUid;
+    private int mRealCallingPid;
+    private int mRealCallingUid;
+    private final @Nullable ActivityOptions mOriginalOptions;
+    private @Nullable ActivityOptions mCallerOptions;
+
+    /**
+     * Constructs a new instance from a bundle and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
+     * this object.
+     *
+     * @param bOptions The {@link ActivityOptions} as {@link Bundle}.
+     */
+    static SafeActivityOptions fromBundle(Bundle bOptions) {
+        return bOptions != null
+                ? new SafeActivityOptions(ActivityOptions.fromBundle(bOptions))
+                : null;
+    }
+
+    /**
+     * Constructs a new instance and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when constructing
+     * this object.
+     *
+     * @param options The options to wrap.
+     */
+    SafeActivityOptions(@Nullable ActivityOptions options) {
+        mOriginalCallingPid = Binder.getCallingPid();
+        mOriginalCallingUid = Binder.getCallingUid();
+        mOriginalOptions = options;
+    }
+
+    /**
+     * Overrides options with options from a caller and records {@link Binder#getCallingPid}/
+     * {@link Binder#getCallingUid}. Thus, calling identity MUST NOT be cleared when calling this
+     * method.
+     */
+    void setCallerOptions(@Nullable ActivityOptions options) {
+        mRealCallingPid = Binder.getCallingPid();
+        mRealCallingUid = Binder.getCallingUid();
+        mCallerOptions = options;
+    }
+
+    /**
+     * Performs permission check and retrieves the options.
+     *
+     * @param r The record of the being started activity.
+     */
+    ActivityOptions getOptions(ActivityRecord r) throws SecurityException {
+        return getOptions(r.intent, r.info, r.app, r.mStackSupervisor);
+    }
+
+    /**
+     * Performs permission check and retrieves the options when options are not being used to launch
+     * a specific activity (i.e. a task is moved to front).
+     */
+    ActivityOptions getOptions(ActivityStackSupervisor supervisor) throws SecurityException {
+        return getOptions(null, null, null, supervisor);
+    }
+
+    /**
+     * Performs permission check and retrieves the options.
+     *
+     * @param intent The intent that is being launched.
+     * @param aInfo The info of the activity being launched.
+     * @param callerApp The record of the caller.
+     */
+    ActivityOptions getOptions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
+            @Nullable ProcessRecord callerApp,
+            ActivityStackSupervisor supervisor) throws SecurityException {
+        if (mOriginalOptions != null) {
+            checkPermissions(intent, aInfo, callerApp, supervisor, mOriginalOptions,
+                    mOriginalCallingPid, mOriginalCallingUid);
+        }
+        if (mCallerOptions != null) {
+            checkPermissions(intent, aInfo, callerApp, supervisor, mCallerOptions,
+                    mRealCallingPid, mRealCallingUid);
+        }
+        return mergeActivityOptions(mOriginalOptions, mCallerOptions);
+    }
+
+    /**
+     * @see ActivityOptions#popAppVerificationBundle
+     */
+    Bundle popAppVerificationBundle() {
+        return mOriginalOptions != null ? mOriginalOptions.popAppVerificationBundle() : null;
+    }
+
+    private void abort() {
+        if (mOriginalOptions != null) {
+            ActivityOptions.abort(mOriginalOptions);
+        }
+        if (mCallerOptions != null) {
+            ActivityOptions.abort(mCallerOptions);
+        }
+    }
+
+    static void abort(@Nullable SafeActivityOptions options) {
+        if (options != null) {
+            options.abort();
+        }
+    }
+
+    /**
+     * Merges two activity options into one, with {@code options2} taking precedence in case of a
+     * conflict.
+     */
+    @VisibleForTesting
+    @Nullable ActivityOptions mergeActivityOptions(@Nullable ActivityOptions options1,
+            @Nullable ActivityOptions options2) {
+        if (options1 == null) {
+            return options2;
+        }
+        if (options2 == null) {
+            return options1;
+        }
+        final Bundle b1 = options1.toBundle();
+        final Bundle b2 = options2.toBundle();
+        b1.putAll(b2);
+        return ActivityOptions.fromBundle(b1);
+    }
+
+    private void checkPermissions(@Nullable Intent intent, @Nullable ActivityInfo aInfo,
+            @Nullable ProcessRecord callerApp, ActivityStackSupervisor supervisor,
+            ActivityOptions options, int callingPid, int callingUid) {
+        // If a launch task id is specified, then ensure that the caller is the recents
+        // component or has the START_TASKS_FROM_RECENTS permission
+        if (options.getLaunchTaskId() != INVALID_TASK_ID
+                && !supervisor.mRecentTasks.isCallerRecents(callingUid)) {
+            final int startInTaskPerm = supervisor.mService.checkPermission(
+                    START_TASKS_FROM_RECENTS, callingPid, callingUid);
+            if (startInTaskPerm == PERMISSION_DENIED) {
+                final String msg = "Permission Denial: starting " + getIntentString(intent)
+                        + " from " + callerApp + " (pid=" + callingPid
+                        + ", uid=" + callingUid + ") with launchTaskId="
+                        + options.getLaunchTaskId();
+                Slog.w(TAG, msg);
+                throw new SecurityException(msg);
+            }
+        }
+        // Check if someone tries to launch an activity on a private display with a different
+        // owner.
+        final int launchDisplayId = options.getLaunchDisplayId();
+        if (aInfo != null && launchDisplayId != INVALID_DISPLAY
+                && !supervisor.isCallerAllowedToLaunchOnDisplay(callingPid, callingUid,
+                        launchDisplayId, aInfo)) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with launchDisplayId="
+                    + launchDisplayId;
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+        // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
+        final boolean lockTaskMode = options.getLockTaskMode();
+        if (aInfo != null && lockTaskMode
+                && !supervisor.mService.mLockTaskController.isPackageWhitelisted(
+                        UserHandle.getUserId(callingUid), aInfo.packageName)) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with lockTaskMode=true";
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+
+        // Check permission for remote animations
+        final RemoteAnimationAdapter adapter = options.getRemoteAnimationAdapter();
+        if (adapter != null && supervisor.mService.checkPermission(
+                CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+                        != PERMISSION_GRANTED) {
+            final String msg = "Permission Denial: starting " + getIntentString(intent)
+                    + " from " + callerApp + " (pid=" + callingPid
+                    + ", uid=" + callingUid + ") with remoteAnimationAdapter";
+            Slog.w(TAG, msg);
+            throw new SecurityException(msg);
+        }
+    }
+
+    private String getIntentString(Intent intent) {
+        return intent != null ? intent.toString() : "(no intent)";
+    }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index a714720..f4c99f5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -63,6 +63,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioDevicePort;
 import android.media.AudioFocusInfo;
+import android.media.AudioFocusRequest;
 import android.media.AudioSystem;
 import android.media.AudioFormat;
 import android.media.AudioManager;
@@ -6003,6 +6004,44 @@
     //==========================================================================================
     // Audio Focus
     //==========================================================================================
+    /**
+     * Returns whether a focus request is eligible to force ducking.
+     * Will return true if:
+     * - the AudioAttributes have a usage of USAGE_ASSISTANCE_ACCESSIBILITY,
+     * - the focus request is AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
+     * - the associated Bundle has KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING set to true,
+     * - the uid of the requester is a known accessibility service or root.
+     * @param aa AudioAttributes of the focus request
+     * @param uid uid of the focus requester
+     * @return true if ducking is to be forced
+     */
+    private boolean forceFocusDuckingForAccessibility(@Nullable AudioAttributes aa,
+            int request, int uid) {
+        if (aa == null || aa.getUsage() != AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY
+                || request != AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK) {
+            return false;
+        }
+        final Bundle extraInfo = aa.getBundle();
+        if (extraInfo == null ||
+                !extraInfo.getBoolean(AudioFocusRequest.KEY_ACCESSIBILITY_FORCE_FOCUS_DUCKING)) {
+            return false;
+        }
+        if (uid == 0) {
+            return true;
+        }
+        synchronized (mAccessibilityServiceUidsLock) {
+            if (mAccessibilityServiceUids != null) {
+                int callingUid = Binder.getCallingUid();
+                for (int i = 0; i < mAccessibilityServiceUids.length; i++) {
+                    if (mAccessibilityServiceUids[i] == callingUid) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
     public int requestAudioFocus(AudioAttributes aa, int durationHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
             IAudioPolicyCallback pcb, int sdk) {
@@ -6026,7 +6065,8 @@
         }
 
         return mMediaFocusControl.requestAudioFocus(aa, durationHint, cb, fd,
-                clientId, callingPackageName, flags, sdk);
+                clientId, callingPackageName, flags, sdk,
+                forceFocusDuckingForAccessibility(aa, durationHint, Binder.getCallingUid()));
     }
 
     public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId, AudioAttributes aa,
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 48f0d5a..f2ef02f 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -307,9 +307,10 @@
      * @return true if the focus loss is definitive, false otherwise.
      */
     @GuardedBy("MediaFocusControl.mAudioFocusLock")
-    boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner) {
+    boolean handleFocusLossFromGain(int focusGain, final FocusRequester frWinner, boolean forceDuck)
+    {
         final int focusLoss = focusLossForGainRequest(focusGain);
-        handleFocusLoss(focusLoss, frWinner);
+        handleFocusLoss(focusLoss, frWinner, forceDuck);
         return (focusLoss == AudioManager.AUDIOFOCUS_LOSS);
     }
 
@@ -343,7 +344,8 @@
     }
 
     @GuardedBy("MediaFocusControl.mAudioFocusLock")
-    void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner) {
+    void handleFocusLoss(int focusLoss, @Nullable final FocusRequester frWinner, boolean forceDuck)
+    {
         try {
             if (focusLoss != mFocusLossReceived) {
                 mFocusLossReceived = focusLoss;
@@ -374,19 +376,20 @@
                         && frWinner != null) {
                     // candidate for enforcement by the framework
                     if (frWinner.mCallingUid != this.mCallingUid) {
-                        if ((mGrantFlags
-                                & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0) {
+                        if (!forceDuck && ((mGrantFlags
+                                & AudioManager.AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS) != 0)) {
                             // the focus loser declared it would pause instead of duck, let it
                             // handle it (the framework doesn't pause for apps)
                             handled = false;
                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - flags");
-                        } else if (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW &&
-                                this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL) {
+                        } else if (!forceDuck && (MediaFocusControl.ENFORCE_DUCKING_FOR_NEW &&
+                                this.getSdkTarget() <= MediaFocusControl.DUCKING_IN_APP_SDK_LEVEL))
+                        {
                             // legacy behavior, apps used to be notified when they should be ducking
                             handled = false;
                             Log.v(TAG, "not ducking uid " + this.mCallingUid + " - old SDK");
                         } else {
-                            handled = mFocusController.duckPlayers(frWinner, this);
+                            handled = mFocusController.duckPlayers(frWinner, this, forceDuck);
                         }
                     } // else: the focus change is within the same app, so let the dispatching
                       //       happen as if the framework was not involved.
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index de58b59..9ddc52a 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -101,8 +101,8 @@
     //=================================================================
     // PlayerFocusEnforcer implementation
     @Override
-    public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
-        return mFocusEnforcer.duckPlayers(winner, loser);
+    public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
+        return mFocusEnforcer.duckPlayers(winner, loser, forceDuck);
     }
 
     @Override
@@ -144,7 +144,8 @@
             if (!mFocusStack.empty()) {
                 // notify the current focus owner it lost focus after removing it from stack
                 final FocusRequester exFocusOwner = mFocusStack.pop();
-                exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null);
+                exFocusOwner.handleFocusLoss(AudioManager.AUDIOFOCUS_LOSS, null,
+                        false /*forceDuck*/);
                 exFocusOwner.release();
             }
         }
@@ -166,13 +167,14 @@
      * @param focusGain the new focus gain that will later be added at the top of the stack
      */
     @GuardedBy("mAudioFocusLock")
-    private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr) {
+    private void propagateFocusLossFromGain_syncAf(int focusGain, final FocusRequester fr,
+            boolean forceDuck) {
         final List<String> clientsToRemove = new LinkedList<String>();
         // going through the audio focus stack to signal new focus, traversing order doesn't
         // matter as all entries respond to the same external focus gain
         for (FocusRequester focusLoser : mFocusStack) {
             final boolean isDefinitiveLoss =
-                    focusLoser.handleFocusLossFromGain(focusGain, fr);
+                    focusLoser.handleFocusLossFromGain(focusGain, fr, forceDuck);
             if (isDefinitiveLoss) {
                 clientsToRemove.add(focusLoser.getClientId());
             }
@@ -347,7 +349,7 @@
             Log.e(TAG, "No exclusive focus owner found in propagateFocusLossFromGain_syncAf()",
                     new Exception());
             // no exclusive owner, push at top of stack, focus is granted, propagate change
-            propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr);
+            propagateFocusLossFromGain_syncAf(nfr.getGainRequest(), nfr, false /*forceDuck*/);
             mFocusStack.push(nfr);
             return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
         } else {
@@ -664,7 +666,7 @@
     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int, int) */
     protected int requestAudioFocus(AudioAttributes aa, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName, int flags,
-            int sdk) {
+            int sdk, boolean forceDuck) {
         mEventLogger.log((new AudioEventLogger.StringEvent(
                 "requestAudioFocus() from uid/pid " + Binder.getCallingUid()
                     + "/" + Binder.getCallingPid()
@@ -777,7 +779,7 @@
             } else {
                 // propagate the focus change through the stack
                 if (!mFocusStack.empty()) {
-                    propagateFocusLossFromGain_syncAf(focusChangeHint, nfr);
+                    propagateFocusLossFromGain_syncAf(focusChangeHint, nfr, forceDuck);
                 }
 
                 // push focus requester at the top of the audio focus stack
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 4943173..ff86453 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -421,7 +421,7 @@
     private final DuckingManager mDuckingManager = new DuckingManager();
 
     @Override
-    public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
+    public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) {
         if (DEBUG) {
             Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
                     winner.getClientUid(), loser.getClientUid()));
@@ -441,8 +441,8 @@
                         && loser.hasSameUid(apc.getClientUid())
                         && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
                 {
-                    if (apc.getAudioAttributes().getContentType() ==
-                            AudioAttributes.CONTENT_TYPE_SPEECH) {
+                    if (!forceDuck && (apc.getAudioAttributes().getContentType() ==
+                            AudioAttributes.CONTENT_TYPE_SPEECH)) {
                         // the player is speaking, ducking will make the speech unintelligible
                         // so let the app handle it instead
                         Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId()
diff --git a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
index 0733eca..3c834da 100644
--- a/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
+++ b/services/core/java/com/android/server/audio/PlayerFocusEnforcer.java
@@ -25,7 +25,7 @@
      * @param loser
      * @return
      */
-    public boolean duckPlayers(FocusRequester winner, FocusRequester loser);
+    public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck);
 
     public void unduckPlayers(FocusRequester winner);
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index b97de65..c77ec20 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1849,7 +1849,6 @@
             if (packageName != null && !validatePackageName(getCallingUid(), packageName)) {
                 packageName = null;
             }
-            Preconditions.checkNotNull(c);
             final long token = Binder.clearCallingIdentity();
             try {
                 setBrightnessConfigurationForUserInternal(c, userId, packageName);
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index f1ce5c5..cbf46f8 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -598,14 +598,20 @@
         private boolean setBrightnessConfigurationForUser(BrightnessConfiguration c,
                 int userSerial, String packageName) {
             BrightnessConfiguration currentConfig = mConfigurations.get(userSerial);
-            if (currentConfig == null || !currentConfig.equals(c)) {
-                if (packageName == null) {
-                    mPackageNames.remove(userSerial);
+            if (currentConfig != c && (currentConfig == null || !currentConfig.equals(c))) {
+                if (c != null) {
+                    if (packageName == null) {
+                        mPackageNames.remove(userSerial);
+                    } else {
+                        mPackageNames.put(userSerial, packageName);
+                    }
+                    mTimeStamps.put(userSerial, System.currentTimeMillis());
+                    mConfigurations.put(userSerial, c);
                 } else {
-                    mPackageNames.put(userSerial, packageName);
+                    mPackageNames.remove(userSerial);
+                    mTimeStamps.delete(userSerial);
+                    mConfigurations.remove(userSerial);
                 }
-                mTimeStamps.put(userSerial, System.currentTimeMillis());
-                mConfigurations.put(userSerial, c);
                 return true;
             }
             return false;
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 0f5cb0a..611f4ec 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1054,6 +1054,14 @@
         if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
             pw.print(" DEVICE_NOT_DOZING");
         }
+        if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+            pw.print(" BACKGROUND_NOT_RESTRICTED");
+        }
+        if (constraints != 0) {
+            pw.print(" [0x");
+            pw.print(Integer.toHexString(constraints));
+            pw.print("]");
+        }
     }
 
     /** Writes constraints to the given repeating proto field. */
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 07ea51b..31c20cb 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -53,6 +53,7 @@
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.database.sqlite.SQLiteDatabase;
+import android.hardware.authsecret.V1_0.IAuthSecret;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -77,10 +78,10 @@
 import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProperties;
 import android.security.keystore.KeyProtection;
-import android.security.keystore.KeychainProtectionParams;
 import android.security.keystore.UserNotAuthenticatedException;
-import android.security.keystore.WrappedApplicationKey;
-import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyChainSnapshot;
 import android.service.gatekeeper.GateKeeperResponse;
 import android.service.gatekeeper.IGateKeeperService;
 import android.text.TextUtils;
@@ -126,8 +127,10 @@
 import java.security.UnrecoverableKeyException;
 import java.security.cert.CertificateException;
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -183,6 +186,7 @@
 
     private boolean mFirstCallToVold;
     protected IGateKeeperService mGateKeeperService;
+    protected IAuthSecret mAuthSecretService;
 
     /**
      * The UIDs that are used for system credential storage in keystore.
@@ -613,6 +617,14 @@
         } catch (RemoteException e) {
             Slog.e(TAG, "Failure retrieving IGateKeeperService", e);
         }
+        // Find the AuthSecret HAL
+        try {
+            mAuthSecretService = IAuthSecret.getService();
+        } catch (NoSuchElementException e) {
+            Slog.i(TAG, "Device doesn't implement AuthSecret HAL");
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to get AuthSecret HAL", e);
+        }
         mDeviceProvisionedObserver.onSystemReady();
         // TODO: maybe skip this for split system user mode.
         mStorage.prefetchUser(UserHandle.USER_SYSTEM);
@@ -1586,8 +1598,10 @@
                 userId, progressCallback);
         // The user employs synthetic password based credential.
         if (response != null) {
-            mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
-                    userId);
+            if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
+                mRecoverableKeyStoreManager.lockScreenSecretAvailable(credentialType, credential,
+                        userId);
+            }
             return response;
         }
 
@@ -1968,7 +1982,7 @@
     }
 
     @Override
-    public KeychainSnapshot getRecoveryData(@NonNull byte[] account) throws RemoteException {
+    public KeyChainSnapshot getRecoveryData(@NonNull byte[] account) throws RemoteException {
         return mRecoverableKeyStoreManager.getRecoveryData(account);
     }
 
@@ -1997,7 +2011,7 @@
     }
 
     @Override
-    public void setRecoverySecretTypes(@NonNull @KeychainProtectionParams.UserSecretType
+    public void setRecoverySecretTypes(@NonNull @KeyChainProtectionParams.UserSecretType
             int[] secretTypes) throws RemoteException {
         mRecoverableKeyStoreManager.setRecoverySecretTypes(secretTypes);
     }
@@ -2014,7 +2028,7 @@
     }
 
     @Override
-    public void recoverySecretAvailable(@NonNull KeychainProtectionParams recoverySecret)
+    public void recoverySecretAvailable(@NonNull KeyChainProtectionParams recoverySecret)
             throws RemoteException {
         mRecoverableKeyStoreManager.recoverySecretAvailable(recoverySecret);
     }
@@ -2022,7 +2036,7 @@
     @Override
     public byte[] startRecoverySession(@NonNull String sessionId,
             @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
-            @NonNull byte[] vaultChallenge, @NonNull List<KeychainProtectionParams> secrets)
+            @NonNull byte[] vaultChallenge, @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         return mRecoverableKeyStoreManager.startRecoverySession(sessionId, verifierPublicKey,
                 vaultParams, vaultChallenge, secrets);
@@ -2127,6 +2141,20 @@
     private SparseArray<AuthenticationToken> mSpCache = new SparseArray();
 
     private void onAuthTokenKnownForUser(@UserIdInt int userId, AuthenticationToken auth) {
+        // Pass the primary user's auth secret to the HAL
+        if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) {
+            try {
+                final byte[] rawSecret = auth.deriveVendorAuthSecret();
+                final ArrayList<Byte> secret = new ArrayList<>(rawSecret.length);
+                for (int i = 0; i < rawSecret.length; ++i) {
+                    secret.add(rawSecret[i]);
+                }
+                mAuthSecretService.primaryUserCredential(secret);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to pass primary user secret to AuthSecret HAL", e);
+            }
+        }
+
         // Update the SP cache, removing the entry when allowed
         synchronized (mSpManager) {
             if (shouldCacheSpForUser(userId)) {
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 7a3a746..88b2a36 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -121,6 +121,7 @@
     private static final byte[] PERSONALIZATION_USER_GK_AUTH = "user-gk-authentication".getBytes();
     private static final byte[] PERSONALIZATION_SP_GK_AUTH = "sp-gk-authentication".getBytes();
     private static final byte[] PERSONALIZATION_FBE_KEY = "fbe-key".getBytes();
+    private static final byte[] PERSONALIZATION_AUTHSECRET_KEY = "authsecret-hal".getBytes();
     private static final byte[] PERSONALIZATION_SP_SPLIT = "sp-split".getBytes();
     private static final byte[] PERSONALIZATION_E0 = "e0-encryption".getBytes();
     private static final byte[] PERSONALISATION_WEAVER_PASSWORD = "weaver-pwd".getBytes();
@@ -159,6 +160,11 @@
                     syntheticPassword.getBytes());
         }
 
+        public byte[] deriveVendorAuthSecret() {
+            return SyntheticPasswordCrypto.personalisedHash(PERSONALIZATION_AUTHSECRET_KEY,
+                    syntheticPassword.getBytes());
+        }
+
         private void initialize(byte[] P0, byte[] P1) {
             this.P1 = P1;
             this.syntheticPassword = String.valueOf(HexEncoding.encode(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index 724073a..e1e769c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -16,14 +16,14 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
+import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.KeychainProtectionParams;
-import android.security.keystore.KeychainSnapshot;
-import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -36,6 +36,7 @@
 import java.nio.ByteOrder;
 import java.nio.charset.StandardCharsets;
 import java.security.GeneralSecurityException;
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyStoreException;
 import java.security.MessageDigest;
@@ -254,12 +255,12 @@
         }
         // TODO: store raw data in RecoveryServiceMetadataEntry and generate Parcelables later
         // TODO: use Builder.
-        KeychainProtectionParams metadata = new KeychainProtectionParams(
+        KeyChainProtectionParams metadata = new KeyChainProtectionParams(
                 /*userSecretType=*/ TYPE_LOCKSCREEN,
                 /*lockScreenUiFormat=*/ getUiFormat(mCredentialType, mCredential),
                 /*keyDerivationParams=*/ KeyDerivationParams.createSha256Params(salt),
                 /*secret=*/ new byte[0]);
-        ArrayList<KeychainProtectionParams> metadataList = new ArrayList<>();
+        ArrayList<KeyChainProtectionParams> metadataList = new ArrayList<>();
         metadataList.add(metadata);
 
         int snapshotVersion = incrementSnapshotVersion(recoveryAgentUid);
@@ -267,13 +268,13 @@
         // If application keys are not updated, snapshot will not be created on next unlock.
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(mUserId, recoveryAgentUid, false);
 
-        mRecoverySnapshotStorage.put(recoveryAgentUid, new KeychainSnapshot.Builder()
+        mRecoverySnapshotStorage.put(recoveryAgentUid, new KeyChainSnapshot.Builder()
                 .setSnapshotVersion(snapshotVersion)
                 .setMaxAttempts(TRUSTED_HARDWARE_MAX_ATTEMPTS)
                 .setCounterId(counterId)
                 .setTrustedHardwarePublicKey(SecureBox.encodePublicKey(publicKey))
                 .setServerParams(vaultHandle)
-                .setKeychainProtectionParams(metadataList)
+                .setKeyChainProtectionParams(metadataList)
                 .setWrappedApplicationKeys(createApplicationKeyEntries(encryptedApplicationKeys))
                 .setEncryptedRecoveryKeyBlob(encryptedRecoveryKey)
                 .build());
@@ -301,7 +302,8 @@
      */
     private Map<String, SecretKey> getKeysToSync(int recoveryAgentUid)
             throws InsecureUserException, KeyStoreException, UnrecoverableKeyException,
-            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         PlatformKeyManager platformKeyManager = mPlatformKeyManagerFactory.newInstance();
         PlatformDecryptionKey decryptKey = platformKeyManager.getDecryptKey(mUserId);
         Map<String, WrappedKey> wrappedKeys = mRecoverableKeyStoreDb.getAllKeys(
@@ -315,7 +317,7 @@
      */
     private boolean shoudCreateSnapshot(int recoveryAgentUid) {
         int[] types = mRecoverableKeyStoreDb.getRecoverySecretTypes(mUserId, recoveryAgentUid);
-        if (!ArrayUtils.contains(types, KeychainProtectionParams.TYPE_LOCKSCREEN)) {
+        if (!ArrayUtils.contains(types, KeyChainProtectionParams.TYPE_LOCKSCREEN)) {
             // Only lockscreen type is supported.
             // We will need to pass extra argument to KeySyncTask to support custom pass phrase.
             return false;
@@ -338,14 +340,14 @@
      * @return The format - either pattern, pin, or password.
      */
     @VisibleForTesting
-    @KeychainProtectionParams.LockScreenUiFormat static int getUiFormat(
+    @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat(
             int credentialType, String credential) {
         if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) {
-            return KeychainProtectionParams.TYPE_PATTERN;
+            return KeyChainProtectionParams.UI_FORMAT_PATTERN;
         } else if (isPin(credential)) {
-            return KeychainProtectionParams.TYPE_PIN;
+            return KeyChainProtectionParams.UI_FORMAT_PIN;
         } else {
-            return KeychainProtectionParams.TYPE_PASSWORD;
+            return KeyChainProtectionParams.UI_FORMAT_PASSWORD;
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index 76508d5..b6c3c66 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -33,10 +33,10 @@
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
 
-import android.security.keystore.KeychainProtectionParams;
-import android.security.keystore.KeychainSnapshot;
-import android.security.keystore.RecoveryController;
-import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.RecoveryController;
+import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -170,11 +170,11 @@
      * @hide
      */
     public @NonNull
-    KeychainSnapshot getRecoveryData(@NonNull byte[] account)
+    KeyChainSnapshot getRecoveryData(@NonNull byte[] account)
             throws RemoteException {
         checkRecoverKeyStorePermission();
         int uid = Binder.getCallingUid();
-        KeychainSnapshot snapshot = mSnapshotStorage.get(uid);
+        KeyChainSnapshot snapshot = mSnapshotStorage.get(uid);
         if (snapshot == null) {
             throw new ServiceSpecificException(ERROR_NO_SNAPSHOT_PENDING);
         }
@@ -256,7 +256,7 @@
      * @hide
      */
     public void setRecoverySecretTypes(
-            @NonNull @KeychainProtectionParams.UserSecretType int[] secretTypes)
+            @NonNull @KeyChainProtectionParams.UserSecretType int[] secretTypes)
             throws RemoteException {
         checkRecoverKeyStorePermission();
         int userId = UserHandle.getCallingUserId();
@@ -291,9 +291,9 @@
     }
 
     public void recoverySecretAvailable(
-            @NonNull KeychainProtectionParams recoverySecret) throws RemoteException {
+            @NonNull KeyChainProtectionParams recoverySecret) throws RemoteException {
         int uid = Binder.getCallingUid();
-        if (recoverySecret.getLockScreenUiFormat() == KeychainProtectionParams.TYPE_LOCKSCREEN) {
+        if (recoverySecret.getLockScreenUiFormat() == KeyChainProtectionParams.TYPE_LOCKSCREEN) {
             throw new SecurityException(
                     "Caller " + uid + " is not allowed to set lock screen secret");
         }
@@ -319,14 +319,14 @@
             @NonNull byte[] verifierPublicKey,
             @NonNull byte[] vaultParams,
             @NonNull byte[] vaultChallenge,
-            @NonNull List<KeychainProtectionParams> secrets)
+            @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         checkRecoverKeyStorePermission();
         int uid = Binder.getCallingUid();
 
         if (secrets.size() != 1) {
             throw new UnsupportedOperationException(
-                    "Only a single KeychainProtectionParams is supported");
+                    "Only a single KeyChainProtectionParams is supported");
         }
 
         PublicKey publicKey;
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index c33c9de..d85e89e 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -184,7 +184,8 @@
     public static Map<String, SecretKey> unwrapKeys(
             PlatformDecryptionKey platformKey,
             Map<String, WrappedKey> wrappedKeys)
-            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException {
+            throws NoSuchAlgorithmException, NoSuchPaddingException, BadPlatformKeyException,
+            InvalidKeyException, InvalidAlgorithmParameterException {
         HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
         Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
         int platformKeyGenerationId = platformKey.getGenerationId();
@@ -201,20 +202,10 @@
                         platformKey.getGenerationId()));
             }
 
-            try {
-                cipher.init(
-                        Cipher.UNWRAP_MODE,
-                        platformKey.getKey(),
-                        new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
-            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
-                Log.e(TAG,
-                        String.format(
-                                Locale.US,
-                                "Could not init Cipher to unwrap recoverable key with alias '%s'",
-                                alias),
-                        e);
-                continue;
-            }
+            cipher.init(
+                    Cipher.UNWRAP_MODE,
+                    platformKey.getKey(),
+                    new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
             SecretKey key;
             try {
                 key = (SecretKey) cipher.unwrap(
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index 2b1416e..f2e71b3 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -404,7 +404,7 @@
     /**
      * Updates the list of user secret types used for end-to-end encryption.
      * If no secret types are set, recovery snapshot will not be created.
-     * See {@code KeychainProtectionParams}
+     * See {@code KeyChainProtectionParams}
      *
      * @param userId The userId of the profile the application is running under.
      * @param uid The uid of the application.
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 62bb41e..3f93cc6 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,7 +17,7 @@
 package com.android.server.locksettings.recoverablekeystore.storage;
 
 import android.annotation.Nullable;
-import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.recovery.KeyChainSnapshot;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -34,12 +34,12 @@
  */
 public class RecoverySnapshotStorage {
     @GuardedBy("this")
-    private final SparseArray<KeychainSnapshot> mSnapshotByUid = new SparseArray<>();
+    private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>();
 
     /**
      * Sets the latest {@code snapshot} for the recovery agent {@code uid}.
      */
-    public synchronized void put(int uid, KeychainSnapshot snapshot) {
+    public synchronized void put(int uid, KeyChainSnapshot snapshot) {
         mSnapshotByUid.put(uid, snapshot);
     }
 
@@ -47,7 +47,7 @@
      * Returns the latest snapshot for the recovery agent {@code uid}, or null if none exists.
      */
     @Nullable
-    public synchronized KeychainSnapshot get(int uid) {
+    public synchronized KeyChainSnapshot get(int uid) {
         return mSnapshotByUid.get(uid);
     }
 
diff --git a/services/core/java/com/android/server/media/MediaSession2Record.java b/services/core/java/com/android/server/media/MediaSession2Record.java
new file mode 100644
index 0000000..b25eaa7
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSession2Record.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.MediaSession2;
+import android.media.SessionToken;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Records a {@link MediaSession2} and holds {@link MediaController2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSession2Record {
+    interface SessionDestroyedListener {
+        void onSessionDestroyed(MediaSession2Record record);
+    }
+
+    private static final String TAG = "Session2Record";
+    private static final boolean DEBUG = true; // TODO(jaewan): Change
+
+    private final Context mContext;
+    private final SessionDestroyedListener mSessionDestroyedListener;
+
+    // TODO(jaewan): Replace these with the mContext.getMainExecutor()
+    private final Handler mMainHandler;
+    private final Executor mMainExecutor;
+
+    private MediaController2 mController;
+    private ControllerCallback mControllerCallback;
+
+    private int mSessionPid;
+
+    /**
+     * Constructor
+     */
+    public MediaSession2Record(@NonNull Context context,
+            @NonNull SessionDestroyedListener listener) {
+        mContext = context;
+        mSessionDestroyedListener = listener;
+
+        mMainHandler = new Handler(Looper.getMainLooper());
+        mMainExecutor = (runnable) -> {
+            mMainHandler.post(runnable);
+        };
+    }
+
+    public int getSessionPid() {
+        return mSessionPid;
+    }
+
+    public Context getContext() {
+        return mContext;
+    }
+
+    @CallSuper
+    public void onSessionDestroyed() {
+        if (mController != null) {
+            mControllerCallback.destroy();
+            mController.release();
+            mController = null;
+        }
+        mSessionPid = 0;
+    }
+
+    /**
+     * Create session token and tell server that session is now active.
+     *
+     * @param sessionPid session's pid
+     * @return a token if successfully set, {@code null} if sanity check fails.
+     */
+    // TODO(jaewan): also add uid for multiuser support
+    @CallSuper
+    public @Nullable
+    SessionToken createSessionToken(int sessionPid, String packageName, String id,
+            IMediaSession2 sessionBinder) {
+        if (mController != null) {
+            if (mSessionPid != sessionPid) {
+                // A package uses the same id for session across the different process.
+                return null;
+            }
+            // If a session becomes inactive and then active again very quickly, previous 'inactive'
+            // may not have delivered yet. Check if it's the case and destroy controller before
+            // creating its session record to prevents getXXTokens() API from returning duplicated
+            // tokens.
+            // TODO(jaewan): Change this. If developer is really creating two sessions with the same
+            //               id, this will silently invalidate previous session and no way for
+            //               developers to know that.
+            //               Instead, keep the list of static session ids from our APIs.
+            //               Also change Controller2Impl.onConnectionChanged / getController.
+            //               Also clean up ControllerCallback#destroy().
+            if (DEBUG) {
+                Log.d(TAG, "Session is recreated almost immediately. " + this);
+            }
+            onSessionDestroyed();
+        }
+        mController = onCreateMediaController(packageName, id, sessionBinder);
+        mSessionPid = sessionPid;
+        return mController.getSessionToken();
+    }
+
+    /**
+     * Called when session becomes active and needs controller to listen session's activeness.
+     * <p>
+     * Should be overridden by subclasses to create token with its own extra information.
+     */
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(
+                SessionToken.TYPE_SESSION, packageName, id, null, sessionBinder);
+        return createMediaController(token);
+    }
+
+    final MediaController2 createMediaController(SessionToken token) {
+        mControllerCallback = new ControllerCallback();
+        return new MediaController2(mContext, token, mControllerCallback, mMainExecutor);
+    }
+
+    /**
+     * @return controller. Note that framework can only call oneway calls.
+     */
+    public SessionToken getToken() {
+        return mController == null ? null : mController.getSessionToken();
+    }
+
+    @Override
+    public String toString() {
+        return getToken() == null
+                ? "Token {null}"
+                : "SessionRecord {pid=" + mSessionPid + ", " + getToken().toString() + "}";
+    }
+
+    private class ControllerCallback extends MediaController2.ControllerCallback {
+        private final AtomicBoolean mIsActive = new AtomicBoolean(true);
+
+        // This is called on the main thread with no lock. So place ensure followings.
+        //   1. Don't touch anything in the parent class that needs synchronization.
+        //      All other APIs in the MediaSession2Record assumes that server would use them with
+        //      the lock hold.
+        //   2. This can be called after the controller registered is released.
+        @Override
+        public void onDisconnected() {
+            if (!mIsActive.get()) {
+                return;
+            }
+            if (DEBUG) {
+                Log.d(TAG, "onDisconnected, token=" + getToken());
+            }
+            mSessionDestroyedListener.onSessionDestroyed(MediaSession2Record.this);
+        }
+
+        // TODO(jaewan): Remove this API when we revisit createSessionToken()
+        public void destroy() {
+            mIsActive.set(false);
+        }
+    };
+}
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 06f4f5e..c9c7d04 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -28,13 +28,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.UserInfo;
 import android.database.ContentObserver;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
 import android.media.AudioSystem;
 import android.media.IAudioService;
+import android.media.IMediaSession2;
 import android.media.IRemoteVolumeController;
+import android.media.MediaLibraryService2;
+import android.media.MediaSessionService2;
+import android.media.SessionToken;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.ICallback;
 import android.media.session.IOnMediaKeyListener;
@@ -118,6 +124,24 @@
     // better way to handle this.
     private IRemoteVolumeController mRvc;
 
+    // MediaSession2 support
+    // TODO(jaewan): Support multi-user and managed profile.
+    // TODO(jaewan): Make it priority list for handling volume/media key.
+    private final List<MediaSession2Record> mSessions = new ArrayList<>();
+
+    private final MediaSession2Record.SessionDestroyedListener mSessionDestroyedListener =
+            (MediaSession2Record record) -> {
+                synchronized (mLock) {
+                    if (DEBUG) {
+                        Log.d(TAG, record.toString() + " becomes inactive");
+                    }
+                    record.onSessionDestroyed();
+                    if (!(record instanceof MediaSessionService2Record)) {
+                        mSessions.remove(record);
+                    }
+                }
+            };
+
     public MediaSessionService(Context context) {
         super(context);
         mSessionManagerImpl = new SessionManagerImpl();
@@ -158,6 +182,11 @@
                 PackageManager.FEATURE_LEANBACK);
 
         updateUser();
+
+        // TODO(jaewan): Query per users
+        // TODO(jaewan): Add listener to know changes in list of services.
+        //               Refer TvInputManagerService.registerBroadcastReceivers()
+        buildMediaSessionService2List();
     }
 
     private IAudioService getAudioService() {
@@ -411,6 +440,74 @@
         mHandler.postSessionsChanged(session.getUserId());
     }
 
+    private void buildMediaSessionService2List() {
+        if (DEBUG) {
+            Log.d(TAG, "buildMediaSessionService2List");
+        }
+
+        // TODO(jaewan): Query per users.
+        List<ResolveInfo> services = new ArrayList<>();
+        // If multiple actions are declared for a service, browser gets higher priority.
+        List<ResolveInfo> libraryServices = getContext().getPackageManager().queryIntentServices(
+                new Intent(MediaLibraryService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
+        if (libraryServices != null) {
+            services.addAll(libraryServices);
+        }
+        List<ResolveInfo> sessionServices = getContext().getPackageManager().queryIntentServices(
+                new Intent(MediaSessionService2.SERVICE_INTERFACE), PackageManager.GET_META_DATA);
+        if (sessionServices != null) {
+            services.addAll(sessionServices);
+        }
+        synchronized (mLock) {
+            mSessions.clear();
+            if (services == null) {
+                return;
+            }
+            for (int i = 0; i < services.size(); i++) {
+                if (services.get(i) == null || services.get(i).serviceInfo == null) {
+                    continue;
+                }
+                ServiceInfo serviceInfo = services.get(i).serviceInfo;
+                String id = (serviceInfo.metaData != null) ? serviceInfo.metaData.getString(
+                        MediaSessionService2.SERVICE_META_DATA) : null;
+                // Do basic sanity check
+                // TODO(jaewan): also santity check if it's protected with the system|privileged
+                //               permission
+                boolean conflict = (getSessionRecordLocked(serviceInfo.name, id) != null);
+                if (conflict) {
+                    Log.w(TAG, serviceInfo.packageName + " contains multiple"
+                            + " MediaSessionService2s declared in the manifest with"
+                            + " the same ID=" + id + ". Ignoring "
+                            + serviceInfo.packageName + "/" + serviceInfo.name);
+                } else {
+                    int type = (libraryServices.contains(services.get(i)))
+                            ? SessionToken.TYPE_LIBRARY_SERVICE : SessionToken.TYPE_SESSION_SERVICE;
+                    MediaSessionService2Record record =
+                            new MediaSessionService2Record(getContext(), mSessionDestroyedListener,
+                                    type, serviceInfo.packageName, serviceInfo.name, id);
+                    mSessions.add(record);
+                }
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG, "Found " + mSessions.size() + " session services");
+            for (int i = 0; i < mSessions.size(); i++) {
+                Log.d(TAG, "   " + mSessions.get(i).getToken());
+            }
+        }
+    }
+
+    MediaSession2Record getSessionRecordLocked(String packageName, String id) {
+        for (int i = 0; i < mSessions.size(); i++) {
+            MediaSession2Record record = mSessions.get(i);
+            if (record.getToken().getPackageName().equals(packageName)
+                    && record.getToken().getId().equals(id)) {
+                return record;
+            }
+        }
+        return null;
+    }
+
     private void enforcePackageName(String packageName, int uid) {
         if (TextUtils.isEmpty(packageName)) {
             throw new IllegalArgumentException("packageName may not be empty");
@@ -1312,6 +1409,57 @@
             }
         }
 
+        @Override
+        public Bundle createSessionToken(String sessionPackage, String id,
+                IMediaSession2 sessionBinder) throws RemoteException {
+            int uid = Binder.getCallingUid();
+            int pid = Binder.getCallingPid();
+
+            MediaSession2Record record;
+            SessionToken token;
+            // TODO(jaewan): Add sanity check for the token if calling package is from uid.
+            synchronized (mLock) {
+                record = getSessionRecordLocked(sessionPackage, id);
+                if (record == null) {
+                    record = new MediaSession2Record(getContext(), mSessionDestroyedListener);
+                    mSessions.add(record);
+                }
+                token = record.createSessionToken(pid, sessionPackage, id, sessionBinder);
+                if (token == null) {
+                    Log.d(TAG, "failed to create session token for " + sessionPackage
+                            + " from pid=" + pid + ". Previously " + record);
+                } else {
+                    Log.d(TAG, "session " + token + " is created");
+                }
+            }
+            return token == null ? null : token.toBundle();
+        }
+
+        // TODO(jaewan): Protect this API with permission
+        // TODO(jaewan): Add listeners for change in operations..
+        @Override
+        public List<Bundle> getSessionTokens(boolean activeSessionOnly,
+                boolean sessionServiceOnly) throws RemoteException {
+            List<Bundle> tokens = new ArrayList<>();
+            synchronized (mLock) {
+                for (int i = 0; i < mSessions.size(); i++) {
+                    MediaSession2Record record = mSessions.get(i);
+                    boolean isSessionService = (record instanceof MediaSessionService2Record);
+                    boolean isActive = record.getSessionPid() != 0;
+                    if ((!activeSessionOnly && isSessionService)
+                            || (!sessionServiceOnly && isActive)) {
+                        SessionToken token = record.getToken();
+                        if (token != null) {
+                            tokens.add(token.toBundle());
+                        } else {
+                            Log.wtf(TAG, "Null token for record=" + record);
+                        }
+                    }
+                }
+            }
+            return tokens;
+        }
+
         private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
                 final int uid) {
             String packageName = null;
diff --git a/services/core/java/com/android/server/media/MediaSessionService2Record.java b/services/core/java/com/android/server/media/MediaSessionService2Record.java
new file mode 100644
index 0000000..bd97dbc
--- /dev/null
+++ b/services/core/java/com/android/server/media/MediaSessionService2Record.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 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.media;
+
+import android.content.Context;
+import android.media.IMediaSession2;
+import android.media.MediaController2;
+import android.media.SessionToken;
+import android.media.MediaSessionService2;
+
+/**
+ * Records a {@link MediaSessionService2}.
+ * <p>
+ * Owner of this object should handle synchronization.
+ */
+class MediaSessionService2Record extends MediaSession2Record {
+    private static final boolean DEBUG = true; // TODO(jaewan): Modify
+    private static final String TAG = "SessionService2Record";
+
+    private final int mType;
+    private final String mServiceName;
+    private final SessionToken mToken;
+
+    public MediaSessionService2Record(Context context,
+            SessionDestroyedListener sessionDestroyedListener, int type,
+            String packageName, String serviceName, String id) {
+        super(context, sessionDestroyedListener);
+        mType = type;
+        mServiceName = serviceName;
+        mToken = new SessionToken(mType, packageName, id, mServiceName, null);
+    }
+
+    /**
+     * Overriden to change behavior of
+     * {@link #createSessionToken(int, String, String, IMediaSession2)}}.
+     */
+    @Override
+    MediaController2 onCreateMediaController(
+            String packageName, String id, IMediaSession2 sessionBinder) {
+        SessionToken token = new SessionToken(mType, packageName, id, mServiceName, sessionBinder);
+        return createMediaController(token);
+    }
+
+    /**
+     * @return token with no session binder information.
+     */
+    @Override
+    public SessionToken getToken() {
+        return mToken;
+    }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 2bd9cab..b4bc7f5 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -37,6 +37,7 @@
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.Set;
 
 public class NetworkPolicyLogger {
     static final String TAG = "NetworkPolicy";
@@ -62,6 +63,7 @@
     private static final int EVENT_TEMP_POWER_SAVE_WL_CHANGED = 10;
     private static final int EVENT_UID_FIREWALL_RULE_CHANGED = 11;
     private static final int EVENT_FIREWALL_CHAIN_ENABLED = 12;
+    private static final int EVENT_UPDATE_METERED_RESTRICTED_PKGS = 13;
 
     static final int NTWK_BLOCKED_POWER = 0;
     static final int NTWK_ALLOWED_NON_METERED = 1;
@@ -179,6 +181,14 @@
         }
     }
 
+    void meteredRestrictedPkgsChanged(Set<Integer> restrictedUids) {
+        synchronized (mLock) {
+            final String log = "Metered restricted uids: " + restrictedUids;
+            if (LOGD) Slog.d(TAG, log);
+            mEventsBuffer.event(log);
+        }
+    }
+
     void dumpLogs(IndentingPrintWriter pw) {
         synchronized (mLock) {
             pw.println();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 971ac8b..6490964 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -19,6 +19,8 @@
 import android.net.Network;
 import android.telephony.SubscriptionPlan;
 
+import java.util.Set;
+
 /**
  * Network Policy Manager local system service interface.
  *
@@ -71,4 +73,21 @@
      * Informs that admin data is loaded and available.
      */
     public abstract void onAdminDataAvailable();
+
+    /**
+     * Sets a list of packages which are restricted by admin from accessing metered data.
+     *
+     * @param packageNames the list of restricted packages.
+     * @param userId the userId in which {@param packagesNames} are restricted.
+     */
+    public abstract void setMeteredRestrictedPackages(
+            Set<String> packageNames, int userId);
+
+
+    /**
+     * Similar to {@link #setMeteredRestrictedPackages(Set, int)} but updates the restricted
+     * packages list asynchronously.
+     */
+    public abstract void setMeteredRestrictedPackagesAsync(
+            Set<String> packageNames, int userId);
 }
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e406d51..0e54768 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -232,6 +232,7 @@
 import java.util.Calendar;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -349,6 +350,7 @@
     private static final int MSG_POLICIES_CHANGED = 13;
     private static final int MSG_RESET_FIREWALL_RULES_BY_UID = 15;
     private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
+    private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
 
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
@@ -480,6 +482,13 @@
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseIntArray mNetIdToSubId = new SparseIntArray();
 
+    /**
+     * Indicates the uids restricted by admin from accessing metered data. It's a mapping from
+     * userId to restricted uids which belong to that user.
+     */
+    @GuardedBy("mUidRulesFirstLock")
+    private final SparseArray<Set<Integer>> mMeteredRestrictedUids = new SparseArray<>();
+
     private final RemoteCallbackList<INetworkPolicyListener>
             mListeners = new RemoteCallbackList<>();
 
@@ -898,6 +907,9 @@
                         // Remove any persistable state for the given user; both cleaning up after a
                         // USER_REMOVED, and one last sanity check during USER_ADDED
                         removeUserStateUL(userId, true);
+                        // Removing outside removeUserStateUL since that can also be called when
+                        // user resets app preferences.
+                        mMeteredRestrictedUids.remove(userId);
                         if (action == ACTION_USER_ADDED) {
                             // Add apps that are whitelisted by default.
                             addDefaultRestrictBackgroundWhitelistUidsUL(userId);
@@ -3137,6 +3149,15 @@
                 }
                 fout.decreaseIndent();
 
+                fout.println("Admin restricted uids for metered data:");
+                fout.increaseIndent();
+                size = mMeteredRestrictedUids.size();
+                for (int i = 0; i < size; ++i) {
+                    fout.print("u" + mMeteredRestrictedUids.keyAt(i) + ": ");
+                    fout.println(mMeteredRestrictedUids.valueAt(i));
+                }
+                fout.decreaseIndent();
+
                 mLogger.dumpLogs(fout);
             }
         }
@@ -3705,6 +3726,7 @@
         final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE);
         final int oldUidRules = mUidRules.get(uid, RULE_NONE);
         final boolean isForeground = isUidForegroundOnRestrictBackgroundUL(uid);
+        final boolean isRestrictedByAdmin = isRestrictedByAdminUL(uid);
 
         final boolean isBlacklisted = (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0;
         final boolean isWhitelisted = (uidPolicy & POLICY_ALLOW_METERED_BACKGROUND) != 0;
@@ -3712,7 +3734,9 @@
         int newRule = RULE_NONE;
 
         // First step: define the new rule based on user restrictions and foreground state.
-        if (isForeground) {
+        if (isRestrictedByAdmin) {
+            newRule = RULE_REJECT_METERED;
+        } else if (isForeground) {
             if (isBlacklisted || (mRestrictBackground && !isWhitelisted)) {
                 newRule = RULE_TEMPORARY_ALLOW_METERED;
             } else if (isWhitelisted) {
@@ -3732,6 +3756,7 @@
                     + ": isForeground=" +isForeground
                     + ", isBlacklisted=" + isBlacklisted
                     + ", isWhitelisted=" + isWhitelisted
+                    + ", isRestrictedByAdmin=" + isRestrictedByAdmin
                     + ", oldRule=" + uidRulesToString(oldRule)
                     + ", newRule=" + uidRulesToString(newRule)
                     + ", newUidRules=" + uidRulesToString(newUidRules)
@@ -3767,13 +3792,13 @@
                 if (!isWhitelisted) {
                     setMeteredNetworkWhitelist(uid, false);
                 }
-                if (isBlacklisted) {
+                if (isBlacklisted || isRestrictedByAdmin) {
                     setMeteredNetworkBlacklist(uid, true);
                 }
             } else if (hasRule(newRule, RULE_REJECT_METERED)
                     || hasRule(oldRule, RULE_REJECT_METERED)) {
                 // Flip state because app was explicitly added or removed to blacklist.
-                setMeteredNetworkBlacklist(uid, isBlacklisted);
+                setMeteredNetworkBlacklist(uid, (isBlacklisted || isRestrictedByAdmin));
                 if (hasRule(oldRule, RULE_REJECT_METERED) && isWhitelisted) {
                     // Since blacklist prevails over whitelist, we need to handle the special case
                     // where app is whitelisted and blacklisted at the same time (although such
@@ -3790,6 +3815,7 @@
                         + ": foreground=" + isForeground
                         + ", whitelisted=" + isWhitelisted
                         + ", blacklisted=" + isBlacklisted
+                        + ", isRestrictedByAdmin=" + isRestrictedByAdmin
                         + ", newRule=" + uidRulesToString(newUidRules)
                         + ", oldRule=" + uidRulesToString(oldUidRules));
             }
@@ -4102,6 +4128,12 @@
                     mListeners.finishBroadcast();
                     return true;
                 }
+                case MSG_METERED_RESTRICTED_PACKAGES_CHANGED: {
+                    final int userId = msg.arg1;
+                    final Set<String> packageNames = (Set<String>) msg.obj;
+                    setMeteredRestrictedPackagesInternal(packageNames, userId);
+                    return true;
+                }
                 default: {
                     return false;
                 }
@@ -4605,6 +4637,42 @@
         public void onAdminDataAvailable() {
             mAdminDataAvailableLatch.countDown();
         }
+
+        @Override
+        public void setMeteredRestrictedPackages(Set<String> packageNames, int userId) {
+            setMeteredRestrictedPackagesInternal(packageNames, userId);
+        }
+
+        @Override
+        public void setMeteredRestrictedPackagesAsync(Set<String> packageNames, int userId) {
+            mHandler.obtainMessage(MSG_METERED_RESTRICTED_PACKAGES_CHANGED,
+                    userId, 0, packageNames).sendToTarget();
+        }
+    }
+
+    private void setMeteredRestrictedPackagesInternal(Set<String> packageNames, int userId) {
+        synchronized (mUidRulesFirstLock) {
+            final Set<Integer> newRestrictedUids = new ArraySet<>();
+            for (String packageName : packageNames) {
+                final int uid = getUidForPackage(packageName, userId);
+                if (uid >= 0) {
+                    newRestrictedUids.add(uid);
+                }
+            }
+            final Set<Integer> oldRestrictedUids = mMeteredRestrictedUids.get(userId);
+            mMeteredRestrictedUids.put(userId, newRestrictedUids);
+            handleRestrictedPackagesChangeUL(oldRestrictedUids, newRestrictedUids);
+            mLogger.meteredRestrictedPkgsChanged(newRestrictedUids);
+        }
+    }
+
+    private int getUidForPackage(String packageName, int userId) {
+        try {
+            return mContext.getPackageManager().getPackageUidAsUser(packageName,
+                    PackageManager.MATCH_KNOWN_PACKAGES, userId);
+        } catch (NameNotFoundException e) {
+            return -1;
+        }
     }
 
     private int parseSubId(NetworkState state) {
@@ -4642,6 +4710,32 @@
         }
     }
 
+    private void handleRestrictedPackagesChangeUL(Set<Integer> oldRestrictedUids,
+            Set<Integer> newRestrictedUids) {
+        if (oldRestrictedUids == null) {
+            for (int uid : newRestrictedUids) {
+                updateRulesForDataUsageRestrictionsUL(uid);
+            }
+            return;
+        }
+        for (int uid : oldRestrictedUids) {
+            if (!newRestrictedUids.contains(uid)) {
+                updateRulesForDataUsageRestrictionsUL(uid);
+            }
+        }
+        for (int uid : newRestrictedUids) {
+            if (!oldRestrictedUids.contains(uid)) {
+                updateRulesForDataUsageRestrictionsUL(uid);
+            }
+        }
+    }
+
+    private boolean isRestrictedByAdminUL(int uid) {
+        final Set<Integer> restrictedUids = mMeteredRestrictedUids.get(
+                UserHandle.getUserId(uid));
+        return restrictedUids != null && restrictedUids.contains(uid);
+    }
+
     private static boolean hasRule(int uidRules, int rule) {
         return (uidRules & rule) != 0;
     }
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 239ddbe..7165e60 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.net.watchlist;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetdEventCallback;
@@ -210,6 +211,12 @@
         return stopWatchlistLoggingImpl();
     }
 
+    @Nullable
+    @Override
+    public byte[] getWatchlistConfigHash() {
+        return mConfig.getWatchlistConfigHash();
+    }
+
     private void enforceWatchlistLoggingPermission() {
         final int uid = Binder.getCallingUid();
         if (uid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 2f6618f..39b7c7c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -137,6 +137,7 @@
 import android.service.notification.NotificationRecordProto;
 import android.service.notification.NotificationServiceDumpProto;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.SnoozeCriterion;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
@@ -329,6 +330,7 @@
     final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>();
     final ArrayList<ToastRecord> mToastQueue = new ArrayList<>();
     final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>();
+    final ArrayMap<Integer, ArrayList<NotifyingApp>> mRecentApps = new ArrayMap<>();
 
     // The last key in this list owns the hardware.
     ArrayList<String> mLights = new ArrayList<>();
@@ -2110,6 +2112,16 @@
         }
 
         @Override
+        public ParceledListSlice<NotifyingApp> getRecentNotifyingAppsForUser(int userId) {
+            checkCallerIsSystem();
+            synchronized (mNotificationLock) {
+                List<NotifyingApp> apps = new ArrayList<>(
+                        mRecentApps.getOrDefault(userId, new ArrayList<>()));
+                return new ParceledListSlice<>(apps);
+            }
+        }
+
+        @Override
         public void clearData(String packageName, int uid, boolean fromApp) throws RemoteException {
             checkCallerIsSystem();
 
@@ -4096,6 +4108,10 @@
 
                     mNotificationsByKey.put(n.getKey(), r);
 
+                    if (!r.isUpdate) {
+                        logRecentLocked(r);
+                    }
+
                     // Ensure if this is a foreground service that the proper additional
                     // flags are set.
                     if ((notification.flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
@@ -4153,6 +4169,38 @@
     }
 
     /**
+     * Keeps the last 5 packages that have notified, by user.
+     */
+    @GuardedBy("mNotificationLock")
+    @VisibleForTesting
+    protected void logRecentLocked(NotificationRecord r) {
+        if (r.isUpdate) {
+            return;
+        }
+        ArrayList<NotifyingApp> recentAppsForUser =
+                mRecentApps.getOrDefault(r.getUser().getIdentifier(), new ArrayList<>(6));
+        NotifyingApp na = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        // A new notification gets an app moved to the front of the list
+        for (int i = recentAppsForUser.size() - 1; i >= 0; i--) {
+            NotifyingApp naExisting = recentAppsForUser.get(i);
+            if (na.getPackage().equals(naExisting.getPackage())
+                    && na.getUid() == naExisting.getUid()) {
+                recentAppsForUser.remove(i);
+                break;
+            }
+        }
+        // time is always increasing, so always add to the front of the list
+        recentAppsForUser.add(0, na);
+        if (recentAppsForUser.size() > 5) {
+            recentAppsForUser.remove(recentAppsForUser.size() -1);
+        }
+        mRecentApps.put(r.getUser().getIdentifier(), recentAppsForUser);
+    }
+
+    /**
      * Ensures that grouped notification receive their special treatment.
      *
      * <p>Cancels group children if the new notification causes a group to lose
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 253d4f5..321af43 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -169,8 +169,9 @@
         }
 
         final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId);
-        updateAllOverlaysForTarget(packageName, userId, targetPackage);
-        mListener.onOverlaysChanged(packageName, userId);
+        if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
@@ -210,7 +211,9 @@
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAllOverlaysForTarget(packageName, userId, null);
+        if (updateAllOverlaysForTarget(packageName, userId, null)) {
+            mListener.onOverlaysChanged(packageName, userId);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index c059b37..7d00423 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -230,7 +230,7 @@
         }
 
         mItems.remove(moveIdx);
-        final int newParentIdx = select(newParentPackageName, userId);
+        final int newParentIdx = select(newParentPackageName, userId) + 1;
         mItems.add(newParentIdx, itemToMove);
         return moveIdx != newParentIdx;
     }
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 1717b3d..14995b3 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -560,7 +560,6 @@
         private boolean startShortcutIntentsAsPublisher(@NonNull Intent[] intents,
                 @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
             final int code;
-            final long ident = injectClearCallingIdentity();
             try {
                 code = mActivityManagerInternal.startActivitiesAsPackage(publisherPackage,
                         userId, intents, startActivityOptions);
@@ -575,8 +574,6 @@
                     Slog.d(TAG, "SecurityException while launching intent", e);
                 }
                 return false;
-            } finally {
-                injectRestoreCallingIdentity(ident);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index faf6114..42b6946 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,6 +24,8 @@
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
+import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
@@ -108,6 +110,8 @@
 import static com.android.server.pm.PackageManagerServiceUtils.getCompressedFiles;
 import static com.android.server.pm.PackageManagerServiceUtils.getLastModifiedTime;
 import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasCertificate;
+import static com.android.server.pm.PackageManagerServiceUtils.signingDetailsHasSha256Certificate;
 import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
 import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS;
@@ -5421,13 +5425,13 @@
                     if (isCallerInstantApp) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
-                    s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+                    s1 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
                     if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
-                    s1 = ps.signatures.mSignatures;
+                    s1 = ps.signatures.mSigningDetails.signatures;
                 } else {
                     return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                 }
@@ -5440,13 +5444,13 @@
                     if (isCallerInstantApp) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
-                    s2 = ((SharedUserSetting)obj).signatures.mSignatures;
+                    s2 = ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
                 } else if (obj instanceof PackageSetting) {
                     final PackageSetting ps = (PackageSetting) obj;
                     if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
                         return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                     }
-                    s2 = ps.signatures.mSignatures;
+                    s2 = ps.signatures.mSigningDetails.signatures;
                 } else {
                     return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
                 }
@@ -5457,6 +5461,73 @@
         }
     }
 
+    @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+
+        synchronized (mPackages) {
+            final PackageParser.Package p = mPackages.get(packageName);
+            if (p == null || p.mExtras == null) {
+                return false;
+            }
+            final int callingUid = Binder.getCallingUid();
+            final int callingUserId = UserHandle.getUserId(callingUid);
+            final PackageSetting ps = (PackageSetting) p.mExtras;
+            if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                return false;
+            }
+            switch (type) {
+                case CERT_INPUT_RAW_X509:
+                    return signingDetailsHasCertificate(certificate, p.mSigningDetails);
+                case CERT_INPUT_SHA256:
+                    return signingDetailsHasSha256Certificate(certificate, p.mSigningDetails);
+                default:
+                    return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean hasUidSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        final int callingUid = Binder.getCallingUid();
+        final int callingUserId = UserHandle.getUserId(callingUid);
+        // Map to base uids.
+        uid = UserHandle.getAppId(uid);
+        // reader
+        synchronized (mPackages) {
+            final PackageParser.SigningDetails signingDetails;
+            final Object obj = mSettings.getUserIdLPr(uid);
+            if (obj != null) {
+                if (obj instanceof SharedUserSetting) {
+                    final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
+                    if (isCallerInstantApp) {
+                        return false;
+                    }
+                    signingDetails = ((SharedUserSetting)obj).signatures.mSigningDetails;
+                } else if (obj instanceof PackageSetting) {
+                    final PackageSetting ps = (PackageSetting) obj;
+                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
+                        return false;
+                    }
+                    signingDetails = ps.signatures.mSigningDetails;
+                } else {
+                    return false;
+                }
+            } else {
+                return false;
+            }
+            switch (type) {
+                case CERT_INPUT_RAW_X509:
+                    return signingDetailsHasCertificate(certificate, signingDetails);
+                case CERT_INPUT_SHA256:
+                    return signingDetailsHasSha256Certificate(certificate, signingDetails);
+                default:
+                    return false;
+            }
+        }
+    }
+
     /**
      * This method should typically only be used when granting or revoking
      * permissions, since the app may immediately restart after this call.
@@ -8233,19 +8304,15 @@
                 && ps.timeStamp == lastModifiedTime
                 && !isCompatSignatureUpdateNeeded(pkg)
                 && !isRecoverSignatureUpdateNeeded(pkg)) {
-            if (ps.signatures.mSignatures != null
-                    && ps.signatures.mSignatures.length != 0
-                    && ps.signatures.mSignatureSchemeVersion != SignatureSchemeVersion.UNKNOWN) {
+            if (ps.signatures.mSigningDetails.signatures != null
+                    && ps.signatures.mSigningDetails.signatures.length != 0
+                    && ps.signatures.mSigningDetails.signatureSchemeVersion
+                            != SignatureSchemeVersion.UNKNOWN) {
                 // Optimization: reuse the existing cached signing data
                 // if the package appears to be unchanged.
-                try {
-                    pkg.mSigningDetails = new PackageParser.SigningDetails(ps.signatures.mSignatures,
-                            ps.signatures.mSignatureSchemeVersion);
-                    return;
-                } catch (CertificateException e) {
-                    Slog.e(TAG, "Attempt to read public keys from persisted signatures failed for "
-                                    + ps.name, e);
-                }
+                pkg.mSigningDetails =
+                        new PackageParser.SigningDetails(ps.signatures.mSigningDetails);
+                return;
             }
 
             Slog.w(TAG, "PackageSetting for " + ps.name
@@ -8573,8 +8640,9 @@
         if (scanSystemPartition && !isSystemPkgUpdated && pkgAlreadyExists
                 && !pkgSetting.isSystem()) {
             // if the signatures don't match, wipe the installed application and its data
-            if (compareSignatures(pkgSetting.signatures.mSignatures, pkg.mSigningDetails.signatures)
-                    != PackageManager.SIGNATURE_MATCH) {
+            if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
+                    pkg.mSigningDetails.signatures)
+                            != PackageManager.SIGNATURE_MATCH) {
                 logCriticalInfo(Log.WARN,
                         "System package signature mismatch;"
                         + " name: " + pkgSetting.name);
@@ -9936,14 +10004,14 @@
             if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
                 // We just determined the app is signed correctly, so bring
                 // over the latest parsed certs.
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
             } else {
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                             "Package " + pkg.packageName + " upgrade keys do not match the "
                                     + "previously installed version");
                 } else {
-                    pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                    pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
                     String msg = "System package " + pkg.packageName
                             + " signature changed; retaining data.";
                     reportSettingsProblem(Log.WARN, msg);
@@ -9963,21 +10031,22 @@
                 }
                 // We just determined the app is signed correctly, so bring
                 // over the latest parsed certs.
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
             } catch (PackageManagerException e) {
                 if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                     throw e;
                 }
                 // The signature has changed, but this package is in the system
                 // image...  let's recover!
-                pkgSetting.signatures.mSignatures = pkg.mSigningDetails.signatures;
+                pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
                 // However...  if this package is part of a shared user, but it
                 // doesn't match the signature of the shared user, let's fail.
                 // What this means is that you can't change the signatures
                 // associated with an overall shared user, which doesn't seem all
                 // that unreasonable.
                 if (signatureCheckPs.sharedUser != null) {
-                    if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+                    if (compareSignatures(
+                            signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
                             pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
                         throw new PackageManagerException(
                                 INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
@@ -10804,9 +10873,12 @@
                 if (sharedUserSetting != null && sharedUserSetting.isPrivileged()) {
                     // Exempt SharedUsers signed with the platform key.
                     PackageSetting platformPkgSetting = mSettings.mPackages.get("android");
-                    if ((platformPkgSetting.signatures.mSignatures != null) &&
-                            (compareSignatures(platformPkgSetting.signatures.mSignatures,
-                                pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH)) {
+                    if ((platformPkgSetting.signatures.mSigningDetails
+                            != PackageParser.SigningDetails.UNKNOWN)
+                            && (compareSignatures(
+                                    platformPkgSetting.signatures.mSigningDetails.signatures,
+                                    pkg.mSigningDetails.signatures)
+                                            != PackageManager.SIGNATURE_MATCH)) {
                         throw new PackageManagerException("Apps that share a user with a " +
                                 "privileged app must themselves be marked as privileged. " +
                                 pkg.packageName + " shares privileged user " +
@@ -14248,9 +14320,10 @@
             Object obj = mSettings.getUserIdLPr(callingUid);
             if (obj != null) {
                 if (obj instanceof SharedUserSetting) {
-                    callerSignature = ((SharedUserSetting)obj).signatures.mSignatures;
+                    callerSignature =
+                            ((SharedUserSetting)obj).signatures.mSigningDetails.signatures;
                 } else if (obj instanceof PackageSetting) {
-                    callerSignature = ((PackageSetting)obj).signatures.mSignatures;
+                    callerSignature = ((PackageSetting)obj).signatures.mSigningDetails.signatures;
                 } else {
                     throw new SecurityException("Bad object " + obj + " for uid " + callingUid);
                 }
@@ -14262,7 +14335,7 @@
             // not signed with the same cert as the caller.
             if (installerPackageSetting != null) {
                 if (compareSignatures(callerSignature,
-                        installerPackageSetting.signatures.mSignatures)
+                        installerPackageSetting.signatures.mSigningDetails.signatures)
                         != PackageManager.SIGNATURE_MATCH) {
                     throw new SecurityException(
                             "Caller does not have same cert as new installer package "
@@ -14279,7 +14352,7 @@
                 // okay to change it.
                 if (setting != null) {
                     if (compareSignatures(callerSignature,
-                            setting.signatures.mSignatures)
+                            setting.signatures.mSigningDetails.signatures)
                             != PackageManager.SIGNATURE_MATCH) {
                         throw new SecurityException(
                                 "Caller does not have same cert as old installer package "
@@ -16787,7 +16860,8 @@
                                     sourcePackageSetting, scanFlags))) {
                         sigsOk = ksms.checkUpgradeKeySetLocked(sourcePackageSetting, pkg);
                     } else {
-                        sigsOk = compareSignatures(sourcePackageSetting.signatures.mSignatures,
+                        sigsOk = compareSignatures(
+                                sourcePackageSetting.signatures.mSigningDetails.signatures,
                                 pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH;
                     }
                     if (!sigsOk) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 7b96ca6..021c4b8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -54,6 +54,7 @@
 import android.system.Os;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.PackageUtils;
 import android.util.Slog;
 import android.util.jar.StrictJarFile;
 import android.util.proto.ProtoOutputStream;
@@ -74,6 +75,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.PrintWriter;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.CertificateException;
 import java.text.SimpleDateFormat;
@@ -509,7 +512,7 @@
     private static boolean matchSignaturesCompat(String packageName,
             PackageSignatures packageSignatures, PackageParser.SigningDetails parsedSignatures) {
         ArraySet<Signature> existingSet = new ArraySet<Signature>();
-        for (Signature sig : packageSignatures.mSignatures) {
+        for (Signature sig : packageSignatures.mSigningDetails.signatures) {
             existingSet.add(sig);
         }
         ArraySet<Signature> scannedCompatSet = new ArraySet<Signature>();
@@ -526,7 +529,7 @@
         // make sure the expanded scanned set contains all signatures in the existing one
         if (scannedCompatSet.equals(existingSet)) {
             // migrate the old signatures to the new scheme
-            packageSignatures.assignSignatures(parsedSignatures);
+            packageSignatures.mSigningDetails = parsedSignatures;
             return true;
         }
         return false;
@@ -561,8 +564,8 @@
         try {
             PackageParser.collectCertificates(disabledPkgSetting.pkg,
                     PackageParser.PARSE_IS_SYSTEM_DIR);
-            if (compareSignatures(pkgSetting.signatures.mSignatures,
-                        disabledPkgSetting.signatures.mSignatures)
+            if (compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
+                        disabledPkgSetting.signatures.mSigningDetails.signatures)
                     != PackageManager.SIGNATURE_MATCH) {
                 logCriticalInfo(Log.ERROR, "Updated system app mismatches cert on /system: " +
                         pkgSetting.name);
@@ -576,6 +579,69 @@
         return true;
     }
 
+
+    /**
+     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
+     * {@code SigningDetails} with multiple signing certificates.
+     * @param certificate certificate to check for membership
+     * @param signingDetails signing certificates record whose members are to be searched
+     * @return true if {@code certificate} is in {@code signingDetails}
+     */
+    public static boolean signingDetailsHasCertificate(
+            byte[] certificate, PackageParser.SigningDetails signingDetails) {
+        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+            return false;
+        }
+        Signature signature = new Signature(certificate);
+        if (signingDetails.hasPastSigningCertificates()) {
+            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+                if (signingDetails.pastSigningCertificates[i].equals(signature)) {
+                    return true;
+                }
+            }
+        } else {
+            // no signing history, just check the current signer
+            if (signingDetails.signatures.length == 1
+                    && signingDetails.signatures[0].equals(signature)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks the signing certificates to see if the provided certificate is a member.  Invalid for
+     * {@code SigningDetails} with multiple signing certificaes.
+     * @param sha256Certificate certificate to check for membership
+     * @param signingDetails signing certificates record whose members are to be searched
+     * @return true if {@code certificate} is in {@code signingDetails}
+     */
+    public static boolean signingDetailsHasSha256Certificate(
+            byte[] sha256Certificate, PackageParser.SigningDetails signingDetails ) {
+        if (signingDetails == PackageParser.SigningDetails.UNKNOWN) {
+            return false;
+        }
+        if (signingDetails.hasPastSigningCertificates()) {
+            for (int i = 0; i < signingDetails.pastSigningCertificates.length; i++) {
+                byte[] digest = PackageUtils.computeSha256DigestBytes(
+                        signingDetails.pastSigningCertificates[i].toByteArray());
+                if (Arrays.equals(sha256Certificate, digest)) {
+                    return true;
+                }
+            }
+        } else {
+            // no signing history, just check the current signer
+            if (signingDetails.signatures.length == 1) {
+                byte[] digest = PackageUtils.computeSha256DigestBytes(
+                        signingDetails.signatures[0].toByteArray());
+                if (Arrays.equals(sha256Certificate, digest)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /** Returns true to force apk verification if the updated package (in /data) is a priv app. */
     static boolean isApkVerificationForced(@Nullable PackageSetting disabledPs) {
         return disabledPs != null && disabledPs.isPrivileged() &&
@@ -593,9 +659,9 @@
             throws PackageManagerException {
         final String packageName = pkgSetting.name;
         boolean compatMatch = false;
-        if (pkgSetting.signatures.mSignatures != null) {
+        if (pkgSetting.signatures.mSigningDetails.signatures != null) {
             // Already existing package. Make sure signatures match
-            boolean match = compareSignatures(pkgSetting.signatures.mSignatures,
+            boolean match = compareSignatures(pkgSetting.signatures.mSigningDetails.signatures,
                     parsedSignatures.signatures)
                     == PackageManager.SIGNATURE_MATCH;
             if (!match && compareCompat) {
@@ -605,7 +671,7 @@
             }
             if (!match && compareRecover) {
                 match = matchSignaturesRecover(
-                        packageName, pkgSetting.signatures.mSignatures,
+                        packageName, pkgSetting.signatures.mSigningDetails.signatures,
                         parsedSignatures.signatures);
             }
 
@@ -620,17 +686,21 @@
             }
         }
         // Check for shared user signatures
-        if (pkgSetting.sharedUser != null && pkgSetting.sharedUser.signatures.mSignatures != null) {
+        if (pkgSetting.sharedUser != null
+                && pkgSetting.sharedUser.signatures.mSigningDetails.signatures != null) {
             // Already existing package. Make sure signatures match
-            boolean match = compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
-                    parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
+            boolean match =
+                    compareSignatures(
+                            pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
+                            parsedSignatures.signatures) == PackageManager.SIGNATURE_MATCH;
             if (!match && compareCompat) {
                 match = matchSignaturesCompat(
                         packageName, pkgSetting.sharedUser.signatures, parsedSignatures);
             }
             if (!match && compareRecover) {
                 match = matchSignaturesRecover(packageName,
-                        pkgSetting.sharedUser.signatures.mSignatures, parsedSignatures.signatures);
+                        pkgSetting.sharedUser.signatures.mSigningDetails.signatures,
+                        parsedSignatures.signatures);
                 compatMatch |= match;
             }
             if (!match) {
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index e3c4c43..18356c5 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -233,7 +233,7 @@
     }
 
     public Signature[] getSignatures() {
-        return signatures.mSignatures;
+        return signatures.mSigningDetails.signatures;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index d567d5c..d471fc8 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -22,91 +22,148 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.annotation.NonNull;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
 import android.util.Log;
 
 import java.io.IOException;
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 
 class PackageSignatures {
-    Signature[] mSignatures;
-    @SignatureSchemeVersion int mSignatureSchemeVersion;
+
+    @NonNull PackageParser.SigningDetails mSigningDetails;
 
     PackageSignatures(PackageSignatures orig) {
-        if (orig != null && orig.mSignatures != null) {
-            mSignatures = orig.mSignatures.clone();
-            mSignatureSchemeVersion = orig.mSignatureSchemeVersion;
+        if (orig != null && orig.mSigningDetails != PackageParser.SigningDetails.UNKNOWN) {
+            mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+        } else {
+            mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
         }
     }
 
     PackageSignatures(PackageParser.SigningDetails signingDetails) {
-        assignSignatures(signingDetails);
+        mSigningDetails = signingDetails;
     }
 
     PackageSignatures() {
+        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
     }
 
     void writeXml(XmlSerializer serializer, String tagName,
-            ArrayList<Signature> pastSignatures) throws IOException {
-        if (mSignatures == null) {
+            ArrayList<Signature> writtenSignatures) throws IOException {
+        if (mSigningDetails.signatures == null) {
             return;
         }
         serializer.startTag(null, tagName);
-        serializer.attribute(null, "count",
-                Integer.toString(mSignatures.length));
-        serializer.attribute(null, "schemeVersion", Integer.toString(mSignatureSchemeVersion));
-        for (int i=0; i<mSignatures.length; i++) {
-            serializer.startTag(null, "cert");
-            final Signature sig = mSignatures[i];
-            final int sigHash = sig.hashCode();
-            final int numPast = pastSignatures.size();
-            int j;
-            for (j=0; j<numPast; j++) {
-                Signature pastSig = pastSignatures.get(j);
-                if (pastSig.hashCode() == sigHash && pastSig.equals(sig)) {
-                    serializer.attribute(null, "index", Integer.toString(j));
-                    break;
-                }
-            }
-            if (j >= numPast) {
-                pastSignatures.add(sig);
-                serializer.attribute(null, "index", Integer.toString(numPast));
-                serializer.attribute(null, "key", sig.toCharsString());
-            }
-            serializer.endTag(null, "cert");
+        serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length));
+        serializer.attribute(null, "schemeVersion",
+                Integer.toString(mSigningDetails.signatureSchemeVersion));
+        writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null);
+
+        // if we have past signer certificate information, write it out
+        if (mSigningDetails.pastSigningCertificates != null) {
+            serializer.startTag(null, "pastSigs");
+            serializer.attribute(null, "count",
+                    Integer.toString(mSigningDetails.pastSigningCertificates.length));
+            writeCertsListXml(
+                    serializer, writtenSignatures, mSigningDetails.pastSigningCertificates,
+                    mSigningDetails.pastSigningCertificatesFlags);
+            serializer.endTag(null, "pastSigs");
         }
         serializer.endTag(null, tagName);
     }
 
-    void readXml(XmlPullParser parser, ArrayList<Signature> pastSignatures)
+    private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
+            Signature[] signatures, int[] flags) throws IOException {
+        for (int i=0; i<signatures.length; i++) {
+            serializer.startTag(null, "cert");
+            final Signature sig = signatures[i];
+            final int sigHash = sig.hashCode();
+            final int numWritten = writtenSignatures.size();
+            int j;
+            for (j=0; j<numWritten; j++) {
+                Signature writtenSig = writtenSignatures.get(j);
+                if (writtenSig.hashCode() == sigHash && writtenSig.equals(sig)) {
+                    serializer.attribute(null, "index", Integer.toString(j));
+                    break;
+                }
+            }
+            if (j >= numWritten) {
+                writtenSignatures.add(sig);
+                serializer.attribute(null, "index", Integer.toString(numWritten));
+                serializer.attribute(null, "key", sig.toCharsString());
+            }
+            if (flags != null) {
+                serializer.attribute(null, "flags", Integer.toString(flags[i]));
+            }
+            serializer.endTag(null, "cert");
+        }
+    }
+
+    void readXml(XmlPullParser parser, ArrayList<Signature> readSignatures)
             throws IOException, XmlPullParserException {
+        PackageParser.SigningDetails.Builder builder =
+                new PackageParser.SigningDetails.Builder();
+
         String countStr = parser.getAttributeValue(null, "count");
         if (countStr == null) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
-                    "Error in package manager settings: <signatures> has"
+                    "Error in package manager settings: <sigs> has"
                        + " no count at " + parser.getPositionDescription());
             XmlUtils.skipCurrentTag(parser);
         }
+        final int count = Integer.parseInt(countStr);
+
         String schemeVersionStr = parser.getAttributeValue(null, "schemeVersion");
+        int signatureSchemeVersion;
         if (schemeVersionStr == null) {
             PackageManagerService.reportSettingsProblem(Log.WARN,
-                    "Error in package manager settings: <signatures> has no schemeVersion at "
+                    "Error in package manager settings: <sigs> has no schemeVersion at "
                         + parser.getPositionDescription());
-            mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
+            signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
         } else {
-            mSignatureSchemeVersion = Integer.parseInt(countStr);
+            signatureSchemeVersion = Integer.parseInt(schemeVersionStr);
         }
-        final int count = Integer.parseInt(countStr);
-        mSignatures = new Signature[count];
+        builder.setSignatureSchemeVersion(signatureSchemeVersion);
+        Signature[] signatures = new Signature[count];
+        int pos = readCertsListXml(parser, readSignatures, signatures, null, builder);
+        builder.setSignatures(signatures);
+        if (pos < count) {
+            // Should never happen -- there is an error in the written
+            // settings -- but if it does we don't want to generate
+            // a bad array.
+            Signature[] newSigs = new Signature[pos];
+            System.arraycopy(signatures, 0, newSigs, 0, pos);
+            builder = builder.setSignatures(newSigs);
+            PackageManagerService.reportSettingsProblem(Log.WARN,
+                    "Error in package manager settings: <sigs> count does not match number of "
+                            + " <cert> entries" + parser.getPositionDescription());
+        }
+
+        try {
+            mSigningDetails = builder.build();
+        } catch (CertificateException e) {
+            PackageManagerService.reportSettingsProblem(Log.WARN,
+                    "Error in package manager settings: <sigs> "
+                            + "unable to convert certificate(s) to public key(s).");
+            mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+        }
+    }
+
+    private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
+            Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder)
+            throws IOException, XmlPullParserException {
+        int count = signatures.length;
         int pos = 0;
 
         int outerDepth = parser.getDepth();
         int type;
         while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
-               && (type != XmlPullParser.END_TAG
-                       || parser.getDepth() > outerDepth)) {
+                && (type != XmlPullParser.END_TAG
+                || parser.getDepth() > outerDepth)) {
             if (type == XmlPullParser.END_TAG
                     || type == XmlPullParser.TEXT) {
                 continue;
@@ -121,83 +178,128 @@
                             int idx = Integer.parseInt(index);
                             String key = parser.getAttributeValue(null, "key");
                             if (key == null) {
-                                if (idx >= 0 && idx < pastSignatures.size()) {
-                                    Signature sig = pastSignatures.get(idx);
+                                if (idx >= 0 && idx < readSignatures.size()) {
+                                    Signature sig = readSignatures.get(idx);
                                     if (sig != null) {
-                                        mSignatures[pos] = pastSignatures.get(idx);
+                                        signatures[pos] = readSignatures.get(idx);
                                         pos++;
                                     } else {
                                         PackageManagerService.reportSettingsProblem(Log.WARN,
                                                 "Error in package manager settings: <cert> "
-                                                   + "index " + index + " is not defined at "
-                                                   + parser.getPositionDescription());
+                                                        + "index " + index + " is not defined at "
+                                                        + parser.getPositionDescription());
                                     }
                                 } else {
                                     PackageManagerService.reportSettingsProblem(Log.WARN,
                                             "Error in package manager settings: <cert> "
-                                               + "index " + index + " is out of bounds at "
-                                               + parser.getPositionDescription());
+                                                    + "index " + index + " is out of bounds at "
+                                                    + parser.getPositionDescription());
                                 }
                             } else {
-                                while (pastSignatures.size() <= idx) {
-                                    pastSignatures.add(null);
+                                while (readSignatures.size() <= idx) {
+                                    readSignatures.add(null);
                                 }
                                 Signature sig = new Signature(key);
-                                pastSignatures.set(idx, sig);
-                                mSignatures[pos] = sig;
+                                readSignatures.set(idx, sig);
+                                signatures[pos] = sig;
                                 pos++;
                             }
                         } catch (NumberFormatException e) {
                             PackageManagerService.reportSettingsProblem(Log.WARN,
                                     "Error in package manager settings: <cert> "
-                                       + "index " + index + " is not a number at "
-                                       + parser.getPositionDescription());
+                                            + "index " + index + " is not a number at "
+                                            + parser.getPositionDescription());
                         } catch (IllegalArgumentException e) {
                             PackageManagerService.reportSettingsProblem(Log.WARN,
                                     "Error in package manager settings: <cert> "
-                                       + "index " + index + " has an invalid signature at "
-                                       + parser.getPositionDescription() + ": "
-                                       + e.getMessage());
+                                            + "index " + index + " has an invalid signature at "
+                                            + parser.getPositionDescription() + ": "
+                                            + e.getMessage());
+                        }
+
+                        if (flags != null) {
+                            String flagsStr = parser.getAttributeValue(null, "flags");
+                            if (flagsStr != null) {
+                                try {
+                                    flags[pos] = Integer.parseInt(flagsStr);
+                                } catch (NumberFormatException e) {
+                                    PackageManagerService.reportSettingsProblem(Log.WARN,
+                                            "Error in package manager settings: <cert> "
+                                                    + "flags " + flagsStr + " is not a number at "
+                                                    + parser.getPositionDescription());
+                                }
+                            } else {
+                                PackageManagerService.reportSettingsProblem(Log.WARN,
+                                        "Error in package manager settings: <cert> has no"
+                                                + " flags at " + parser.getPositionDescription());
+                            }
                         }
                     } else {
                         PackageManagerService.reportSettingsProblem(Log.WARN,
                                 "Error in package manager settings: <cert> has"
-                                   + " no index at " + parser.getPositionDescription());
+                                        + " no index at " + parser.getPositionDescription());
                     }
                 } else {
                     PackageManagerService.reportSettingsProblem(Log.WARN,
                             "Error in package manager settings: too "
-                               + "many <cert> tags, expected " + count
-                               + " at " + parser.getPositionDescription());
+                                    + "many <cert> tags, expected " + count
+                                    + " at " + parser.getPositionDescription());
+                }
+            } else if (tagName.equals("pastSigs")) {
+                if (flags == null) {
+                    // we haven't encountered pastSigs yet, go ahead
+                    String countStr = parser.getAttributeValue(null, "count");
+                    if (countStr == null) {
+                        PackageManagerService.reportSettingsProblem(Log.WARN,
+                                "Error in package manager settings: <pastSigs> has"
+                                        + " no count at " + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                    }
+                    try {
+                        final int pastSigsCount = Integer.parseInt(countStr);
+                        Signature[] pastSignatures = new Signature[pastSigsCount];
+                        int[] pastSignaturesFlags = new int[pastSigsCount];
+                        int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures,
+                                pastSignaturesFlags, builder);
+                        builder = builder
+                                .setPastSigningCertificates(pastSignatures)
+                                .setPastSigningCertificatesFlags(pastSignaturesFlags);
+
+                        if (pastSigsPos < pastSigsCount) {
+                            // Should never happen -- there is an error in the written
+                            // settings -- but if it does we don't want to generate
+                            // a bad array.
+                            Signature[] newSigs = new Signature[pastSigsPos];
+                            System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos);
+                            int[] newFlags = new int[pastSigsPos];
+                            System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos);
+                            builder = builder
+                                    .setPastSigningCertificates(newSigs)
+                                    .setPastSigningCertificatesFlags(newFlags);
+                            PackageManagerService.reportSettingsProblem(Log.WARN,
+                                    "Error in package manager settings: <pastSigs> count does not "
+                                    + "match number of <cert> entries "
+                                    + parser.getPositionDescription());
+                        }
+                    } catch (NumberFormatException e) {
+                        PackageManagerService.reportSettingsProblem(Log.WARN,
+                                "Error in package manager settings: <pastSigs> "
+                                        + "count " + countStr + " is not a number at "
+                                        + parser.getPositionDescription());
+                    }
+                } else {
+                    PackageManagerService.reportSettingsProblem(Log.WARN,
+                            "<pastSigs> encountered multiple times under the same <sigs> at "
+                                    + parser.getPositionDescription());
                 }
             } else {
                 PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Unknown element under <cert>: "
-                        + parser.getName());
+                        "Unknown element under <sigs>: "
+                                + parser.getName());
             }
             XmlUtils.skipCurrentTag(parser);
         }
-
-        if (pos < count) {
-            // Should never happen -- there is an error in the written
-            // settings -- but if it does we don't want to generate
-            // a bad array.
-            Signature[] newSigs = new Signature[pos];
-            System.arraycopy(mSignatures, 0, newSigs, 0, pos);
-            mSignatures = newSigs;
-        }
-    }
-
-    void assignSignatures(PackageParser.SigningDetails signingDetails) {
-        mSignatureSchemeVersion = signingDetails.signatureSchemeVersion;
-        if (!signingDetails.hasSignatures()) {
-            mSignatures = null;
-            return;
-        }
-        mSignatures = new Signature[signingDetails.signatures.length];
-        for (int i=0; i<signingDetails.signatures.length; i++) {
-            mSignatures[i] = signingDetails.signatures[i];
-        }
+        return pos;
     }
 
     @Override
@@ -206,16 +308,26 @@
         buf.append("PackageSignatures{");
         buf.append(Integer.toHexString(System.identityHashCode(this)));
         buf.append(" version:");
-        buf.append(mSignatureSchemeVersion);
+        buf.append(mSigningDetails.signatureSchemeVersion);
         buf.append(", signatures:[");
-        if (mSignatures != null) {
-            for (int i=0; i<mSignatures.length; i++) {
+        if (mSigningDetails.signatures != null) {
+            for (int i = 0; i < mSigningDetails.signatures.length; i++) {
                 if (i > 0) buf.append(", ");
                 buf.append(Integer.toHexString(
-                        mSignatures[i].hashCode()));
+                        mSigningDetails.signatures[i].hashCode()));
             }
         }
         buf.append("]}");
+        buf.append(", past signatures:[");
+        if (mSigningDetails.pastSigningCertificates != null) {
+            for (int i = 0; i < mSigningDetails.pastSigningCertificates.length; i++) {
+                if (i > 0) buf.append(", ");
+                buf.append(Integer.toHexString(
+                        mSigningDetails.pastSigningCertificates[i].hashCode()));
+                buf.append(" flags: ");
+                buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
+            }
+        }
         return buf.toString();
     }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index ecbc452..8ce412e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -920,13 +920,13 @@
     // by that time.
     void insertPackageSettingLPw(PackageSetting p, PackageParser.Package pkg) {
         // Update signatures if needed.
-        if (p.signatures.mSignatures == null) {
-            p.signatures.assignSignatures(pkg.mSigningDetails);
+        if (p.signatures.mSigningDetails.signatures == null) {
+            p.signatures.mSigningDetails = pkg.mSigningDetails;
         }
         // If this app defines a shared user id initialize
         // the shared user signatures as well.
-        if (p.sharedUser != null && p.sharedUser.signatures.mSignatures == null) {
-            p.sharedUser.signatures.assignSignatures(pkg.mSigningDetails);
+        if (p.sharedUser != null && p.sharedUser.signatures.mSigningDetails.signatures == null) {
+            p.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
         }
         addPackageSettingLPw(p, p.sharedUser);
     }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 4cb5e08..f82dc24 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -28,12 +28,10 @@
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
-import android.os.SystemClock;
-import android.telephony.ModemActivityInfo;
-import android.telephony.TelephonyManager;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Environment;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
@@ -41,18 +39,22 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StatFs;
 import android.os.StatsLogEventWrapper;
 import android.os.SynchronousResultReceiver;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.telephony.ModemActivityInfo;
+import android.telephony.TelephonyManager;
 import android.util.Slog;
 import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
-import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.PowerProfile;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -97,6 +99,11 @@
     private final KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
     private IWifiManager mWifiManager = null;
     private TelephonyManager mTelephony = null;
+    private final StatFs mStatFsData = new StatFs(Environment.getDataDirectory().getAbsolutePath());
+    private final StatFs mStatFsSystem =
+        new StatFs(Environment.getRootDirectory().getAbsolutePath());
+    private final StatFs mStatFsTemp =
+        new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
 
     public StatsCompanionService(Context context) {
         super();
@@ -560,7 +567,7 @@
                     if (clusterTimeMs != null) {
                         for (int speed = clusterTimeMs.length - 1; speed >= 0; --speed) {
                             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
-                            e.writeInt(tagId);
+                            e.writeInt(cluster);
                             e.writeInt(speed);
                             e.writeLong(clusterTimeMs[speed]);
                             ret.add(e);
@@ -589,6 +596,7 @@
                         e.writeLong(wifiInfo.getControllerIdleTimeMillis());
                         e.writeLong(wifiInfo.getControllerEnergyUsed());
                         ret.add(e);
+                        return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Pulling wifiManager for wifi controller activity energy info has error", e);
                     } finally {
@@ -619,6 +627,7 @@
                     e.writeLong(modemInfo.getRxTimeMillis());
                     e.writeLong(modemInfo.getEnergyUsed());
                     ret.add(e);
+                    return ret.toArray(new StatsLogEventWrapper[ret.size()]);
                 }
                 break;
             }
@@ -627,14 +636,30 @@
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
                 e.writeLong(SystemClock.elapsedRealtime());
                 ret.add(e);
-                break;
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
             case StatsLog.CPU_IDLE_TIME: {
                 List<StatsLogEventWrapper> ret = new ArrayList();
                 StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
                 e.writeLong(SystemClock.uptimeMillis());
                 ret.add(e);
-                break;
+                return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.DISK_SPACE: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 3);
+              e.writeLong(mStatFsData.getAvailableBytes());
+              e.writeLong(mStatFsSystem.getAvailableBytes());
+              e.writeLong(mStatFsTemp.getAvailableBytes());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
+            }
+            case StatsLog.SYSTEM_UPTIME: {
+              List<StatsLogEventWrapper> ret = new ArrayList();
+              StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, 1);
+              e.writeLong(SystemClock.uptimeMillis());
+              ret.add(e);
+              return ret.toArray(new StatsLogEventWrapper[ret.size()]);
             }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 30fc63c..be9b204 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -143,6 +143,26 @@
                 return null;
             }
 
+            // Determine the installed distro state. This should be possible regardless of whether
+            // there's an operation in progress.
+            DistroVersion installedDistroVersion;
+            int distroStatus = DISTRO_STATUS_UNKNOWN;
+            DistroRulesVersion installedDistroRulesVersion = null;
+            try {
+                installedDistroVersion = mInstaller.getInstalledDistroVersion();
+                if (installedDistroVersion == null) {
+                    distroStatus = DISTRO_STATUS_NONE;
+                    installedDistroRulesVersion = null;
+                } else {
+                    distroStatus = DISTRO_STATUS_INSTALLED;
+                    installedDistroRulesVersion = new DistroRulesVersion(
+                            installedDistroVersion.rulesVersion,
+                            installedDistroVersion.revision);
+                }
+            } catch (DistroException | IOException e) {
+                Slog.w(TAG, "Failed to read installed distro.", e);
+            }
+
             boolean operationInProgress = this.mOperationInProgress.get();
 
             // Determine the staged operation status, if possible.
@@ -168,27 +188,6 @@
                     Slog.w(TAG, "Failed to read staged distro.", e);
                 }
             }
-
-            // Determine the installed distro state, if possible.
-            DistroVersion installedDistroVersion;
-            int distroStatus = DISTRO_STATUS_UNKNOWN;
-            DistroRulesVersion installedDistroRulesVersion = null;
-            if (!operationInProgress) {
-                try {
-                    installedDistroVersion = mInstaller.getInstalledDistroVersion();
-                    if (installedDistroVersion == null) {
-                        distroStatus = DISTRO_STATUS_NONE;
-                        installedDistroRulesVersion = null;
-                    } else {
-                        distroStatus = DISTRO_STATUS_INSTALLED;
-                        installedDistroRulesVersion = new DistroRulesVersion(
-                                installedDistroVersion.rulesVersion,
-                                installedDistroVersion.revision);
-                    }
-                } catch (DistroException | IOException e) {
-                    Slog.w(TAG, "Failed to read installed distro.", e);
-                }
-            }
             return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
                     operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
                     distroStatus, installedDistroRulesVersion);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 7a0b1bf..4f2866b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -26,6 +26,7 @@
 import com.android.internal.R;
 import com.android.server.SystemService;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -135,4 +136,14 @@
     public CharSequence getPrintingDisabledReason() {
         return null;
     }
+
+    @Override
+    public List<String> setMeteredDataDisabled(ComponentName admin, List<String> packageNames) {
+        return packageNames;
+    }
+
+    @Override
+    public List<String> getMeteredDataDisabled(ComponentName admin) {
+        return new ArrayList<>();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6bee9d6..4d6989b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -806,6 +806,8 @@
         private static final String TAG_MANDATORY_BACKUP_TRANSPORT = "mandatory_backup_transport";
         private static final String TAG_START_USER_SESSION_MESSAGE = "start_user_session_message";
         private static final String TAG_END_USER_SESSION_MESSAGE = "end_user_session_message";
+        private static final String TAG_METERED_DATA_DISABLED_PACKAGES
+                = "metered_data_disabled_packages";
 
         DeviceAdminInfo info;
 
@@ -872,6 +874,9 @@
             }
         }
 
+        // The list of packages which are not allowed to use metered data.
+        List<String> meteredDisabledPackages;
+
         final Set<String> accountTypesWithManagementDisabled = new ArraySet<>();
 
         // The list of permitted accessibility services package namesas set by a profile
@@ -1153,6 +1158,7 @@
             writePackageListToXml(out, TAG_PERMITTED_NOTIFICATION_LISTENERS,
                     permittedNotificationListeners);
             writePackageListToXml(out, TAG_KEEP_UNINSTALLED_PACKAGES, keepUninstalledPackages);
+            writePackageListToXml(out, TAG_METERED_DATA_DISABLED_PACKAGES, meteredDisabledPackages);
             if (hasUserRestrictions()) {
                 UserRestrictionsUtils.writeRestrictions(
                         out, userRestrictions, TAG_USER_RESTRICTIONS);
@@ -1349,6 +1355,8 @@
                     permittedNotificationListeners = readPackageList(parser, tag);
                 } else if (TAG_KEEP_UNINSTALLED_PACKAGES.equals(tag)) {
                     keepUninstalledPackages = readPackageList(parser, tag);
+                } else if (TAG_METERED_DATA_DISABLED_PACKAGES.equals(tag)) {
+                    meteredDisabledPackages = readPackageList(parser, tag);
                 } else if (TAG_USER_RESTRICTIONS.equals(tag)) {
                     userRestrictions = UserRestrictionsUtils.readRestrictions(parser);
                 } else if (TAG_DEFAULT_ENABLED_USER_RESTRICTIONS.equals(tag)) {
@@ -1647,6 +1655,7 @@
                             policy.mAdminList.remove(i);
                             policy.mAdminMap.remove(aa.info.getComponent());
                             pushActiveAdminPackagesLocked(userHandle);
+                            pushMeteredDisabledPackagesLocked(userHandle);
                         }
                     }
                 } catch (RemoteException re) {
@@ -3502,6 +3511,7 @@
         mInjector.postOnSystemServerInitThreadPool(() -> {
             pushActiveAdminPackages();
             mUsageStatsManagerInternal.onAdminDataAvailable();
+            pushAllMeteredRestrictedPackages();
             mInjector.getNetworkPolicyManagerInternal().onAdminDataAvailable();
         });
     }
@@ -3517,6 +3527,17 @@
         }
     }
 
+    private void pushAllMeteredRestrictedPackages() {
+        synchronized (this) {
+            final List<UserInfo> users = mUserManager.getUsers();
+            for (int i = users.size() - 1; i >= 0; --i) {
+                final int userId = users.get(i).id;
+                mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackagesAsync(
+                        getMeteredDisabledPackagesLocked(userId), userId);
+            }
+        }
+    }
+
     private void pushActiveAdminPackagesLocked(int userId) {
         mUsageStatsManagerInternal.setActiveAdminApps(
                 getActiveAdminPackagesLocked(userId), userId);
@@ -11031,6 +11052,93 @@
     }
 
     @Override
+    public List<String> setMeteredDataDisabled(ComponentName who, List<String> packageNames) {
+        Preconditions.checkNotNull(who);
+        Preconditions.checkNotNull(packageNames);
+
+        if (!mHasFeature) {
+            return packageNames;
+        }
+        synchronized (this) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            final int callingUserId = mInjector.userHandleGetCallingUserId();
+            final long identity = mInjector.binderClearCallingIdentity();
+            try {
+                final List<String> excludedPkgs
+                        = removeInvalidPkgsForMeteredDataRestriction(callingUserId, packageNames);
+                admin.meteredDisabledPackages = packageNames;
+                pushMeteredDisabledPackagesLocked(callingUserId);
+                saveSettingsLocked(callingUserId);
+                return excludedPkgs;
+            } finally {
+                mInjector.binderRestoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    private List<String> removeInvalidPkgsForMeteredDataRestriction(
+            int userId, List<String> pkgNames) {
+        final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
+        final List<String> excludedPkgs = new ArrayList<>();
+        for (int i = pkgNames.size() - 1; i >= 0; --i) {
+            final String pkgName = pkgNames.get(i);
+            // If the package is an active admin, don't restrict it.
+            if (activeAdmins.contains(pkgName)) {
+                excludedPkgs.add(pkgName);
+                continue;
+            }
+            // If the package doesn't exist, don't restrict it.
+            try {
+                if (!mInjector.getIPackageManager().isPackageAvailable(pkgName, userId)) {
+                    excludedPkgs.add(pkgName);
+                }
+            } catch (RemoteException e) {
+                // Should not happen
+            }
+        }
+        pkgNames.removeAll(excludedPkgs);
+        return excludedPkgs;
+    }
+
+    @Override
+    public List<String> getMeteredDataDisabled(ComponentName who) {
+        Preconditions.checkNotNull(who);
+
+        if (!mHasFeature) {
+            return new ArrayList<>();
+        }
+        synchronized (this) {
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(who,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+            return admin.meteredDisabledPackages == null
+                    ? new ArrayList<>() : admin.meteredDisabledPackages;
+        }
+    }
+
+    private void pushMeteredDisabledPackagesLocked(int userId) {
+        mInjector.getNetworkPolicyManagerInternal().setMeteredRestrictedPackages(
+                getMeteredDisabledPackagesLocked(userId), userId);
+    }
+
+    private Set<String> getMeteredDisabledPackagesLocked(int userId) {
+        final DevicePolicyData policy = getUserData(userId);
+        final Set<String> restrictedPkgs = new ArraySet<>();
+        for (int i = policy.mAdminList.size() - 1; i >= 0; --i) {
+            final ActiveAdmin admin = policy.mAdminList.get(i);
+            if (!isActiveAdminWithPolicyForUserLocked(admin,
+                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, userId)) {
+                // Not a profile or device owner, ignore
+                continue;
+            }
+            if (admin.meteredDisabledPackages != null) {
+                restrictedPkgs.addAll(admin.meteredDisabledPackages);
+            }
+        }
+        return restrictedPkgs;
+    }
+
+    @Override
     public void setAffiliationIds(ComponentName admin, List<String> ids) {
         if (!mHasFeature) {
             return;
@@ -11389,6 +11497,7 @@
                 resetGlobalProxyLocked(policy);
             }
             pushActiveAdminPackagesLocked(userHandle);
+            pushMeteredDisabledPackagesLocked(userHandle);
             saveSettingsLocked(userHandle);
             updateMaximumTimeToLockLocked(userHandle);
             policy.mRemovingAdmins.remove(adminReceiver);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 7c3082f..045b73c 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -134,7 +134,6 @@
 
 import org.junit.After;
 import org.junit.Before;
-import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.MethodRule;
@@ -185,7 +184,6 @@
     "com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner"
  * </code></pre>
  */
-@Ignore
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class NetworkPolicyManagerServiceTest {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
index 8d5556e..07262e1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MagnificationGestureHandlerTest.java
@@ -23,6 +23,8 @@
 
 import static com.android.server.testutils.TestUtils.strictMock;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
@@ -32,6 +34,7 @@
 
 import android.annotation.NonNull;
 import android.content.Context;
+import android.os.Handler;
 import android.os.Message;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -46,7 +49,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CompletableFuture;
 import java.util.function.IntConsumer;
+import java.util.function.Supplier;
 
 
 /**
@@ -130,7 +135,7 @@
     }
 
     @NonNull
-    public MagnificationGestureHandler newInstance(boolean detectTripleTap,
+    private MagnificationGestureHandler newInstance(boolean detectTripleTap,
             boolean detectShortcutTrigger) {
         MagnificationGestureHandler h = new MagnificationGestureHandler(
                 mContext, mMagnificationController,
@@ -192,6 +197,16 @@
         });
     }
 
+    @Test
+    public void testTransitionToDelegatingStateAndClear_preservesShortcutTriggeredState() {
+        mMgh.mDetectingState.transitionToDelegatingStateAndClear();
+        assertFalse(mMgh.mDetectingState.mShortcutTriggered);
+
+        goFromStateIdleTo(STATE_SHORTCUT_TRIGGERED);
+        mMgh.mDetectingState.transitionToDelegatingStateAndClear();
+        assertTrue(mMgh.mDetectingState.mShortcutTriggered);
+    }
+
     /**
      * Covers edges of the graph not covered by "canonical" transitions specified in
      * {@link #goFromStateIdleTo} and {@link #returnToNormalFrom}
@@ -510,14 +525,20 @@
         fastForward(1);
     }
 
+    private static MotionEvent fromTouchscreen(MotionEvent ev) {
+        ev.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+        return ev;
+    }
+
     private MotionEvent moveEvent(float x, float y) {
-        return MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0);
+        return fromTouchscreen(
+        	    MotionEvent.obtain(mLastDownTime, mClock.now(), ACTION_MOVE, x, y, 0));
     }
 
     private MotionEvent downEvent() {
         mLastDownTime = mClock.now();
-        return MotionEvent.obtain(mLastDownTime, mLastDownTime,
-                ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0);
+        return fromTouchscreen(MotionEvent.obtain(mLastDownTime, mLastDownTime,
+                ACTION_DOWN, DEFAULT_X, DEFAULT_Y, 0));
     }
 
     private MotionEvent upEvent() {
@@ -525,8 +546,8 @@
     }
 
     private MotionEvent upEvent(long downTime) {
-        return MotionEvent.obtain(downTime, mClock.now(),
-                MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0);
+        return fromTouchscreen(MotionEvent.obtain(downTime, mClock.now(),
+                MotionEvent.ACTION_UP, DEFAULT_X, DEFAULT_Y, 0));
     }
 
     private MotionEvent pointerEvent(int action, float x, float y) {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index 589a89b..b58c700 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -228,8 +228,8 @@
 
         if (containsConditions(preconditions,PRECONDITION_CANNOT_START_ANY_ACTIVITY)) {
             doReturn(false).when(service.mStackSupervisor).checkStartAnyActivityPermission(
-                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(), anyBoolean(),
-                    any(), any(), any(), any());
+                    any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
+                    anyBoolean(), any(), any(), any());
         }
 
         try {
@@ -278,7 +278,7 @@
                     .setResultTo(resultTo)
                     .setRequestCode(requestCode)
                     .setReason("testLaunchActivityPermissionDenied")
-                    .setActivityOptions(options)
+                    .setActivityOptions(new SafeActivityOptions(options))
                     .execute();
             verify(options, times(1)).abort();
         }
diff --git a/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java b/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
new file mode 100644
index 0000000..168bc17
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/SafeActivityOptionsTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.ActivityOptions;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.FlakyTest;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@Presubmit
+@FlakyTest
+@RunWith(AndroidJUnit4.class)
+public class SafeActivityOptionsTest {
+
+    @Test
+    public void testMerge() {
+        final ActivityOptions opts1 = ActivityOptions.makeBasic();
+        opts1.setLaunchDisplayId(5);
+        final ActivityOptions opts2 = ActivityOptions.makeBasic();
+        opts2.setLaunchDisplayId(6);
+        final SafeActivityOptions options = new SafeActivityOptions(opts1);
+        final ActivityOptions result = options.mergeActivityOptions(opts1, opts2);
+        assertEquals(6, result.getLaunchDisplayId());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 9cac536..613d0af 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -87,6 +87,11 @@
     }
 
     @Override
+    public Intent getCarLaunchIntentForPackage(String packageName) {
+        return null;
+    }
+
+    @Override
     public int[] getPackageGids(String packageName) throws NameNotFoundException {
         return new int[0];
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 725ede8..e8d6ed2 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -126,6 +126,7 @@
             permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS,
             permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
     public static final String NOT_DEVICE_OWNER_MSG = "does not own the device";
+    public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile";
     public static final String ONGOING_CALL_MSG = "ongoing call on the device";
 
     // TODO replace all instances of this with explicit {@link #mServiceContext}.
@@ -301,10 +302,10 @@
 
         // Verify
         verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
-                MockUtils.checkAdminApps(admin1.getPackageName()),
+                MockUtils.checkApps(admin1.getPackageName()),
                 eq(UserHandle.USER_SYSTEM));
         verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
-                MockUtils.checkAdminApps(admin2.getPackageName(),
+                MockUtils.checkApps(admin2.getPackageName(),
                         adminAnotherPackage.getPackageName()),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
         verify(getServices().usageStatsManagerInternal).onAdminDataAvailable();
@@ -705,7 +706,7 @@
 
         assertFalse(dpm.isAdminActiveAsUser(admin1, DpmMockContext.CALLER_USER_HANDLE));
         verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
-                MockUtils.checkAdminApps(admin2.getPackageName()),
+                MockUtils.checkApps(admin2.getPackageName()),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
 
         // Again broadcast from saveSettingsLocked().
@@ -1360,6 +1361,7 @@
                 eq(packageName),
                 anyInt(),
                 eq(userId));
+        doReturn(true).when(getServices().ipackageManager).isPackageAvailable(packageName, userId);
         // Setup application UID with the PackageManager
         doReturn(uid).when(getServices().packageManager).getPackageUidAsUser(
                 eq(packageName),
@@ -2101,6 +2103,53 @@
         }
     }
 
+    public void testSetGetMeteredDataDisabled() throws Exception {
+        setAsProfileOwner(admin1);
+
+        final ArrayList<String> emptyList = new ArrayList<>();
+        assertEquals(emptyList, dpm.getMeteredDataDisabled(admin1));
+
+        // Setup
+        final ArrayList<String> pkgsToRestrict = new ArrayList<>();
+        final String package1 = "com.example.one";
+        final String package2 = "com.example.two";
+        pkgsToRestrict.add(package1);
+        pkgsToRestrict.add(package2);
+        setupPackageInPackageManager(package1, DpmMockContext.CALLER_USER_HANDLE, 123, 0);
+        setupPackageInPackageManager(package2, DpmMockContext.CALLER_USER_HANDLE, 456, 0);
+        List<String> excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+
+        // Verify
+        assertEquals(emptyList, excludedPkgs);
+        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabled(admin1));
+        verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
+                MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+
+        // Setup
+        pkgsToRestrict.remove(package1);
+        excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+
+        // Verify
+        assertEquals(emptyList, excludedPkgs);
+        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabled(admin1));
+        verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
+                MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])),
+                eq(DpmMockContext.CALLER_USER_HANDLE));
+    }
+
+    public void testSetGetMeteredDataDisabled_deviceAdmin() {
+        mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+        dpm.setActiveAdmin(admin1, true);
+        assertTrue(dpm.isAdminActive(admin1));
+        mContext.callerPermissions.remove(permission.MANAGE_DEVICE_ADMINS);
+
+        assertExpectException(SecurityException.class,  /* messageRegex= */ NOT_PROFILE_OWNER_MSG,
+                () -> dpm.setMeteredDataDisabled(admin1, new ArrayList<>()));
+        assertExpectException(SecurityException.class,  /* messageRegex= */ NOT_PROFILE_OWNER_MSG,
+                () -> dpm.getMeteredDataDisabled(admin1));
+    }
+
     public void testCreateAdminSupportIntent() throws Exception {
         // Setup device owner.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index dec962e..92ea766 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -119,25 +119,25 @@
         return MockitoHamcrest.argThat(m);
     }
 
-    public static Set<String> checkAdminApps(String... adminApps) {
+    public static Set<String> checkApps(String... adminApps) {
         final Matcher<Set<String>> m = new BaseMatcher<Set<String>>() {
             @Override
             public boolean matches(Object item) {
                 if (item == null) return false;
-                final Set<String> actualAdminApps = (Set<String>) item;
-                if (adminApps.length != actualAdminApps.size()) {
+                final Set<String> actualApps = (Set<String>) item;
+                if (adminApps.length != actualApps.size()) {
                     return false;
                 }
-                final Set<String> copyOfAdmins = new ArraySet<>(actualAdminApps);
+                final Set<String> copyOfApps = new ArraySet<>(actualApps);
                 for (String adminApp : adminApps) {
-                    copyOfAdmins.remove(adminApp);
+                    copyOfApps.remove(adminApp);
                 }
-                return copyOfAdmins.isEmpty();
+                return copyOfApps.isEmpty();
             }
 
             @Override
             public void describeTo(Description description) {
-                description.appendText("Admin apps=" + Arrays.toString(adminApps));
+                description.appendText("Apps=" + Arrays.toString(adminApps));
             }
         };
         return MockitoHamcrest.argThat(m);
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
index c5f8c90..675000e 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -171,6 +171,23 @@
                 newDataStore.getBrightnessConfiguration(0 /*userSerial*/));
     }
 
+    @Test
+    public void testNullBrightnessConfiguration() {
+        final float[] lux = { 0f, 10f };
+        final float[] nits = {1f, 100f };
+        final BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits)
+                .setDescription("a description")
+                .build();
+        mDataStore.loadIfNeeded();
+        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+
+        mDataStore.setBrightnessConfigurationForUser(config, 0, "packagename");
+        assertNotNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+
+        mDataStore.setBrightnessConfigurationForUser(null, 0, "packagename");
+        assertNull(mDataStore.getBrightnessConfiguration(0 /*userSerial*/));
+    }
+
     public class TestInjector extends PersistentDataStore.Injector {
         private InputStream mReadStream;
         private OutputStream mWriteStream;
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index 272b5d8..e864870 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -31,6 +31,7 @@
 import android.app.trust.TrustManager;
 import android.content.ComponentName;
 import android.content.pm.UserInfo;
+import android.hardware.authsecret.V1_0.IAuthSecret;
 import android.os.FileUtils;
 import android.os.IProgressListener;
 import android.os.RemoteException;
@@ -80,6 +81,7 @@
     DevicePolicyManagerInternal mDevicePolicyManagerInternal;
     KeyStore mKeyStore;
     MockSyntheticPasswordManager mSpManager;
+    IAuthSecret mAuthSecretService;
 
     @Override
     protected void setUp() throws Exception {
@@ -115,17 +117,21 @@
         };
         mSpManager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService,
                 mUserManager);
+        mAuthSecretService = mock(IAuthSecret.class);
         mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage,
                 mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager,
-                mSpManager);
+                mSpManager, mAuthSecretService);
         when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO);
         mPrimaryUserProfiles.add(PRIMARY_USER_INFO);
         installChildProfile(MANAGED_PROFILE_USER_ID);
         installAndTurnOffChildProfile(TURNED_OFF_PROFILE_USER_ID);
-        when(mUserManager.getUsers(anyBoolean())).thenReturn(mPrimaryUserProfiles);
         when(mUserManager.getProfiles(eq(PRIMARY_USER_ID))).thenReturn(mPrimaryUserProfiles);
         when(mUserManager.getUserInfo(eq(SECONDARY_USER_ID))).thenReturn(SECONDARY_USER_INFO);
 
+        final ArrayList<UserInfo> allUsers = new ArrayList<>(mPrimaryUserProfiles);
+        allUsers.add(SECONDARY_USER_INFO);
+        when(mUserManager.getUsers(anyBoolean())).thenReturn(allUsers);
+
         when(mActivityManager.unlockUser(anyInt(), any(), any(), any())).thenAnswer(
                 new Answer<Boolean>() {
             @Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
index 4ad9f19..d2caa0a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java
@@ -22,6 +22,7 @@
 import static com.android.server.testutils.TestUtils.assertExpectException;
 
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.verify;
 
@@ -31,6 +32,10 @@
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 
+import java.util.ArrayList;
+
+import org.mockito.ArgumentCaptor;
+
 /**
  * Run the synthetic password tests with caching enabled.
  *
@@ -88,6 +93,26 @@
                     .getResponseCode());
     }
 
+    public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException {
+        final String PASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-password";
+        final String NEWPASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword";
+
+        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+        // Untrusted change password
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+
+        // Verify the password
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+                    .getResponseCode());
+
+        // Ensure the same secret was passed each time
+        ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class);
+        verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture());
+        assertEquals(1, secret.getAllValues().stream().distinct().count());
+    }
+
     public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException {
         final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password";
         final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword";
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
index 0916a33..fe683ab 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java
@@ -20,6 +20,7 @@
 
 import android.app.IActivityManager;
 import android.content.Context;
+import android.hardware.authsecret.V1_0.IAuthSecret;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Process;
@@ -42,10 +43,12 @@
         private LockPatternUtils mLockPatternUtils;
         private IStorageManager mStorageManager;
         private SyntheticPasswordManager mSpManager;
+        private IAuthSecret mAuthSecretService;
 
         public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore,
                 IActivityManager activityManager, LockPatternUtils lockPatternUtils,
-                IStorageManager storageManager, SyntheticPasswordManager spManager) {
+                IStorageManager storageManager, SyntheticPasswordManager spManager,
+                IAuthSecret authSecretService) {
             super(context);
             mLockSettingsStorage = storage;
             mKeyStore = keyStore;
@@ -109,10 +112,11 @@
     protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils,
             LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore,
             IStorageManager storageManager, IActivityManager mActivityManager,
-            SyntheticPasswordManager spManager) {
+            SyntheticPasswordManager spManager, IAuthSecret authSecretService) {
         super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils,
-                storageManager, spManager));
+                storageManager, spManager, authSecretService));
         mGateKeeperService = gatekeeper;
+        mAuthSecretService = authSecretService;
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index b07d6ac..294c3e9 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -24,6 +24,10 @@
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY;
 import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY;
 
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
 import android.app.admin.PasswordMetrics;
@@ -36,6 +40,10 @@
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
 import com.android.server.locksettings.SyntheticPasswordManager.PasswordData;
 
+import java.util.ArrayList;
+
+import org.mockito.ArgumentCaptor;
+
 
 /**
  * runtest frameworks-services -c com.android.server.locksettings.SyntheticPasswordTests
@@ -169,6 +177,46 @@
         assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
     }
 
+    public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException {
+        final String PASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password";
+        final String NEWPASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new";
+
+        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+        mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD,
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+                        .getResponseCode());
+
+        // Check the same secret was passed each time
+        ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class);
+        verify(mAuthSecretService, atLeastOnce()).primaryUserCredential(secret.capture());
+        assertEquals(1, secret.getAllValues().stream().distinct().count());
+    }
+
+    public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException {
+        final String PASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password";
+        final String NEWPASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new";
+
+        initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID);
+        reset(mAuthSecretService);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID)
+                        .getResponseCode());
+        verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class));
+    }
+
+    public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException {
+        final String PASSWORD = "testSecondaryUserDoesNotPassAuthSecret-password";
+        final String NEWPASSWORD = "testSecondaryUserDoesNotPassAuthSecret-new";
+
+        initializeCredentialUnderSP(PASSWORD, SECONDARY_USER_ID);
+        assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
+                PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID)
+                        .getResponseCode());
+        verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class));
+    }
+
     public void testManagedProfileUnifiedChallengeMigration() throws RemoteException {
         final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd";
         disableSyntheticPassword();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 7eec4fe..6a3a260 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -16,11 +16,11 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
+import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
 
-import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD;
-import static android.security.keystore.KeychainProtectionParams.TYPE_PATTERN;
-import static android.security.keystore.KeychainProtectionParams.TYPE_PIN;
+import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD;
+import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PATTERN;
+import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PIN;
 
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
 import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -40,9 +40,9 @@
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.KeychainSnapshot;
-import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.WrappedApplicationKey;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -190,19 +190,19 @@
 
     @Test
     public void getUiFormat_returnsPinIfPin() {
-        assertEquals(TYPE_PIN,
+        assertEquals(UI_FORMAT_PIN,
                 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234"));
     }
 
     @Test
     public void getUiFormat_returnsPasswordIfPassword() {
-        assertEquals(TYPE_PASSWORD,
+        assertEquals(UI_FORMAT_PASSWORD,
                 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a"));
     }
 
     @Test
     public void getUiFormat_returnsPatternIfPattern() {
-        assertEquals(TYPE_PATTERN,
+        assertEquals(UI_FORMAT_PATTERN,
                 KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234"));
 
     }
@@ -287,33 +287,33 @@
 
         mKeySyncTask.run();
 
-        KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        KeyDerivationParams KeyDerivationParams =
-                keychainSnapshot.getKeychainProtectionParams().get(0).getKeyDerivationParams();
-        assertThat(KeyDerivationParams.getAlgorithm()).isEqualTo(
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        KeyDerivationParams keyDerivationParams =
+                keyChainSnapshot.getKeyChainProtectionParams().get(0).getKeyDerivationParams();
+        assertThat(keyDerivationParams.getAlgorithm()).isEqualTo(
                 KeyDerivationParams.ALGORITHM_SHA256);
         verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID);
         byte[] lockScreenHash = KeySyncTask.hashCredentials(
-                KeyDerivationParams.getSalt(),
+                keyDerivationParams.getSalt(),
                 TEST_CREDENTIAL);
         Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID);
         counterId = 1L; // TODO: use value from the database.
         assertThat(counterId).isNotNull();
         byte[] recoveryKey = decryptThmEncryptedKey(
                 lockScreenHash,
-                keychainSnapshot.getEncryptedRecoveryKeyBlob(),
+                keyChainSnapshot.getEncryptedRecoveryKeyBlob(),
                 /*vaultParams=*/ KeySyncUtils.packVaultParams(
                         mKeyPair.getPublic(),
                         counterId,
                         /*maxAttempts=*/ 10,
                         TEST_VAULT_HANDLE));
-        List<WrappedApplicationKey> applicationKeys = keychainSnapshot.getWrappedApplicationKeys();
+        List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
         assertThat(applicationKeys).hasSize(1);
-        assertThat(keychainSnapshot.getCounterId()).isEqualTo(counterId);
-        assertThat(keychainSnapshot.getMaxAttempts()).isEqualTo(10);
-        assertThat(keychainSnapshot.getTrustedHardwarePublicKey())
+        assertThat(keyChainSnapshot.getCounterId()).isEqualTo(counterId);
+        assertThat(keyChainSnapshot.getMaxAttempts()).isEqualTo(10);
+        assertThat(keyChainSnapshot.getTrustedHardwarePublicKey())
                 .isEqualTo(SecureBox.encodePublicKey(mKeyPair.getPublic()));
-        assertThat(keychainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
+        assertThat(keyChainSnapshot.getServerParams()).isEqualTo(TEST_VAULT_HANDLE);
         WrappedApplicationKey keyData = applicationKeys.get(0);
         assertEquals(TEST_APP_KEY_ALIAS, keyData.getAlias());
         assertThat(keyData.getAlias()).isEqualTo(keyData.getAlias());
@@ -332,14 +332,14 @@
 
         mKeySyncTask.run();
 
-        KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(1); // default value;
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, true);
 
         mKeySyncTask.run();
 
-        keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        assertThat(keychainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
+        keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertThat(keyChainSnapshot.getSnapshotVersion()).isEqualTo(2); // Updated
     }
 
     @Test
@@ -362,10 +362,10 @@
 
         mKeySyncTask.run();
 
-        KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
-        assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
-                isEqualTo(TYPE_PASSWORD);
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
+                isEqualTo(UI_FORMAT_PASSWORD);
     }
 
    @Test
@@ -388,11 +388,11 @@
 
         mKeySyncTask.run();
 
-        KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
         // Password with only digits is changed to pin.
-        assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
-                isEqualTo(TYPE_PIN);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
+                isEqualTo(UI_FORMAT_PIN);
     }
 
     @Test
@@ -415,10 +415,10 @@
 
         mKeySyncTask.run();
 
-        KeychainSnapshot keychainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
-        assertThat(keychainSnapshot.getKeychainProtectionParams()).hasSize(1);
-        assertThat(keychainSnapshot.getKeychainProtectionParams().get(0).getLockScreenUiFormat()).
-                isEqualTo(TYPE_PATTERN);
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams()).hasSize(1);
+        assertThat(keyChainSnapshot.getKeyChainProtectionParams().get(0).getLockScreenUiFormat()).
+                isEqualTo(UI_FORMAT_PATTERN);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 970bc33..c863aab 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -16,8 +16,8 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
-import static android.security.keystore.KeychainProtectionParams.TYPE_LOCKSCREEN;
-import static android.security.keystore.KeychainProtectionParams.TYPE_PASSWORD;
+import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
+import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FORMAT_PASSWORD;
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertArrayEquals;
@@ -42,9 +42,9 @@
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.security.keystore.KeyDerivationParams;
-import android.security.keystore.KeychainProtectionParams;
-import android.security.keystore.WrappedApplicationKey;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
 import android.support.test.filters.SmallTest;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
@@ -250,9 +250,9 @@
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
                 ImmutableList.of(
-                        new KeychainProtectionParams(
+                        new KeyChainProtectionParams(
                                 TYPE_LOCKSCREEN,
-                                TYPE_PASSWORD,
+                                UI_FORMAT_PASSWORD,
                                 KeyDerivationParams.createSha256Params(TEST_SALT),
                                 TEST_SECRET)));
 
@@ -269,9 +269,9 @@
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
                 ImmutableList.of(
-                        new KeychainProtectionParams(
+                        new KeyChainProtectionParams(
                                 TYPE_LOCKSCREEN,
-                                TYPE_PASSWORD,
+                                UI_FORMAT_PASSWORD,
                                 KeyDerivationParams.createSha256Params(TEST_SALT),
                                 TEST_SECRET)));
 
@@ -290,9 +290,9 @@
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
                 ImmutableList.of(
-                        new KeychainProtectionParams(
+                        new KeyChainProtectionParams(
                                 TYPE_LOCKSCREEN,
-                                TYPE_PASSWORD,
+                                UI_FORMAT_PASSWORD,
                                 KeyDerivationParams.createSha256Params(TEST_SALT),
                                 TEST_SECRET)));
 
@@ -309,9 +309,9 @@
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
                 ImmutableList.of(
-                        new KeychainProtectionParams(
+                        new KeyChainProtectionParams(
                                 TYPE_LOCKSCREEN,
-                                TYPE_PASSWORD,
+                                UI_FORMAT_PASSWORD,
                                 KeyDerivationParams.createSha256Params(TEST_SALT),
                                 TEST_SECRET)));
 
@@ -332,7 +332,7 @@
             fail("should have thrown");
         } catch (UnsupportedOperationException e) {
             assertThat(e.getMessage()).startsWith(
-                    "Only a single KeychainProtectionParams is supported");
+                    "Only a single KeyChainProtectionParams is supported");
         }
     }
 
@@ -345,9 +345,9 @@
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
                     ImmutableList.of(
-                            new KeychainProtectionParams(
+                            new KeyChainProtectionParams(
                                     TYPE_LOCKSCREEN,
-                                    TYPE_PASSWORD,
+                                    UI_FORMAT_PASSWORD,
                                     KeyDerivationParams.createSha256Params(TEST_SALT),
                                     TEST_SECRET)));
             fail("should have thrown");
@@ -367,9 +367,9 @@
                     vaultParams,
                     TEST_VAULT_CHALLENGE,
                     ImmutableList.of(
-                            new KeychainProtectionParams(
+                            new KeyChainProtectionParams(
                                     TYPE_LOCKSCREEN,
-                                    TYPE_PASSWORD,
+                                    UI_FORMAT_PASSWORD,
                                     KeyDerivationParams.createSha256Params(TEST_SALT),
                                     TEST_SECRET)));
             fail("should have thrown");
@@ -400,9 +400,9 @@
                 TEST_PUBLIC_KEY,
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
-                ImmutableList.of(new KeychainProtectionParams(
+                ImmutableList.of(new KeyChainProtectionParams(
                         TYPE_LOCKSCREEN,
-                        TYPE_PASSWORD,
+                        UI_FORMAT_PASSWORD,
                         KeyDerivationParams.createSha256Params(TEST_SALT),
                         TEST_SECRET)));
 
@@ -424,9 +424,9 @@
                 TEST_PUBLIC_KEY,
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
-                ImmutableList.of(new KeychainProtectionParams(
+                ImmutableList.of(new KeyChainProtectionParams(
                         TYPE_LOCKSCREEN,
-                        TYPE_PASSWORD,
+                        UI_FORMAT_PASSWORD,
                         KeyDerivationParams.createSha256Params(TEST_SALT),
                         TEST_SECRET)));
         byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
@@ -456,9 +456,9 @@
                 TEST_PUBLIC_KEY,
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
-                ImmutableList.of(new KeychainProtectionParams(
+                ImmutableList.of(new KeyChainProtectionParams(
                         TYPE_LOCKSCREEN,
-                        TYPE_PASSWORD,
+                        UI_FORMAT_PASSWORD,
                         KeyDerivationParams.createSha256Params(TEST_SALT),
                         TEST_SECRET)));
         byte[] keyClaimant = mRecoverySessionStorage.get(Binder.getCallingUid(), TEST_SESSION_ID)
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index 56b44e2..d61a294 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -3,7 +3,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 
-import android.security.keystore.KeychainSnapshot;
+import android.security.keystore.recovery.KeyChainSnapshot;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -26,25 +26,25 @@
     @Test
     public void get_returnsSetSnapshot() {
         int userId = 1000;
-        KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
+        KeyChainSnapshot keyChainSnapshot = new KeyChainSnapshot(
                 /*snapshotVersion=*/ 1,
                 new ArrayList<>(),
                 new ArrayList<>(),
                 new byte[0]);
-        mRecoverySnapshotStorage.put(userId, keychainSnapshot);
+        mRecoverySnapshotStorage.put(userId, keyChainSnapshot);
 
-        assertEquals(keychainSnapshot, mRecoverySnapshotStorage.get(userId));
+        assertEquals(keyChainSnapshot, mRecoverySnapshotStorage.get(userId));
     }
 
     @Test
     public void remove_removesSnapshots() {
         int userId = 1000;
-        KeychainSnapshot keychainSnapshot = new KeychainSnapshot(
+        KeyChainSnapshot keyChainSnapshot = new KeyChainSnapshot(
                 /*snapshotVersion=*/ 1,
                 new ArrayList<>(),
                 new ArrayList<>(),
                 new byte[0]);
-        mRecoverySnapshotStorage.put(userId, keychainSnapshot);
+        mRecoverySnapshotStorage.put(userId, keyChainSnapshot);
 
         mRecoverySnapshotStorage.remove(userId);
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 49601c3..5c7348c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -497,7 +497,9 @@
                 new PackageParser.SigningDetails(
                         new Signature[] { new Signature(new byte[16]) },
                         2,
-                        new ArraySet<>());
+                        new ArraySet<>(),
+                        null,
+                        null);
         pkg.mExtras = new Bundle();
         pkg.mRestrictedAccountType = "foo19";
         pkg.mRequiredAccountType = "foo20";
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index d09d0c8..1cfae1e 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -289,11 +289,13 @@
 
         // Request the rules state while the async operation is "happening".
         RulesState actualRulesState = mRulesManagerService.getRulesState();
+        DistroRulesVersion expectedInstalledDistroRulesVersion =
+                new DistroRulesVersion(installedRulesVersion, revision);
         RulesState expectedRuleState = new RulesState(
                 systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED,
                 true /* operationInProgress */,
                 RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */,
-                RulesState.DISTRO_STATUS_UNKNOWN, null /* installedDistroRulesVersion */);
+                RulesState.DISTRO_STATUS_INSTALLED, expectedInstalledDistroRulesVersion);
         assertEquals(expectedRuleState, actualRulesState);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
index f8db4fa..794d033 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -54,7 +54,7 @@
     @Test
     public void testApply_clipNone() {
         Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
                 mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
@@ -99,7 +99,8 @@
     public void testApply_clipBeforeNoStackBounds() {
         // Stack bounds is (0, 0, 0, 0) animation clip is (0, 0, 20, 20)
         Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
+        a.initialize(0, 0, 0, 0);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
                 null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
@@ -110,7 +111,7 @@
     public void testApply_clipBeforeSmallerAnimationClip() {
         // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5)
         Rect windowCrop = new Rect(0, 0, 5, 5);
-        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
                 mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
@@ -122,11 +123,17 @@
     public void testApply_clipBeforeSmallerStackClip() {
         // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 20, 20)
         Rect windowCrop = new Rect(0, 0, 20, 20);
-        Animation a = new ClipRectAnimation(windowCrop, windowCrop);
+        Animation a = createClipRectAnimation(windowCrop, windowCrop);
         WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null,
                 mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM);
         windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
         verify(mTransaction).setWindowCrop(eq(mSurfaceControl),
                 argThat(rect -> rect.equals(mStackBounds)));
     }
+
+    private Animation createClipRectAnimation(Rect fromClip, Rect toClip) {
+        Animation a = new ClipRectAnimation(fromClip, toClip);
+        a.initialize(0, 0, 0, 0);
+        return a;
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index ad3fecf..e0bebee 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -73,6 +73,7 @@
 import android.service.notification.Adjustment;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
+import android.service.notification.NotifyingApp;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -105,8 +106,10 @@
 import java.io.FileOutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -253,10 +256,19 @@
         mFile.delete();
     }
 
-    public void waitForIdle() throws Exception {
+    public void waitForIdle() {
         mTestableLooper.processAllMessages();
     }
 
+    private StatusBarNotification generateSbn(String pkg, int uid, long postTime, int userId) {
+        Notification.Builder nb = new Notification.Builder(mContext, "a")
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0,
+                nb.build(), new UserHandle(userId), null, postTime);
+        return sbn;
+    }
+
     private NotificationRecord generateNotificationRecord(NotificationChannel channel, int id,
             String groupKey, boolean isSummary) {
         Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
@@ -2291,4 +2303,102 @@
 
         verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
     }
+
+    @Test
+    public void testRecents() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+    }
+
+    @Test
+    public void testRecentsNoDuplicatePackages() throws Exception {
+        final NotificationRecord p1 = new NotificationRecord(mContext, generateSbn("p", 1, 1000, 0),
+                mTestNotificationChannel);
+        final NotificationRecord p2 = new NotificationRecord(mContext, generateSbn("p", 1, 2000, 0),
+                mTestNotificationChannel);
+
+        mService.logRecentLocked(p1);
+        mService.logRecentLocked(p2);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 1);
+        NotifyingApp expected = new NotifyingApp().setPackage("p").setUid(1).setLastNotified(2000);
+        assertEquals(expected, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsWithDuplicatePackage() throws Exception {
+        Set<NotifyingApp> expected = new HashSet<>();
+
+        final NotificationRecord oldest = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 0), mTestNotificationChannel);
+        mService.logRecentLocked(oldest);
+        for (int i = 1; i <= 5; i++) {
+            NotificationRecord r = new NotificationRecord(mContext,
+                    generateSbn("p" + i, i, i*100, 0), mTestNotificationChannel);
+            expected.add(new NotifyingApp()
+                    .setPackage(r.sbn.getPackageName())
+                    .setUid(r.sbn.getUid())
+                    .setLastNotified(r.sbn.getPostTime()));
+            mService.logRecentLocked(r);
+        }
+        NotificationRecord r = new NotificationRecord(mContext,
+                generateSbn("p" + 3, 3, 300000, 0), mTestNotificationChannel);
+        expected.remove(new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(3)
+                .setLastNotified(300));
+        NotifyingApp newest = new NotifyingApp()
+                .setPackage(r.sbn.getPackageName())
+                .setUid(r.sbn.getUid())
+                .setLastNotified(r.sbn.getPostTime());
+        expected.add(newest);
+        mService.logRecentLocked(r);
+
+        List<NotifyingApp> apps = mBinderService.getRecentNotifyingAppsForUser(0).getList();
+        assertTrue(apps.size() == 5);
+        for (NotifyingApp actual : apps) {
+            assertTrue("got unexpected result: " + actual, expected.contains(actual));
+        }
+        assertEquals(newest, apps.get(0));
+    }
+
+    @Test
+    public void testRecentsMultiuser() throws Exception {
+        final NotificationRecord user1 = new NotificationRecord(mContext,
+                generateSbn("p", 1000, 9, 1), mTestNotificationChannel);
+        mService.logRecentLocked(user1);
+
+        final NotificationRecord user2 = new NotificationRecord(mContext,
+                generateSbn("p2", 100000, 9999, 2), mTestNotificationChannel);
+        mService.logRecentLocked(user2);
+
+        assertEquals(0, mBinderService.getRecentNotifyingAppsForUser(0).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(1).getList().size());
+        assertEquals(1, mBinderService.getRecentNotifyingAppsForUser(2).getList().size());
+
+        assertTrue(mBinderService.getRecentNotifyingAppsForUser(2).getList().contains(
+                new NotifyingApp()
+                        .setPackage(user2.sbn.getPackageName())
+                        .setUid(user2.sbn.getUid())
+                        .setLastNotified(user2.sbn.getPostTime())));
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
index 4f153ee..0a630f4 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationStatsTest.java
@@ -1,3 +1,18 @@
+/**
+ * Copyright (c) 2017, 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.notification;
 
 import static android.service.notification.NotificationStats.DISMISSAL_PEEK;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
new file mode 100644
index 0000000..fbb8c33
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotifyingAppTest.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright (c) 2018, 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.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.os.Parcel;
+import android.service.notification.NotifyingApp;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NotifyingAppTest extends UiServiceTestCase {
+
+    @Test
+    public void testConstructor() {
+        NotifyingApp na = new NotifyingApp();
+        assertEquals(0, na.getUid());
+        assertEquals(0, na.getLastNotified());
+        assertEquals(null, na.getPackage());
+    }
+
+    @Test
+    public void testPackage() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("test");
+        assertEquals("test", na.getPackage());
+    }
+
+    @Test
+    public void testUid() {
+        NotifyingApp na = new NotifyingApp();
+        na.setUid(90);
+        assertEquals(90, na.getUid());
+    }
+
+    @Test
+    public void testLastNotified() {
+        NotifyingApp na = new NotifyingApp();
+        na.setLastNotified((long) 8000);
+        assertEquals((long) 8000, na.getLastNotified());
+    }
+
+    @Test
+    public void testWriteToParcel() {
+        NotifyingApp na = new NotifyingApp();
+        na.setPackage("package");
+        na.setUid(200);
+        na.setLastNotified(4000);
+
+        Parcel parcel = Parcel.obtain();
+        na.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotifyingApp na1 = NotifyingApp.CREATOR.createFromParcel(parcel);
+        assertEquals(na.getLastNotified(), na1.getLastNotified());
+        assertEquals(na.getPackage(), na1.getPackage());
+        assertEquals(na.getUid(), na1.getUid());
+    }
+
+    @Test
+    public void testCompareTo() {
+        NotifyingApp na1 = new NotifyingApp();
+        na1.setPackage("pkg1");
+        na1.setUid(1000);
+        na1.setLastNotified(6);
+
+        NotifyingApp na2 = new NotifyingApp();
+        na2.setPackage("a");
+        na2.setUid(999);
+        na2.setLastNotified(1);
+
+        assertTrue(na1.compareTo(na2) < 0);
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index cb32d1f..4d458b0 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -92,6 +92,17 @@
         return false;
     }
 
+    /**
+     * Returns whether the event type is one caused by user visible
+     * interaction. Excludes those that are internally generated.
+     * @param eventType
+     * @return
+     */
+    private boolean isUserVisibleEvent(int eventType) {
+        return eventType != UsageEvents.Event.SYSTEM_INTERACTION
+                && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
+    }
+
     void update(String packageName, long timeStamp, int eventType) {
         UsageStats usageStats = getOrCreateUsageStats(packageName);
 
@@ -109,7 +120,7 @@
             usageStats.mLastEvent = eventType;
         }
 
-        if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
+        if (isUserVisibleEvent(eventType)) {
             usageStats.mLastTimeUsed = timeStamp;
         }
         usageStats.mEndTimeStamp = timeStamp;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 979feaa..096fdcc 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -115,6 +115,26 @@
 
     AppStandbyController mAppStandby;
 
+    private UsageStatsManagerInternal.AppIdleStateChangeListener mStandbyChangeListener =
+            new UsageStatsManagerInternal.AppIdleStateChangeListener() {
+                @Override
+                public void onAppIdleStateChanged(String packageName, int userId, boolean idle,
+                        int bucket) {
+                    Event event = new Event();
+                    event.mEventType = Event.STANDBY_BUCKET_CHANGED;
+                    event.mBucket = bucket;
+                    event.mPackage = packageName;
+                    // This will later be converted to system time.
+                    event.mTimeStamp = SystemClock.elapsedRealtime();
+                    mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
+                }
+
+                @Override
+                public void onParoleStateChanged(boolean isParoleOn) {
+
+                }
+            };
+
     public UsageStatsService(Context context) {
         super(context);
     }
@@ -129,6 +149,7 @@
 
         mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());
 
+        mAppStandby.addListener(mStandbyChangeListener);
         File systemDataDir = new File(Environment.getDataDirectory(), "system");
         mUsageStatsDir = new File(systemDataDir, "usagestats");
         mUsageStatsDir.mkdirs();
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index cc53a9c..d1ed599 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -64,6 +64,7 @@
     private static final String LAST_EVENT_ATTR = "lastEvent";
     private static final String TYPE_ATTR = "type";
     private static final String SHORTCUT_ID_ATTR = "shortcutId";
+    private static final String STANDBY_BUCKET_ATTR = "standbyBucket";
 
     // Time attributes stored as an offset of the beginTime.
     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
@@ -173,6 +174,9 @@
                 final String id = XmlUtils.readStringAttribute(parser, SHORTCUT_ID_ATTR);
                 event.mShortcutId = (id != null) ? id.intern() : null;
                 break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                event.mBucket = XmlUtils.readIntAttribute(parser, STANDBY_BUCKET_ATTR, 0);
+                break;
         }
 
         if (statsOut.events == null) {
@@ -276,6 +280,10 @@
                     XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId);
                 }
                 break;
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                if (event.mBucket != 0) {
+                    XmlUtils.writeIntAttribute(xml, STANDBY_BUCKET_ATTR, event.mBucket);
+                }
         }
 
         xml.endTag(null, EVENT_TAG);
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index f02221c..ec12da2 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -599,6 +599,9 @@
             if (event.mShortcutId != null) {
                 pw.printPair("shortcutId", event.mShortcutId);
             }
+            if (event.mEventType == UsageEvents.Event.STANDBY_BUCKET_CHANGED) {
+                pw.printPair("standbyBucket", event.mBucket);
+            }
             pw.printHexPair("flags", event.mFlags);
             pw.println();
         }
@@ -645,6 +648,8 @@
                 return "CHOOSER_ACTION";
             case UsageEvents.Event.NOTIFICATION_SEEN:
                 return "NOTIFICATION_SEEN";
+            case UsageEvents.Event.STANDBY_BUCKET_CHANGED:
+                return "STANDBY_BUCKET_CHANGED";
             default:
                 return "UNKNOWN";
         }
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index fc814be..703f96d 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import android.annotation.SystemApi;
+
 /**
  * Contains access network related constants.
  */
@@ -30,6 +32,18 @@
     }
 
     /**
+     * Wireless transportation type
+     * @hide
+     */
+    @SystemApi
+    public static final class TransportType {
+        /** Wireless Wide Area Networks (i.e. Cellular) */
+        public static final int WWAN = 1;
+        /** Wireless Local Area Networks (i.e. Wifi) */
+        public static final int WLAN = 2;
+    }
+
+    /**
      * Frenquency bands for GERAN.
      * http://www.etsi.org/deliver/etsi_ts/145000_145099/145005/14.00.00_60/ts_145005v140000p.pdf
      */
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 649d478..cbc9428 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -275,7 +275,6 @@
      *
      * @see SubscriptionManager#getSubscriptionPlans(int)
      * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
-     * @hide
      */
     @SystemApi
     public static final String KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING =
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index d2134f9..fc2ef27 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -51,12 +51,15 @@
         "none", "poor", "moderate", "good", "great"
     };
 
-    /** @hide */
-    //Use int max, as -1 is a valid value in signal strength
-    public static final int INVALID = 0x7FFFFFFF;
+    /**
+     * Use Integer.MAX_VALUE because -1 is a valid value in signal strength.
+     * @hide
+     */
+    public static final int INVALID = Integer.MAX_VALUE;
 
     private static final int LTE_RSRP_THRESHOLDS_NUM = 6;
 
+    /** Parameters reported by the Radio */
     private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
     private int mGsmBitErrorRate;   // bit error rate (0-7, 99) as defined in TS 27.007 8.5
     private int mCdmaDbm;   // This value is the RSSI value
@@ -69,11 +72,13 @@
     private int mLteRsrq;
     private int mLteRssnr;
     private int mLteCqi;
-    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
-                                // signal strength level
     private int mTdScdmaRscp;
 
-    private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+    /** Parameters from the framework */
+    private int mLteRsrpBoost; // offset to be reduced from the rsrp threshold while calculating
+                                // signal strength level
+    private boolean mIsGsm; // This value is set by the ServiceStateTracker
+                            // onSignalStrengthResult.
     private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
 
     // The threshold of LTE RSRP for determining the display level of LTE signal bar.
@@ -103,28 +108,12 @@
      * @hide
      */
     public SignalStrength() {
-        mGsmSignalStrength = 99;
-        mGsmBitErrorRate = -1;
-        mCdmaDbm = -1;
-        mCdmaEcio = -1;
-        mEvdoDbm = -1;
-        mEvdoEcio = -1;
-        mEvdoSnr = -1;
-        mLteSignalStrength = 99;
-        mLteRsrp = INVALID;
-        mLteRsrq = INVALID;
-        mLteRssnr = INVALID;
-        mLteCqi = INVALID;
-        mLteRsrpBoost = 0;
-        mTdScdmaRscp = INVALID;
-        isGsm = true;
-        mUseOnlyRsrpForLteLevel = false;
-        setLteRsrpThresholds(getDefaultLteRsrpThresholds());
+        this(true);
     }
 
     /**
      * This constructor is used to create SignalStrength with default
-     * values and set the isGsmFlag with the value passed in the input
+     * values and set the gsmFlag with the value passed in the input
      *
      * @param gsmFlag true if Gsm Phone,false if Cdma phone
      * @return newly created SignalStrength
@@ -143,134 +132,26 @@
         mLteRsrq = INVALID;
         mLteRssnr = INVALID;
         mLteCqi = INVALID;
-        mLteRsrpBoost = 0;
         mTdScdmaRscp = INVALID;
-        isGsm = gsmFlag;
+        mLteRsrpBoost = 0;
+        mIsGsm = gsmFlag;
         mUseOnlyRsrpForLteLevel = false;
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
     }
 
     /**
-     * Constructor
+     * Constructor with all fields present
      *
      * @hide
      */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+    public SignalStrength(
+            int gsmSignalStrength, int gsmBitErrorRate,
             int cdmaDbm, int cdmaEcio,
             int evdoDbm, int evdoEcio, int evdoSnr,
             int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
-        mTdScdmaRscp = tdScdmaRscp;
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int tdScdmaRscp, boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
-        mTdScdmaRscp = tdScdmaRscp;
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
-                lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
-    }
-
-    /**
-     * Constructor
-     *
-     * @hide
-     */
-    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            boolean gsmFlag) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsmFlag, false);
-    }
-
-    /**
-     * Copy constructors
-     *
-     * @param s Source SignalStrength
-     *
-     * @hide
-     */
-    public SignalStrength(SignalStrength s) {
-        copyFrom(s);
-    }
-
-    /**
-     * Initialize gsm/cdma values, sets lte values to defaults.
-     *
-     * @param gsmSignalStrength
-     * @param gsmBitErrorRate
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     * @param gsm
-     *
-     * @hide
-     */
-    public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            boolean gsm) {
-        initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
-                evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
-                INVALID, INVALID, INVALID, 0, gsm, false);
-    }
-
-    /**
-     * Initialize all the values
-     *
-     * @param gsmSignalStrength
-     * @param gsmBitErrorRate
-     * @param cdmaDbm
-     * @param cdmaEcio
-     * @param evdoDbm
-     * @param evdoEcio
-     * @param evdoSnr
-     * @param lteSignalStrength
-     * @param lteRsrp
-     * @param lteRsrq
-     * @param lteRssnr
-     * @param lteCqi
-     * @param lteRsrpBoost
-     * @param gsm
-     * @param useOnlyRsrpForLteLevel
-     *
-     * @hide
-     */
-    public void initialize(int gsmSignalStrength, int gsmBitErrorRate,
-            int cdmaDbm, int cdmaEcio,
-            int evdoDbm, int evdoEcio, int evdoSnr,
-            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
-            int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
+            int tdScdmaRscp,
+            // values Added by config
+            int lteRsrpBoost, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
         mGsmSignalStrength = gsmSignalStrength;
         mGsmBitErrorRate = gsmBitErrorRate;
         mCdmaDbm = cdmaDbm;
@@ -283,16 +164,41 @@
         mLteRsrq = lteRsrq;
         mLteRssnr = lteRssnr;
         mLteCqi = lteCqi;
-        mLteRsrpBoost = lteRsrpBoost;
         mTdScdmaRscp = INVALID;
-        isGsm = gsm;
-        mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
-
+        mLteRsrpBoost = lteRsrpBoost;
+        mIsGsm = gsmFlag;
+        mUseOnlyRsrpForLteLevel = lteLevelBaseOnRsrp;
         setLteRsrpThresholds(getDefaultLteRsrpThresholds());
         if (DBG) log("initialize: " + toString());
     }
 
     /**
+     * Constructor for only values provided by Radio HAL
+     *
+     * @hide
+     */
+    public SignalStrength(int gsmSignalStrength, int gsmBitErrorRate,
+            int cdmaDbm, int cdmaEcio,
+            int evdoDbm, int evdoEcio, int evdoSnr,
+            int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
+            int tdScdmaRscp) {
+        this(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
+                evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
+                lteRsrq, lteRssnr, lteCqi, tdScdmaRscp, 0, true, false);
+    }
+
+    /**
+     * Copy constructors
+     *
+     * @param s Source SignalStrength
+     *
+     * @hide
+     */
+    public SignalStrength(SignalStrength s) {
+        copyFrom(s);
+    }
+
+    /**
      * @hide
      */
     protected void copyFrom(SignalStrength s) {
@@ -308,9 +214,9 @@
         mLteRsrq = s.mLteRsrq;
         mLteRssnr = s.mLteRssnr;
         mLteCqi = s.mLteCqi;
-        mLteRsrpBoost = s.mLteRsrpBoost;
         mTdScdmaRscp = s.mTdScdmaRscp;
-        isGsm = s.isGsm;
+        mLteRsrpBoost = s.mLteRsrpBoost;
+        mIsGsm = s.mIsGsm;
         mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
         setLteRsrpThresholds(s.mLteRsrpThresholds);
     }
@@ -335,40 +241,11 @@
         mLteRsrq = in.readInt();
         mLteRssnr = in.readInt();
         mLteCqi = in.readInt();
-        mLteRsrpBoost = in.readInt();
         mTdScdmaRscp = in.readInt();
-        isGsm = (in.readInt() != 0);
-        mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
-        for (int i = 0; i < LTE_RSRP_THRESHOLDS_NUM; i++) {
-            mLteRsrpThresholds[i] = in.readInt();
-        }
-    }
-
-    /**
-     * Make a SignalStrength object from the given parcel as passed up by
-     * the ril which does not have isGsm. isGsm will be changed by ServiceStateTracker
-     * so the default is a don't care.
-     *
-     * @hide
-     */
-    public static SignalStrength makeSignalStrengthFromRilParcel(Parcel in) {
-        if (DBG) log("Size of signalstrength parcel:" + in.dataSize());
-
-        SignalStrength ss = new SignalStrength();
-        ss.mGsmSignalStrength = in.readInt();
-        ss.mGsmBitErrorRate = in.readInt();
-        ss.mCdmaDbm = in.readInt();
-        ss.mCdmaEcio = in.readInt();
-        ss.mEvdoDbm = in.readInt();
-        ss.mEvdoEcio = in.readInt();
-        ss.mEvdoSnr = in.readInt();
-        ss.mLteSignalStrength = in.readInt();
-        ss.mLteRsrp = in.readInt();
-        ss.mLteRsrq = in.readInt();
-        ss.mLteRssnr = in.readInt();
-        ss.mLteCqi = in.readInt();
-        ss.mTdScdmaRscp = in.readInt();
-        return ss;
+        mLteRsrpBoost = in.readInt();
+        mIsGsm = in.readBoolean();
+        mUseOnlyRsrpForLteLevel = in.readBoolean();
+        in.readIntArray(mLteRsrpThresholds);
     }
 
     /**
@@ -387,13 +264,11 @@
         out.writeInt(mLteRsrq);
         out.writeInt(mLteRssnr);
         out.writeInt(mLteCqi);
-        out.writeInt(mLteRsrpBoost);
         out.writeInt(mTdScdmaRscp);
-        out.writeInt(isGsm ? 1 : 0);
-        out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
-        for (int i = 0; i < LTE_RSRP_THRESHOLDS_NUM; i++) {
-            out.writeInt(mLteRsrpThresholds[i]);
-        }
+        out.writeInt(mLteRsrpBoost);
+        out.writeBoolean(mIsGsm);
+        out.writeBoolean(mUseOnlyRsrpForLteLevel);
+        out.writeIntArray(mLteRsrpThresholds);
     }
 
     /**
@@ -456,24 +331,24 @@
     }
 
     /**
-     * Fix {@link #isGsm} based on the signal strength data.
+     * Fix {@link #mIsGsm} based on the signal strength data.
      *
      * @hide
      */
     public void fixType() {
-        isGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+        mIsGsm = getCdmaRelatedSignalStrength() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
     }
 
     /**
      * @param true - Gsm, Lte phones
      *        false - Cdma phones
      *
-     * Used by voice phone to set the isGsm
+     * Used by voice phone to set the mIsGsm
      *        flag
      * @hide
      */
     public void setGsm(boolean gsmFlag) {
-        isGsm = gsmFlag;
+        mIsGsm = gsmFlag;
     }
 
     /**
@@ -604,7 +479,7 @@
      *     while 4 represents a very strong signal strength.
      */
     public int getLevel() {
-        int level = isGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
+        int level = mIsGsm ? getGsmRelatedSignalStrength() : getCdmaRelatedSignalStrength();
         if (DBG) log("getLevel=" + level);
         return level;
     }
@@ -616,15 +491,13 @@
      */
     public int getAsuLevel() {
         int asuLevel = 0;
-        if (isGsm) {
-            if (getLteLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                if (getTdScdmaLevel() == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
-                    asuLevel = getGsmAsuLevel();
-                } else {
-                    asuLevel = getTdScdmaAsuLevel();
-                }
-            } else {
+        if (mIsGsm) {
+            if (mLteRsrp != SignalStrength.INVALID) {
                 asuLevel = getLteAsuLevel();
+            } else if (mTdScdmaRscp != SignalStrength.INVALID) {
+                asuLevel = getTdScdmaAsuLevel();
+            } else {
+                asuLevel = getGsmAsuLevel();
             }
         } else {
             int cdmaAsuLevel = getCdmaAsuLevel();
@@ -966,7 +839,7 @@
      * @return true if this is for GSM
      */
     public boolean isGsm() {
-        return this.isGsm;
+        return this.mIsGsm;
     }
 
     /**
@@ -1038,7 +911,7 @@
                 + (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
                 + (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
                 + (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
-                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
+                + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (mIsGsm ? 1 : 0)
                 + (mUseOnlyRsrpForLteLevel ? 1 : 0) + (Arrays.hashCode(mLteRsrpThresholds)));
     }
 
@@ -1073,7 +946,7 @@
                 && mLteCqi == s.mLteCqi
                 && mLteRsrpBoost == s.mLteRsrpBoost
                 && mTdScdmaRscp == s.mTdScdmaRscp
-                && isGsm == s.isGsm
+                && mIsGsm == s.mIsGsm
                 && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel
                 && Arrays.equals(mLteRsrpThresholds, s.mLteRsrpThresholds));
     }
@@ -1098,7 +971,7 @@
                 + " " + mLteCqi
                 + " " + mLteRsrpBoost
                 + " " + mTdScdmaRscp
-                + " " + (isGsm ? "gsm|lte" : "cdma")
+                + " " + (mIsGsm ? "gsm|lte" : "cdma")
                 + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
                          "use_rsrp_and_rssnr_for_lte_level")
                 + " " + (Arrays.toString(mLteRsrpThresholds)));
@@ -1153,10 +1026,10 @@
         mLteRsrq = m.getInt("LteRsrq");
         mLteRssnr = m.getInt("LteRssnr");
         mLteCqi = m.getInt("LteCqi");
-        mLteRsrpBoost = m.getInt("lteRsrpBoost");
+        mLteRsrpBoost = m.getInt("LteRsrpBoost");
         mTdScdmaRscp = m.getInt("TdScdma");
-        isGsm = m.getBoolean("isGsm");
-        mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
+        mIsGsm = m.getBoolean("IsGsm");
+        mUseOnlyRsrpForLteLevel = m.getBoolean("UseOnlyRsrpForLteLevel");
         ArrayList<Integer> lteRsrpThresholds = m.getIntegerArrayList("lteRsrpThresholds");
         for (int i = 0; i < lteRsrpThresholds.size(); i++) {
             mLteRsrpThresholds[i] = lteRsrpThresholds.get(i);
@@ -1182,10 +1055,10 @@
         m.putInt("LteRsrq", mLteRsrq);
         m.putInt("LteRssnr", mLteRssnr);
         m.putInt("LteCqi", mLteCqi);
-        m.putInt("lteRsrpBoost", mLteRsrpBoost);
+        m.putInt("LteRsrpBoost", mLteRsrpBoost);
         m.putInt("TdScdma", mTdScdmaRscp);
-        m.putBoolean("isGsm", isGsm);
-        m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
+        m.putBoolean("IsGsm", mIsGsm);
+        m.putBoolean("UseOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
         ArrayList<Integer> lteRsrpThresholds = new ArrayList<Integer>();
         for (int value : mLteRsrpThresholds) {
             lteRsrpThresholds.add(value);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 57f4cf2..debf43d 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -462,8 +462,6 @@
      * <p>
      * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
      * the user is interested in.
-     *
-     * @hide
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     @SystemApi
@@ -481,8 +479,6 @@
      * <p>
      * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
      * the user is interested in.
-     *
-     * @hide
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @SystemApi
@@ -1698,7 +1694,6 @@
      * </ul>
      *
      * @param subId the subscriber this relationship applies to
-     * @hide
      */
     @SystemApi
     public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
@@ -1728,7 +1723,6 @@
      * @param plans the list of plans. The first plan is always the primary and
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
-     * @hide
      */
     @SystemApi
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
@@ -1769,7 +1763,6 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
-     * @hide
      */
     @SystemApi
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@@ -1804,7 +1797,6 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
-     * @hide
      */
     @SystemApi
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index 265e3e7..9411652 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -43,7 +43,6 @@
  *
  * @see SubscriptionManager#setSubscriptionPlans(int, java.util.List)
  * @see SubscriptionManager#getSubscriptionPlans(int)
- * @hide
  */
 @SystemApi
 public final class SubscriptionPlan implements Parcelable {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 17f809d..e33b25e 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2136,8 +2136,8 @@
     public static final int SIM_STATE_PRESENT = 11;
 
     /**
-     * Extra included in {@link Intent.ACTION_SIM_CARD_STATE_CHANGED} and
-     * {@link Intent.ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
+     * Extra included in {@link #ACTION_SIM_CARD_STATE_CHANGED} and
+     * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} to indicate the card/application state.
      *
      * @hide
      */
@@ -2145,6 +2145,89 @@
     public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
 
     /**
+     * Broadcast Action: The sim card state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim card state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_ABSENT}</dt>
+     *       <dd>SIM card not found</dd>
+     *       <dt>{@link #SIM_STATE_CARD_IO_ERROR}</dt>
+     *       <dd>SIM card IO error</dd>
+     *       <dt>{@link #SIM_STATE_CARD_RESTRICTED}</dt>
+     *       <dd>SIM card is restricted</dd>
+     *       <dt>{@link #SIM_STATE_PRESENT}</dt>
+     *       <dd>SIM card is present</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using {@link #getSimCardState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_CARD_STATE_CHANGED =
+            "android.telephony.action.SIM_CARD_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The sim application state has changed.
+     * The intent will have the following extra values:</p>
+     * <dl>
+     *   <dt>{@link #EXTRA_SIM_STATE}</dt>
+     *   <dd>The sim application state. One of:
+     *     <dl>
+     *       <dt>{@link #SIM_STATE_NOT_READY}</dt>
+     *       <dd>SIM card applications not ready</dd>
+     *       <dt>{@link #SIM_STATE_PIN_REQUIRED}</dt>
+     *       <dd>SIM card PIN locked</dd>
+     *       <dt>{@link #SIM_STATE_PUK_REQUIRED}</dt>
+     *       <dd>SIM card PUK locked</dd>
+     *       <dt>{@link #SIM_STATE_NETWORK_LOCKED}</dt>
+     *       <dd>SIM card network locked</dd>
+     *       <dt>{@link #SIM_STATE_PERM_DISABLED}</dt>
+     *       <dd>SIM card permanently disabled due to PUK failures</dd>
+     *       <dt>{@link #SIM_STATE_LOADED}</dt>
+     *       <dd>SIM card data loaded</dd>
+     *     </dl>
+     *   </dd>
+     * </dl>
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The current state can also be queried using
+     * {@link #getSimApplicationState()}.
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_APPLICATION_STATE_CHANGED =
+            "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: Status of the SIM slots on the device has changed.
+     *
+     * <p class="note">Requires the READ_PRIVILEGED_PHONE_STATE permission.
+     *
+     * <p class="note">The status can be queried using
+     * {@link #getUiccSlotsInfo()}
+     *
+     * <p class="note">This is a protected intent that can only be sent by the system.
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SIM_SLOT_STATUS_CHANGED =
+            "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
+
+    /**
      * @return true if a ICC card is present
      */
     public boolean hasIccCard() {
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index ea08175..fa19ea0 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -17,6 +17,7 @@
 package android.telephony.data;
 
 import android.annotation.CallSuper;
+import android.annotation.IntDef;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.Intent;
@@ -32,6 +33,8 @@
 import android.telephony.SubscriptionManager;
 import android.util.SparseArray;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -56,6 +59,33 @@
     public static final String DATA_SERVICE_INTERFACE = "android.telephony.data.DataService";
     public static final String DATA_SERVICE_EXTRA_SLOT_ID = "android.telephony.data.extra.SLOT_ID";
 
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SetupDataReason {}
+
+    /** {@hide} */
+    @IntDef(prefix = "REQUEST_REASON_", value = {
+            REQUEST_REASON_NORMAL,
+            REQUEST_REASON_SHUTDOWN,
+            REQUEST_REASON_HANDOVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DeactivateDataReason {}
+
+
+    /** The reason of the data request is normal */
+    public static final int REQUEST_REASON_NORMAL = 1;
+
+    /** The reason of the data request is device shutdown */
+    public static final int REQUEST_REASON_SHUTDOWN = 2;
+
+    /** The reason of the data request is IWLAN handover */
+    public static final int REQUEST_REASON_HANDOVER = 3;
+
     private static final int DATA_SERVICE_INTERNAL_REQUEST_INITIALIZE_SERVICE          = 1;
     private static final int DATA_SERVICE_REQUEST_SETUP_DATA_CALL                      = 2;
     private static final int DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL                 = 3;
@@ -110,13 +140,14 @@
          * @param dataProfile Data profile used for data call setup. See {@link DataProfile}
          * @param isRoaming True if the device is data roaming.
          * @param allowRoaming True if data roaming is allowed by the user.
-         * @param isHandover True if the request is for IWLAN handover.
-         * @param linkProperties If {@code isHandover} is true, this is the link properties of the
-         * existing data connection, otherwise null.
+         * @param reason The reason for data setup. Must be {@link #REQUEST_REASON_NORMAL} or
+         * {@link #REQUEST_REASON_HANDOVER}.
+         * @param linkProperties If {@code reason} is {@link #REQUEST_REASON_HANDOVER}, this is the
+         * link properties of the existing data connection, otherwise null.
          * @param callback The result callback for this request.
          */
         public void setupDataCall(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-                                  boolean allowRoaming, boolean isHandover,
+                                  boolean allowRoaming, @SetupDataReason int reason,
                                   LinkProperties linkProperties, DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onSetupDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED, null);
@@ -128,12 +159,12 @@
          * provided callback to notify the platform.
          *
          * @param cid Call id returned in the callback of {@link DataServiceProvider#setupDataCall(
-         * int, DataProfile, boolean, boolean, boolean, LinkProperties, DataServiceCallback)}.
-         * @param reasonRadioShutDown True if the deactivate request reason is device shut down.
-         * @param isHandover True if the request is for IWLAN handover.
+         * int, DataProfile, boolean, boolean, int, LinkProperties, DataServiceCallback)}.
+         * @param reason The reason for data deactivation. Must be {@link #REQUEST_REASON_NORMAL},
+         * {@link #REQUEST_REASON_SHUTDOWN} or {@link #REQUEST_REASON_HANDOVER}.
          * @param callback The result callback for this request.
          */
-        public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
+        public void deactivateDataCall(int cid, @DeactivateDataReason int reason,
                                        DataServiceCallback callback) {
             // The default implementation is to return unsupported.
             callback.onDeactivateDataCallComplete(DataServiceCallback.RESULT_ERROR_UNSUPPORTED);
@@ -219,32 +250,29 @@
         public final DataProfile dataProfile;
         public final boolean isRoaming;
         public final boolean allowRoaming;
-        public final boolean isHandover;
+        public final int reason;
         public final LinkProperties linkProperties;
         public final IDataServiceCallback callback;
         SetupDataCallRequest(int accessNetworkType, DataProfile dataProfile, boolean isRoaming,
-                             boolean allowRoaming, boolean isHandover,
-                             LinkProperties linkProperties, IDataServiceCallback callback) {
+                             boolean allowRoaming, int reason, LinkProperties linkProperties,
+                             IDataServiceCallback callback) {
             this.accessNetworkType = accessNetworkType;
             this.dataProfile = dataProfile;
             this.isRoaming = isRoaming;
             this.allowRoaming = allowRoaming;
             this.linkProperties = linkProperties;
-            this.isHandover = isHandover;
+            this.reason = reason;
             this.callback = callback;
         }
     }
 
     private static final class DeactivateDataCallRequest {
         public final int cid;
-        public final boolean reasonRadioShutDown;
-        public final boolean isHandover;
+        public final int reason;
         public final IDataServiceCallback callback;
-        DeactivateDataCallRequest(int cid, boolean reasonRadioShutDown, boolean isHandover,
-                                  IDataServiceCallback callback) {
+        DeactivateDataCallRequest(int cid, int reason, IDataServiceCallback callback) {
             this.cid = cid;
-            this.reasonRadioShutDown = reasonRadioShutDown;
-            this.isHandover = isHandover;
+            this.reason = reason;
             this.callback = callback;
         }
     }
@@ -311,7 +339,7 @@
                     SetupDataCallRequest setupDataCallRequest = (SetupDataCallRequest) message.obj;
                     service.setupDataCall(setupDataCallRequest.accessNetworkType,
                             setupDataCallRequest.dataProfile, setupDataCallRequest.isRoaming,
-                            setupDataCallRequest.allowRoaming, setupDataCallRequest.isHandover,
+                            setupDataCallRequest.allowRoaming, setupDataCallRequest.reason,
                             setupDataCallRequest.linkProperties,
                             new DataServiceCallback(setupDataCallRequest.callback));
 
@@ -321,8 +349,7 @@
                     DeactivateDataCallRequest deactivateDataCallRequest =
                             (DeactivateDataCallRequest) message.obj;
                     service.deactivateDataCall(deactivateDataCallRequest.cid,
-                            deactivateDataCallRequest.reasonRadioShutDown,
-                            deactivateDataCallRequest.isHandover,
+                            deactivateDataCallRequest.reason,
                             new DataServiceCallback(deactivateDataCallRequest.callback));
                     break;
                 case DATA_SERVICE_REQUEST_SET_INITIAL_ATTACH_APN:
@@ -370,7 +397,8 @@
         }
     }
 
-    private DataService() {
+    /** @hide */
+    protected DataService() {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
 
@@ -472,19 +500,18 @@
 
         @Override
         public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
-                                  boolean isRoaming, boolean allowRoaming, boolean isHandover,
+                                  boolean isRoaming, boolean allowRoaming, int reason,
                                   LinkProperties linkProperties, IDataServiceCallback callback) {
             mHandler.obtainMessage(DATA_SERVICE_REQUEST_SETUP_DATA_CALL, mSlotId, 0,
                     new SetupDataCallRequest(accessNetworkType, dataProfile, isRoaming,
-                            allowRoaming, isHandover, linkProperties, callback))
+                            allowRoaming, reason, linkProperties, callback))
                     .sendToTarget();
         }
 
         @Override
-        public void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
-                                       IDataServiceCallback callback) {
+        public void deactivateDataCall(int cid, int reason, IDataServiceCallback callback) {
             mHandler.obtainMessage(DATA_SERVICE_REQUEST_DEACTIVATE_DATA_CALL, mSlotId, 0,
-                    new DeactivateDataCallRequest(cid, reasonRadioShutDown, isHandover, callback))
+                    new DeactivateDataCallRequest(cid, reason, callback))
                     .sendToTarget();
         }
 
diff --git a/telephony/java/android/telephony/data/IDataService.aidl b/telephony/java/android/telephony/data/IDataService.aidl
index 4eaaa252..07720b6 100644
--- a/telephony/java/android/telephony/data/IDataService.aidl
+++ b/telephony/java/android/telephony/data/IDataService.aidl
@@ -26,10 +26,9 @@
 oneway interface IDataService
 {
     void setupDataCall(int accessNetwork, in DataProfile dataProfile, boolean isRoaming,
-                       boolean allowRoaming, boolean isHandover, in LinkProperties linkProperties,
+                       boolean allowRoaming, int reason, in LinkProperties linkProperties,
                        IDataServiceCallback callback);
-    void deactivateDataCall(int cid, boolean reasonRadioShutDown, boolean isHandover,
-                            IDataServiceCallback callback);
+    void deactivateDataCall(int cid, int reason, IDataServiceCallback callback);
     void setInitialAttachApn(in DataProfile dataProfile, boolean isRoaming,
                              IDataServiceCallback callback);
     void setDataProfile(in List<DataProfile> dps, boolean isRoaming, IDataServiceCallback callback);
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index d146707..7f913ce 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.app.Activity;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -598,7 +599,11 @@
         }
     }
 
-    private static IEuiccController getIEuiccController() {
+    /**
+     * @hide
+     */
+    @TestApi
+    protected IEuiccController getIEuiccController() {
         return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller"));
     }
 }
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 13e3693..1ddc52c 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -108,6 +108,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public Intent getCarLaunchIntentForPackage(String packageName) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public int[] getPackageGids(String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException();
@@ -1190,4 +1196,17 @@
     public CharSequence getHarmfulAppWarning(String packageName) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public boolean hasSigningCertificate(
+            String packageName, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean hasSigningCertificate(
+            int uid, byte[] certificate, @PackageManager.CertificateInputType int type) {
+        throw new UnsupportedOperationException();
+    }
+
 }
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index d9d4eeb..1618e07 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -268,6 +268,31 @@
                         anyInt());
     }
 
+    public void testCreateTwoTransformsWithSameSpis() throws Exception {
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+
+        IpSecTransformResponse createTransformResp =
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+        assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+        // Attempting to create transform a second time with the same SPIs should throw an error...
+        try {
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+                fail("IpSecService should have thrown an error for reuse of SPI");
+        } catch (IllegalStateException expected) {
+        }
+
+        // ... even if the transform is deleted
+        mIpSecService.deleteTransform(createTransformResp.resourceId);
+        try {
+                mIpSecService.createTransform(ipSecConfig, new Binder());
+                fail("IpSecService should have thrown an error for reuse of SPI");
+        } catch (IllegalStateException expected) {
+        }
+    }
+
     @Test
     public void testDeleteTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();