Merge "Add more API on hiddenapi-light-greylist.txt" into pi-dev
diff --git a/Android.bp b/Android.bp
index ce93568..9c76e14 100644
--- a/Android.bp
+++ b/Android.bp
@@ -145,7 +145,7 @@
         ":libcamera_client_framework_aidl",
         "core/java/android/hardware/IConsumerIrService.aidl",
         "core/java/android/hardware/ISerialManager.aidl",
-        "core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl",
+        "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 6aa34a6..1b28913 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -23,5 +23,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.perftests.core" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
index 6ede827..512dbc9 100644
--- a/apct-tests/perftests/multiuser/AndroidTest.xml
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -23,5 +23,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.perftests.multiuser" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/api/current.txt b/api/current.txt
index 527d364..9cb64d6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6749,6 +6749,12 @@
     field public static final android.os.Parcelable.Creator<android.app.admin.DnsEvent> CREATOR;
   }
 
+  public class FreezePeriod {
+    ctor public FreezePeriod(java.time.MonthDay, java.time.MonthDay);
+    method public java.time.MonthDay getEnd();
+    method public java.time.MonthDay getStart();
+  }
+
   public abstract class NetworkEvent implements android.os.Parcelable {
     method public int describeContents();
     method public long getId();
@@ -6819,16 +6825,16 @@
     field public static final int SECURITY_PATCH_STATE_UNKNOWN = 0; // 0x0
   }
 
-  public class SystemUpdatePolicy implements android.os.Parcelable {
+  public final class SystemUpdatePolicy implements android.os.Parcelable {
     method public static android.app.admin.SystemUpdatePolicy createAutomaticInstallPolicy();
     method public static android.app.admin.SystemUpdatePolicy createPostponeInstallPolicy();
     method public static android.app.admin.SystemUpdatePolicy createWindowedInstallPolicy(int, int);
     method public int describeContents();
-    method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getFreezePeriods();
+    method public java.util.List<android.app.admin.FreezePeriod> getFreezePeriods();
     method public int getInstallWindowEnd();
     method public int getInstallWindowStart();
     method public int getPolicyType();
-    method public android.app.admin.SystemUpdatePolicy setFreezePeriods(java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>>);
+    method public android.app.admin.SystemUpdatePolicy setFreezePeriods(java.util.List<android.app.admin.FreezePeriod>);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy> CREATOR;
     field public static final int TYPE_INSTALL_AUTOMATIC = 1; // 0x1
@@ -6841,11 +6847,12 @@
     method public int getErrorCode();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.admin.SystemUpdatePolicy.ValidationFailedException> CREATOR;
-    field public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 5; // 0x5
-    field public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 4; // 0x4
-    field public static final int ERROR_DUPLICATE_OR_OVERLAP = 1; // 0x1
-    field public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 3; // 0x3
-    field public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 2; // 0x2
+    field public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 6; // 0x6
+    field public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 5; // 0x5
+    field public static final int ERROR_DUPLICATE_OR_OVERLAP = 2; // 0x2
+    field public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 4; // 0x4
+    field public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 3; // 0x3
+    field public static final int ERROR_UNKNOWN = 1; // 0x1
   }
 
 }
@@ -13658,17 +13665,17 @@
     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 int getAllocator();
-    method public boolean getConserveMemory();
     method public android.graphics.Rect getCrop();
+    method public int getMemorySizePolicy();
     method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
     method public android.graphics.PostProcessor getPostProcessor();
     method public boolean isDecodeAsAlphaMaskEnabled();
     method public boolean isMutableRequired();
     method public boolean isUnpremultipliedRequired();
     method public void setAllocator(int);
-    method public void setConserveMemory(boolean);
     method public void setCrop(android.graphics.Rect);
     method public void setDecodeAsAlphaMaskEnabled(boolean);
+    method public void setMemorySizePolicy(int);
     method public void setMutableRequired(boolean);
     method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
     method public void setPostProcessor(android.graphics.PostProcessor);
@@ -13680,6 +13687,8 @@
     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 MEMORY_POLICY_DEFAULT = 1; // 0x1
+    field public static final int MEMORY_POLICY_LOW_RAM = 0; // 0x0
   }
 
   public static final class ImageDecoder.DecodeException extends java.io.IOException {
@@ -15756,9 +15765,9 @@
 
 package android.hardware.biometrics {
 
-  public class BiometricDialog {
-    method public void authenticate(android.hardware.biometrics.BiometricDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricDialog.AuthenticationCallback);
-    method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricDialog.AuthenticationCallback);
+  public class BiometricPrompt {
+    method public void authenticate(android.hardware.biometrics.BiometricPrompt.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
+    method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricPrompt.AuthenticationCallback);
     field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
     field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
     field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
@@ -15778,31 +15787,31 @@
     field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
   }
 
-  public static abstract class BiometricDialog.AuthenticationCallback {
-    ctor public BiometricDialog.AuthenticationCallback();
+  public static abstract class BiometricPrompt.AuthenticationCallback {
+    ctor public BiometricPrompt.AuthenticationCallback();
     method public void onAuthenticationError(int, java.lang.CharSequence);
     method public void onAuthenticationFailed();
     method public void onAuthenticationHelp(int, java.lang.CharSequence);
-    method public void onAuthenticationSucceeded(android.hardware.biometrics.BiometricDialog.AuthenticationResult);
+    method public void onAuthenticationSucceeded(android.hardware.biometrics.BiometricPrompt.AuthenticationResult);
   }
 
-  public static class BiometricDialog.AuthenticationResult {
-    method public android.hardware.biometrics.BiometricDialog.CryptoObject getCryptoObject();
+  public static class BiometricPrompt.AuthenticationResult {
+    method public android.hardware.biometrics.BiometricPrompt.CryptoObject getCryptoObject();
   }
 
-  public static class BiometricDialog.Builder {
-    ctor public BiometricDialog.Builder(android.content.Context);
-    method public android.hardware.biometrics.BiometricDialog build();
-    method public android.hardware.biometrics.BiometricDialog.Builder setDescription(java.lang.CharSequence);
-    method public android.hardware.biometrics.BiometricDialog.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
-    method public android.hardware.biometrics.BiometricDialog.Builder setSubtitle(java.lang.CharSequence);
-    method public android.hardware.biometrics.BiometricDialog.Builder setTitle(java.lang.CharSequence);
+  public static class BiometricPrompt.Builder {
+    ctor public BiometricPrompt.Builder(android.content.Context);
+    method public android.hardware.biometrics.BiometricPrompt build();
+    method public android.hardware.biometrics.BiometricPrompt.Builder setDescription(java.lang.CharSequence);
+    method public android.hardware.biometrics.BiometricPrompt.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
+    method public android.hardware.biometrics.BiometricPrompt.Builder setSubtitle(java.lang.CharSequence);
+    method public android.hardware.biometrics.BiometricPrompt.Builder setTitle(java.lang.CharSequence);
   }
 
-  public static final class BiometricDialog.CryptoObject {
-    ctor public BiometricDialog.CryptoObject(java.security.Signature);
-    ctor public BiometricDialog.CryptoObject(javax.crypto.Cipher);
-    ctor public BiometricDialog.CryptoObject(javax.crypto.Mac);
+  public static final class BiometricPrompt.CryptoObject {
+    ctor public BiometricPrompt.CryptoObject(java.security.Signature);
+    ctor public BiometricPrompt.CryptoObject(javax.crypto.Cipher);
+    ctor public BiometricPrompt.CryptoObject(javax.crypto.Mac);
     method public javax.crypto.Cipher getCipher();
     method public javax.crypto.Mac getMac();
     method public java.security.Signature getSignature();
@@ -33160,6 +33169,7 @@
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
+    method public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
     method public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
     method public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
@@ -33167,6 +33177,7 @@
     method public android.os.StrictMode.VmPolicy.Builder penaltyDropBox();
     method public android.os.StrictMode.VmPolicy.Builder penaltyListener(java.util.concurrent.Executor, android.os.StrictMode.OnVmViolationListener);
     method public android.os.StrictMode.VmPolicy.Builder penaltyLog();
+    method public android.os.StrictMode.VmPolicy.Builder permitNonSdkApiUsage();
     method public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(java.lang.Class, int);
   }
 
@@ -33581,6 +33592,9 @@
   public final class NetworkViolation extends android.os.strictmode.Violation {
   }
 
+  public final class NonSdkApiUsedViolation extends android.os.strictmode.Violation {
+  }
+
   public final class ResourceMismatchViolation extends android.os.strictmode.Violation {
   }
 
@@ -36679,6 +36693,7 @@
     field public static final java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
     field public static final java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
     field public static final java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
+    field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode";
     field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
     field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
     field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
@@ -36784,7 +36799,6 @@
     field public static final deprecated java.lang.String RADIO_NFC = "nfc";
     field public static final deprecated java.lang.String RADIO_WIFI = "wifi";
     field public static final java.lang.String RINGTONE = "ringtone";
-    field public static final java.lang.String RTT_CALLING_MODE = "rtt_calling_mode";
     field public static final java.lang.String SCREEN_BRIGHTNESS = "screen_brightness";
     field public static final java.lang.String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
     field public static final int SCREEN_BRIGHTNESS_MODE_AUTOMATIC = 1; // 0x1
@@ -38456,23 +38470,10 @@
 
   public abstract class ConfirmationCallback {
     ctor public ConfirmationCallback();
-    method public void onConfirmedByUser(byte[]);
-    method public void onDismissedByApplication();
-    method public void onDismissedByUser();
-    method public void onError(java.lang.Exception);
-  }
-
-  public class ConfirmationDialog {
-    method public void cancelPrompt();
-    method public static boolean isSupported();
-    method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException;
-  }
-
-  public static class ConfirmationDialog.Builder {
-    ctor public ConfirmationDialog.Builder();
-    method public android.security.ConfirmationDialog build(android.content.Context);
-    method public android.security.ConfirmationDialog.Builder setExtraData(byte[]);
-    method public android.security.ConfirmationDialog.Builder setPromptText(java.lang.CharSequence);
+    method public void onCanceled();
+    method public void onConfirmed(byte[]);
+    method public void onDismissed();
+    method public void onError(java.lang.Throwable);
   }
 
   public class ConfirmationNotAvailableException extends java.lang.Exception {
@@ -38480,6 +38481,19 @@
     ctor public ConfirmationNotAvailableException(java.lang.String);
   }
 
+  public class ConfirmationPrompt {
+    method public void cancelPrompt();
+    method public static boolean isSupported(android.content.Context);
+    method public void presentPrompt(java.util.concurrent.Executor, android.security.ConfirmationCallback) throws android.security.ConfirmationAlreadyPresentingException, android.security.ConfirmationNotAvailableException;
+  }
+
+  public static final class ConfirmationPrompt.Builder {
+    ctor public ConfirmationPrompt.Builder(android.content.Context);
+    method public android.security.ConfirmationPrompt build();
+    method public android.security.ConfirmationPrompt.Builder setExtraData(byte[]);
+    method public android.security.ConfirmationPrompt.Builder setPromptText(java.lang.CharSequence);
+  }
+
   public final class KeyChain {
     ctor public KeyChain();
     method public static void choosePrivateKeyAlias(android.app.Activity, android.security.KeyChainAliasCallback, java.lang.String[], java.security.Principal[], java.lang.String, int, java.lang.String);
@@ -44682,7 +44696,7 @@
     method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
     field public static final int ALL = 15; // 0xf
     field public static final int EMAIL_ADDRESSES = 2; // 0x2
-    field public static final int MAP_ADDRESSES = 8; // 0x8
+    field public static final deprecated int MAP_ADDRESSES = 8; // 0x8
     field public static final int PHONE_NUMBERS = 4; // 0x4
     field public static final int WEB_URLS = 1; // 0x1
     field public static final android.text.util.Linkify.MatchFilter sPhoneNumberMatchFilter;
@@ -51301,7 +51315,7 @@
     method public void documentHasImages(android.os.Message);
     method public static void enableSlowWholeDocumentDraw();
     method public void evaluateJavascript(java.lang.String, android.webkit.ValueCallback<java.lang.String>);
-    method public static java.lang.String findAddress(java.lang.String);
+    method public static deprecated java.lang.String findAddress(java.lang.String);
     method public deprecated int findAll(java.lang.String);
     method public void findAllAsync(java.lang.String);
     method public void findNext(boolean);
@@ -51313,7 +51327,6 @@
     method public android.graphics.Bitmap getFavicon();
     method public android.webkit.WebView.HitTestResult getHitTestResult();
     method public deprecated java.lang.String[] getHttpAuthUsernamePassword(java.lang.String, java.lang.String);
-    method public android.os.Looper getLooper();
     method public java.lang.String getOriginalUrl();
     method public int getProgress();
     method public boolean getRendererPriorityWaivedWhenNotVisible();
diff --git a/api/removed.txt b/api/removed.txt
index af2b25e..1d6a8c2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -178,10 +178,12 @@
 
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public deprecated boolean getAsAlphaMask();
+    method public deprecated boolean getConserveMemory();
     method public deprecated boolean getDecodeAsAlphaMask();
     method public deprecated boolean getMutable();
     method public deprecated boolean getRequireUnpremultiplied();
     method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+    method public deprecated void setConserveMemory(boolean);
     method public deprecated android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
     method public deprecated android.graphics.ImageDecoder setMutable(boolean);
     method public deprecated android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
diff --git a/api/system-current.txt b/api/system-current.txt
index c85e2d3..7e95ddc 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -163,6 +163,7 @@
     field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
     field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
     field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
+    field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
     field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
     field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
@@ -451,7 +452,7 @@
     field public static final int STATE_USER_UNMANAGED = 0; // 0x0
   }
 
-  public class SystemUpdatePolicy implements android.os.Parcelable {
+  public final class SystemUpdatePolicy implements android.os.Parcelable {
     method public int describeContents();
     method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
     method public void writeToParcel(android.os.Parcel, int);
@@ -878,6 +879,7 @@
     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_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
     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_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
     field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
@@ -3081,10 +3083,10 @@
   }
 
   public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
-    method public void addAddress(android.net.LinkAddress) throws java.io.IOException;
+    method public void addAddress(java.net.InetAddress, int) throws java.io.IOException;
     method public void close();
     method public java.lang.String getInterfaceName();
-    method public void removeAddress(android.net.LinkAddress) throws java.io.IOException;
+    method public void removeAddress(java.net.InetAddress, int) throws java.io.IOException;
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -4353,6 +4355,7 @@
     method public byte[] getServerParams();
     method public int getSnapshotVersion();
     method public java.security.cert.CertPath getTrustedHardwareCertPath();
+    method public deprecated byte[] getTrustedHardwarePublicKey();
     method public java.util.List<android.security.keystore.recovery.WrappedApplicationKey> getWrappedApplicationKeys();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.security.keystore.recovery.KeyChainSnapshot> CREATOR;
@@ -4377,18 +4380,25 @@
 
   public class RecoveryController {
     method public android.security.keystore.recovery.RecoverySession createRecoverySession();
+    method public byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
     method public java.security.Key generateKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public java.util.List<java.lang.String> getAliases() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public static android.security.keystore.recovery.RecoveryController getInstance(android.content.Context);
     method public java.security.Key getKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException, java.security.UnrecoverableKeyException;
     method public android.security.keystore.recovery.KeyChainSnapshot getKeyChainSnapshot() throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int[] getRecoverySecretTypes() throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public int getRecoveryStatus(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public java.util.Map<java.lang.String, java.security.cert.X509Certificate> getRootCertificates();
     method public java.security.Key importKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
+    method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void initRecoveryService(java.lang.String, byte[], byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void removeKey(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setRecoverySecretTypes(int[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
     method public void setRecoveryStatus(java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setServerParams(byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException;
     method public void setSnapshotCreatedPendingIntent(android.app.PendingIntent) throws android.security.keystore.recovery.InternalRecoveryServiceException;
@@ -4400,6 +4410,9 @@
   public class RecoverySession implements java.lang.AutoCloseable {
     method public void close();
     method public java.util.Map<java.lang.String, java.security.Key> recoverKeyChainSnapshot(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
+    method public deprecated java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
+    method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
+    method public deprecated byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
     method public byte[] start(java.lang.String, java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
   }
 
@@ -4409,6 +4422,7 @@
 
   public final class WrappedApplicationKey implements android.os.Parcelable {
     method public int describeContents();
+    method public deprecated byte[] getAccount();
     method public java.lang.String getAlias();
     method public byte[] getEncryptedKeyMaterial();
     method public void writeToParcel(android.os.Parcel, int);
@@ -4418,6 +4432,7 @@
   public static class WrappedApplicationKey.Builder {
     ctor public WrappedApplicationKey.Builder();
     method public android.security.keystore.recovery.WrappedApplicationKey build();
+    method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
     method public android.security.keystore.recovery.WrappedApplicationKey.Builder setAlias(java.lang.String);
     method public android.security.keystore.recovery.WrappedApplicationKey.Builder setEncryptedKeyMaterial(byte[]);
   }
@@ -4759,8 +4774,8 @@
     method public final android.view.textclassifier.TextClassifier getLocalTextClassifier();
     method public final android.os.IBinder onBind(android.content.Intent);
     method public abstract void onClassifyText(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextClassification.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>);
-    method public abstract void onCreateTextClassificationSession(android.view.textclassifier.TextClassificationContext, android.view.textclassifier.TextClassificationSessionId);
-    method public abstract void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
+    method public void onCreateTextClassificationSession(android.view.textclassifier.TextClassificationContext, android.view.textclassifier.TextClassificationSessionId);
+    method public void onDestroyTextClassificationSession(android.view.textclassifier.TextClassificationSessionId);
     method public abstract void onGenerateLinks(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextLinks.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>);
     method public void onSelectionEvent(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.SelectionEvent);
     method public abstract void onSuggestSelection(android.view.textclassifier.TextClassificationSessionId, android.view.textclassifier.TextSelection.Request, android.os.CancellationSignal, android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>);
@@ -5106,6 +5121,7 @@
   }
 
   public abstract class NetworkService extends android.app.Service {
+    ctor public NetworkService();
     method protected abstract android.telephony.NetworkService.NetworkServiceProvider createNetworkServiceProvider(int);
     field public static final java.lang.String NETWORK_SERVICE_EXTRA_SLOT_ID = "android.telephony.extra.SLOT_ID";
     field public static final java.lang.String NETWORK_SERVICE_INTERFACE = "android.telephony.NetworkService";
@@ -5387,6 +5403,7 @@
   }
 
   public abstract class DataService extends android.app.Service {
+    ctor public DataService();
     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";
diff --git a/api/system-removed.txt b/api/system-removed.txt
index 7cf12ef..48f43e0 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -91,37 +91,6 @@
 
 }
 
-package android.security.keystore.recovery {
-
-  public final class KeyChainSnapshot implements android.os.Parcelable {
-    method public deprecated byte[] getTrustedHardwarePublicKey();
-  }
-
-  public class RecoveryController {
-    method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
-    method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
-  }
-
-  public class RecoverySession implements java.lang.AutoCloseable {
-    method public deprecated java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
-    method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-    method public deprecated byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
-  }
-
-  public final class WrappedApplicationKey implements android.os.Parcelable {
-    method public deprecated byte[] getAccount();
-  }
-
-  public static class WrappedApplicationKey.Builder {
-    method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
-  }
-
-}
-
 package android.service.notification {
 
   public abstract class NotificationListenerService extends android.app.Service {
diff --git a/api/test-current.txt b/api/test-current.txt
index 9f5fa0c..4878690 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -467,6 +467,14 @@
 
 }
 
+package android.media.audiofx {
+
+  public class AudioEffect {
+    field public static final java.util.UUID EFFECT_TYPE_NULL;
+  }
+
+}
+
 package android.net {
 
   public final class IpSecManager {
@@ -527,6 +535,7 @@
     field public static final int DETECT_VM_CURSOR_LEAKS = 256; // 0x100
     field public static final int DETECT_VM_FILE_URI_EXPOSURE = 8192; // 0x2000
     field public static final int DETECT_VM_INSTANCE_LEAKS = 2048; // 0x800
+    field public static final int DETECT_VM_NON_SDK_API_USAGE = 1073741824; // 0x40000000
     field public static final int DETECT_VM_REGISTRATION_LEAKS = 4096; // 0x1000
     field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
   }
diff --git a/cmds/incidentd/src/PrivacyBuffer.cpp b/cmds/incidentd/src/PrivacyBuffer.cpp
index 6cd2fe1..d753e5e 100644
--- a/cmds/incidentd/src/PrivacyBuffer.cpp
+++ b/cmds/incidentd/src/PrivacyBuffer.cpp
@@ -34,7 +34,7 @@
 void PrivacyBuffer::writeFieldOrSkip(uint32_t fieldTag, bool skip) {
     uint8_t wireType = read_wire_type(fieldTag);
     size_t bytesToWrite = 0;
-    uint32_t varint = 0;
+    uint64_t varint = 0;
 
     switch (wireType) {
         case WIRE_TYPE_VARINT:
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 34a3613..20ecdb1 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -31,7 +31,7 @@
 namespace os {
 namespace incidentd {
 
-const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000;  // 10 seconds
+const int64_t REMOTE_CALL_TIMEOUT_MS = 30 * 1000;  // 30 seconds
 
 /**
  * Base class for sections
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index bc1c51a..8e46714 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -210,7 +210,8 @@
     tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
     tests/e2e/Anomaly_count_e2e_test.cpp \
     tests/e2e/Anomaly_duration_sum_e2e_test.cpp \
-    tests/e2e/ConfigTtl_e2e_test.cpp
+    tests/e2e/ConfigTtl_e2e_test.cpp \
+    tests/e2e/PartialBucket_e2e_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index de8dfe4..90ce735 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -158,7 +158,10 @@
 void StatsLogProcessor::OnLogEvent(LogEvent* event) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
+
     if (currentTimestampNs < mLastLogTimestamp) {
+        StatsdStats::getInstance().noteLogEventSkipped(
+            event->GetTagId(), event->GetElapsedTimestampNs());
         return;
     }
 
@@ -209,7 +212,10 @@
     sp<MetricsManager> newMetricsManager =
         new MetricsManager(key, config, mTimeBaseSec, (timestampNs - 1) / NS_PER_SEC + 1, mUidMap,
                            mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
-
+    auto it = mMetricsManagers.find(key);
+    if (it != mMetricsManagers.end()) {
+        WriteDataToDiskLocked(it->first);
+    }
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
         if (newMetricsManager->shouldAddUidMapListener()) {
@@ -248,19 +254,10 @@
  * onDumpReport dumps serialized ConfigMetricsReportList into outData.
  */
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, const int64_t dumpTimeStampNs,
+                                     const bool include_current_partial_bucket,
                                      vector<uint8_t>* outData) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
 
-    auto it = mMetricsManagers.find(key);
-    if (it == mMetricsManagers.end()) {
-        ALOGW("Config source %s does not exist", key.ToString().c_str());
-        return;
-    }
-
-    // This allows another broadcast to be sent within the rate-limit period if we get close to
-    // filling the buffer again soon.
-    mLastBroadcastTimes.erase(key);
-
     ProtoOutputStream proto;
 
     // Start of ConfigKey.
@@ -270,18 +267,26 @@
     proto.end(configKeyToken);
     // End of ConfigKey.
 
-    // Start of ConfigMetricsReport (reports).
-    uint64_t reportsToken =
-            proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
-    onConfigMetricsReportLocked(key, dumpTimeStampNs, &proto);
-    proto.end(reportsToken);
-    // End of ConfigMetricsReport (reports).
-
-
     // Then, check stats-data directory to see there's any file containing
     // ConfigMetricsReport from previous shutdowns to concatenate to reports.
     StorageManager::appendConfigMetricsReport(key, &proto);
 
+    auto it = mMetricsManagers.find(key);
+    if (it != mMetricsManagers.end()) {
+        // This allows another broadcast to be sent within the rate-limit period if we get close to
+        // filling the buffer again soon.
+        mLastBroadcastTimes.erase(key);
+
+        // Start of ConfigMetricsReport (reports).
+        uint64_t reportsToken =
+                proto.start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_REPORTS);
+        onConfigMetricsReportLocked(key, dumpTimeStampNs, include_current_partial_bucket, &proto);
+        proto.end(reportsToken);
+        // End of ConfigMetricsReport (reports).
+    } else {
+        ALOGW("Config source %s does not exist", key.ToString().c_str());
+    }
+
     if (outData != nullptr) {
         outData->clear();
         outData->resize(proto.size());
@@ -295,7 +300,7 @@
         }
     }
 
-    StatsdStats::getInstance().noteMetricsReportSent(key);
+    StatsdStats::getInstance().noteMetricsReportSent(key, proto.size());
 }
 
 /*
@@ -303,20 +308,24 @@
  */
 void StatsLogProcessor::onConfigMetricsReportLocked(const ConfigKey& key,
                                                     const int64_t dumpTimeStampNs,
+                                                    const bool include_current_partial_bucket,
                                                     ProtoOutputStream* proto) {
     // We already checked whether key exists in mMetricsManagers in
     // WriteDataToDisk.
     auto it = mMetricsManagers.find(key);
+    if (it == mMetricsManagers.end()) {
+        return;
+    }
     int64_t lastReportTimeNs = it->second->getLastReportTimeNs();
     int64_t lastReportWallClockNs = it->second->getLastReportWallClockNs();
 
     // First, fill in ConfigMetricsReport using current data on memory, which
     // starts from filling in StatsLogReport's.
-    it->second->onDumpReport(dumpTimeStampNs, proto);
+    it->second->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, proto);
 
     // Fill in UidMap.
     uint64_t uidMapToken = proto->start(FIELD_TYPE_MESSAGE | FIELD_ID_UID_MAP);
-    mUidMap->appendUidMap(key, proto);
+    mUidMap->appendUidMap(dumpTimeStampNs, key, proto);
     proto->end(uidMapToken);
 
     // Fill in the timestamps.
@@ -328,7 +337,6 @@
                 (long long)lastReportWallClockNs);
     proto->write(FIELD_TYPE_INT64 | FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS,
                 (long long)getWallClockNs());
-
 }
 
 void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
@@ -358,6 +366,7 @@
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
+        WriteDataToDiskLocked(key);
         mMetricsManagers.erase(it);
         mUidMap->OnConfigRemoved(key);
     }
@@ -403,22 +412,30 @@
     }
 }
 
+void StatsLogProcessor::WriteDataToDiskLocked(const ConfigKey& key) {
+    ProtoOutputStream proto;
+    onConfigMetricsReportLocked(key, getElapsedRealtimeNs(),
+                                true /* include_current_partial_bucket*/, &proto);
+    string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
+         (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
+    android::base::unique_fd fd(open(file_name.c_str(),
+                                O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
+    if (fd == -1) {
+        ALOGE("Attempt to write %s but failed", file_name.c_str());
+        return;
+    }
+    proto.flush(fd.get());
+}
+
+void StatsLogProcessor::WriteDataToDiskLocked() {
+    for (auto& pair : mMetricsManagers) {
+        WriteDataToDiskLocked(pair.first);
+    }
+}
+
 void StatsLogProcessor::WriteDataToDisk() {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
-    for (auto& pair : mMetricsManagers) {
-        const ConfigKey& key = pair.first;
-        ProtoOutputStream proto;
-        onConfigMetricsReportLocked(key, getElapsedRealtimeNs(), &proto);
-        string file_name = StringPrintf("%s/%ld_%d_%lld", STATS_DATA_DIR,
-             (long)getWallClockSec(), key.GetUid(), (long long)key.GetId());
-        android::base::unique_fd fd(open(file_name.c_str(),
-                                    O_WRONLY | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR));
-        if (fd == -1) {
-            VLOG("Attempt to write %s but failed", file_name.c_str());
-            return;
-        }
-        proto.flush(fd.get());
-    }
+    WriteDataToDiskLocked();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index d37429e..1e82b1e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -48,7 +48,8 @@
 
     size_t GetMetricsSize(const ConfigKey& key) const;
 
-    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, vector<uint8_t>* outData);
+    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
+                      const bool include_current_partial_bucket, vector<uint8_t>* outData);
 
     /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
     void onAnomalyAlarmFired(
@@ -102,7 +103,11 @@
     void OnConfigUpdatedLocked(
         const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
 
+    void WriteDataToDiskLocked();
+    void WriteDataToDiskLocked(const ConfigKey& key);
+
     void onConfigMetricsReportLocked(const ConfigKey& key, const int64_t dumpTimeStampNs,
+                                     const bool include_current_partial_bucket,
                                      util::ProtoOutputStream* proto);
 
     /* Check if we should send a broadcast if approaching memory limits and if we're over, we
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index adc2664..f16109c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -501,7 +501,7 @@
         if (good) {
             vector<uint8_t> data;
             mProcessor->onDumpReport(ConfigKey(uid, StrToInt64(name)), getElapsedRealtimeNs(),
-                                     &data);
+                                     false /* include_current_bucket*/, &data);
             // TODO: print the returned StatsLogReport to file instead of printing to logcat.
             if (proto) {
                 for (size_t i = 0; i < data.size(); i ++) {
@@ -640,7 +640,7 @@
                                          "Only system uid can call informAllUidData");
     }
 
-    mUidMap->updateMap(uid, version, app);
+    mUidMap->updateMap(getElapsedRealtimeNs(), uid, version, app);
     VLOG("StatsService::informAllUidData succeeded");
 
     return Status::ok();
@@ -653,7 +653,7 @@
         return Status::fromExceptionCode(Status::EX_SECURITY,
                                          "Only system uid can call informOnePackage");
     }
-    mUidMap->updateApp(app, uid, version);
+    mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version);
     return Status::ok();
 }
 
@@ -664,7 +664,7 @@
         return Status::fromExceptionCode(Status::EX_SECURITY,
                                          "Only system uid can call informOnePackageRemoved");
     }
-    mUidMap->removeApp(app, uid);
+    mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
     mConfigManager->RemoveConfigs(uid);
     return Status::ok();
 }
@@ -800,7 +800,8 @@
     VLOG("StatsService::getData with Pid %i, Uid %i", ipc->getCallingPid(), ipc->getCallingUid());
     if (checkCallingPermission(String16(kPermissionDump))) {
         ConfigKey configKey(ipc->getCallingUid(), key);
-        mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), output);
+        mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
+                                 false /* include_current_bucket*/, output);
         return Status::ok();
     } else {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 8d2fd33..a4552e1 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -262,6 +262,10 @@
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_invalid);
+    FRIEND_TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp);
+    FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade);
+    FRIEND_TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval);
+    FRIEND_TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index 0c076e9..b589d0d 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -49,21 +49,25 @@
 const int FIELD_ID_ANOMALY_ALARM_STATS = 9;
 // const int FIELD_ID_PULLED_ATOM_STATS = 10; // The proto is written in stats_log_util.cpp
 const int FIELD_ID_LOGGER_ERROR_STATS = 11;
-const int FIELD_ID_SUBSCRIBER_ALARM_STATS = 12;
+const int FIELD_ID_PERIODIC_ALARM_STATS = 12;
+const int FIELD_ID_SKIPPED_LOG_EVENT_STATS = 13;
 
 const int FIELD_ID_ATOM_STATS_TAG = 1;
 const int FIELD_ID_ATOM_STATS_COUNT = 2;
 
 const int FIELD_ID_ANOMALY_ALARMS_REGISTERED = 1;
-const int FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED = 1;
+const int FIELD_ID_PERIODIC_ALARMS_REGISTERED = 1;
 
 const int FIELD_ID_LOGGER_STATS_TIME = 1;
 const int FIELD_ID_LOGGER_STATS_ERROR_CODE = 2;
 
+const int FIELD_ID_SKIPPED_LOG_EVENT_STATS_TAG = 1;
+const int FIELD_ID_SKIPPED_LOG_EVENT_STATS_TIMESTAMP = 2;
+
 const int FIELD_ID_CONFIG_STATS_UID = 1;
 const int FIELD_ID_CONFIG_STATS_ID = 2;
 const int FIELD_ID_CONFIG_STATS_CREATION = 3;
-const int FIELD_ID_CONFIG_STATS_RESET = 18;
+const int FIELD_ID_CONFIG_STATS_RESET = 19;
 const int FIELD_ID_CONFIG_STATS_DELETION = 4;
 const int FIELD_ID_CONFIG_STATS_METRIC_COUNT = 5;
 const int FIELD_ID_CONFIG_STATS_CONDITION_COUNT = 6;
@@ -72,7 +76,8 @@
 const int FIELD_ID_CONFIG_STATS_VALID = 9;
 const int FIELD_ID_CONFIG_STATS_BROADCAST = 10;
 const int FIELD_ID_CONFIG_STATS_DATA_DROP = 11;
-const int FIELD_ID_CONFIG_STATS_DUMP_REPORT = 12;
+const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME = 12;
+const int FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES = 20;
 const int FIELD_ID_CONFIG_STATS_MATCHER_STATS = 13;
 const int FIELD_ID_CONFIG_STATS_CONDITION_STATS = 14;
 const int FIELD_ID_CONFIG_STATS_METRIC_STATS = 15;
@@ -91,11 +96,10 @@
 const int FIELD_ID_ALERT_STATS_ID = 1;
 const int FIELD_ID_ALERT_STATS_COUNT = 2;
 
-const int FIELD_ID_UID_MAP_SNAPSHOTS = 1;
-const int FIELD_ID_UID_MAP_CHANGES = 2;
-const int FIELD_ID_UID_MAP_BYTES_USED = 3;
-const int FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS = 4;
-const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 5;
+const int FIELD_ID_UID_MAP_CHANGES = 1;
+const int FIELD_ID_UID_MAP_BYTES_USED = 2;
+const int FIELD_ID_UID_MAP_DROPPED_CHANGES = 3;
+const int FIELD_ID_UID_MAP_DELETED_APPS = 4;
 
 const std::map<int, std::pair<size_t, size_t>> StatsdStats::kAtomDimensionKeySizeLimitMap = {
         {android::util::CPU_TIME_PER_UID_FREQ, {6000, 10000}},
@@ -212,32 +216,32 @@
     it->second->data_drop_time_sec.push_back(timeSec);
 }
 
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key) {
-    noteMetricsReportSent(key, getWallClockSec());
+void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes) {
+    noteMetricsReportSent(key, num_bytes, getWallClockSec());
 }
 
-void StatsdStats::noteMetricsReportSent(const ConfigKey& key, int32_t timeSec) {
+void StatsdStats::noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes,
+                                        int32_t timeSec) {
     lock_guard<std::mutex> lock(mLock);
     auto it = mConfigStats.find(key);
     if (it == mConfigStats.end()) {
         ALOGE("Config key %s not found!", key.ToString().c_str());
         return;
     }
-    if (it->second->dump_report_time_sec.size() == kMaxTimestampCount) {
-        it->second->dump_report_time_sec.pop_front();
+    if (it->second->dump_report_stats.size() == kMaxTimestampCount) {
+        it->second->dump_report_stats.pop_front();
     }
-    it->second->dump_report_time_sec.push_back(timeSec);
+    it->second->dump_report_stats.push_back(std::make_pair(timeSec, num_bytes));
 }
 
-void StatsdStats::noteUidMapDropped(int snapshots, int deltas) {
+void StatsdStats::noteUidMapDropped(int deltas) {
     lock_guard<std::mutex> lock(mLock);
-    mUidMapStats.dropped_snapshots += mUidMapStats.dropped_snapshots + snapshots;
     mUidMapStats.dropped_changes += mUidMapStats.dropped_changes + deltas;
 }
 
-void StatsdStats::setUidMapSnapshots(int snapshots) {
+void StatsdStats::noteUidMapAppDeletionDropped() {
     lock_guard<std::mutex> lock(mLock);
-    mUidMapStats.snapshots = snapshots;
+    mUidMapStats.deleted_apps++;
 }
 
 void StatsdStats::setUidMapChanges(int changes) {
@@ -346,6 +350,15 @@
     mPushedAtomStats[atomId]++;
 }
 
+void StatsdStats::noteLogEventSkipped(int tag, int64_t timestamp) {
+    lock_guard<std::mutex> lock(mLock);
+    // grows strictly one at a time. so it won't > kMaxSkippedLogEvents
+    if (mSkippedLogEvents.size() == kMaxSkippedLogEvents) {
+        mSkippedLogEvents.pop_front();
+    }
+    mSkippedLogEvents.push_back(std::make_pair(tag, timestamp));
+}
+
 void StatsdStats::noteLoggerError(int error) {
     lock_guard<std::mutex> lock(mLock);
     // grows strictly one at a time. so it won't > kMaxLoggerErrors
@@ -368,10 +381,11 @@
     mAnomalyAlarmRegisteredStats = 0;
     mPeriodicAlarmRegisteredStats = 0;
     mLoggerErrors.clear();
+    mSkippedLogEvents.clear();
     for (auto& config : mConfigStats) {
         config.second->broadcast_sent_time_sec.clear();
         config.second->data_drop_time_sec.clear();
-        config.second->dump_report_time_sec.clear();
+        config.second->dump_report_stats.clear();
         config.second->annotations.clear();
         config.second->matcher_stats.clear();
         config.second->condition_stats.clear();
@@ -430,8 +444,8 @@
             fprintf(out, "\tdata drop time: %d\n", dataDropTime);
         }
 
-        for (const auto& dumpTime : configStats->dump_report_time_sec) {
-            fprintf(out, "\tdump report time: %d\n", dumpTime);
+        for (const auto& dump : configStats->dump_report_stats) {
+            fprintf(out, "\tdump report time: %d bytes: %lld\n", dump.first, (long long)dump.second);
         }
 
         for (const auto& stats : pair.second->matcher_stats) {
@@ -478,11 +492,9 @@
         fprintf(out, "Subscriber alarm registrations: %d\n", mPeriodicAlarmRegisteredStats);
     }
 
-    fprintf(out,
-            "UID map stats: bytes=%d, snapshots=%d, changes=%d, snapshots lost=%d, changes "
-            "lost=%d\n",
-            mUidMapStats.bytes_used, mUidMapStats.snapshots, mUidMapStats.changes,
-            mUidMapStats.dropped_snapshots, mUidMapStats.dropped_changes);
+    fprintf(out, "UID map stats: bytes=%d, changes=%d, deleted=%d, changes lost=%d\n",
+            mUidMapStats.bytes_used, mUidMapStats.changes, mUidMapStats.deleted_apps,
+            mUidMapStats.dropped_changes);
 
     for (const auto& error : mLoggerErrors) {
         time_t error_time = error.first;
@@ -491,6 +503,9 @@
         strftime(buffer, sizeof(buffer), "%Y-%m-%d %I:%M%p\n", error_tm);
         fprintf(out, "Logger error %d at %s\n", error.second, buffer);
     }
+    for (const auto& skipped : mSkippedLogEvents) {
+        fprintf(out, "Log event (%d) skipped at %lld\n", skipped.first, (long long)skipped.second);
+    }
 }
 
 void addConfigStatsToProto(const ConfigStats& configStats, ProtoOutputStream* proto) {
@@ -523,9 +538,16 @@
                      drop);
     }
 
-    for (const auto& dump : configStats.dump_report_time_sec) {
-        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT | FIELD_COUNT_REPEATED,
-                     dump);
+    for (const auto& dump : configStats.dump_report_stats) {
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_TIME |
+                     FIELD_COUNT_REPEATED,
+                     dump.first);
+    }
+
+    for (const auto& dump : configStats.dump_report_stats) {
+        proto->write(FIELD_TYPE_INT64 | FIELD_ID_CONFIG_STATS_DUMP_REPORT_BYTES |
+                     FIELD_COUNT_REPEATED,
+                     (long long)dump.second);
     }
 
     for (const auto& annotation : configStats.annotations) {
@@ -617,19 +639,17 @@
     }
 
     if (mPeriodicAlarmRegisteredStats > 0) {
-        uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SUBSCRIBER_ALARM_STATS);
-        proto.write(FIELD_TYPE_INT32 | FIELD_ID_SUBSCRIBER_ALARMS_REGISTERED,
+        uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_PERIODIC_ALARM_STATS);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_PERIODIC_ALARMS_REGISTERED,
                     mPeriodicAlarmRegisteredStats);
         proto.end(token);
     }
 
     uint64_t uidMapToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_UIDMAP_STATS);
-    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_SNAPSHOTS, mUidMapStats.snapshots);
     proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_CHANGES, mUidMapStats.changes);
     proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_BYTES_USED, mUidMapStats.bytes_used);
-    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_SNAPSHOTS,
-                mUidMapStats.dropped_snapshots);
     proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DROPPED_CHANGES, mUidMapStats.dropped_changes);
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_UID_MAP_DELETED_APPS, mUidMapStats.deleted_apps);
     proto.end(uidMapToken);
 
     for (const auto& error : mLoggerErrors) {
@@ -640,6 +660,15 @@
         proto.end(token);
     }
 
+    for (const auto& skipped : mSkippedLogEvents) {
+        uint64_t token = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_SKIPPED_LOG_EVENT_STATS |
+                                      FIELD_COUNT_REPEATED);
+        proto.write(FIELD_TYPE_INT32 | FIELD_ID_SKIPPED_LOG_EVENT_STATS_TAG, skipped.first);
+        proto.write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_LOG_EVENT_STATS_TIMESTAMP,
+                    (long long)skipped.second);
+        proto.end(token);
+    }
+
     output->clear();
     size_t bufferSize = proto.size();
     output->resize(bufferSize);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 00bef75..123a703 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -43,7 +43,7 @@
 
     std::list<int32_t> broadcast_sent_time_sec;
     std::list<int32_t> data_drop_time_sec;
-    std::list<int32_t> dump_report_time_sec;
+    std::list<std::pair<int32_t, int64_t>> dump_report_stats;
 
     // Stores how many times a matcher have been matched. The map size is capped by kMaxConfigCount.
     std::map<const int64_t, int> matcher_stats;
@@ -73,11 +73,10 @@
 };
 
 struct UidMapStats {
-    int32_t snapshots;
     int32_t changes;
     int32_t bytes_used;
-    int32_t dropped_snapshots;
     int32_t dropped_changes;
+    int32_t deleted_apps = 0;
 };
 
 // Keeps track of stats of statsd.
@@ -105,6 +104,8 @@
 
     const static int kMaxLoggerErrors = 10;
 
+    const static int kMaxSkippedLogEvents = 200;
+
     const static int kMaxTimestampCount = 20;
 
     const static int kMaxLogSourceCount = 50;
@@ -121,6 +122,9 @@
     // is critical for understanding the metrics.
     const static size_t kMaxBytesUsedUidMap = 50 * 1024;
 
+    // The number of deleted apps that are stored in the uid map.
+    const static int kMaxDeletedAppsInUidMap = 100;
+
     /* Minimum period between two broadcasts in nanoseconds. */
     static const int64_t kMinBroadcastPeriodNs = 60 * NS_PER_SEC;
 
@@ -173,7 +177,7 @@
      *
      * The report may be requested via StatsManager API, or through adb cmd.
      */
-    void noteMetricsReportSent(const ConfigKey& key);
+    void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes);
 
     /**
      * Report the size of output tuple of a condition.
@@ -244,14 +248,18 @@
     void noteRegisteredPeriodicAlarmChanged();
 
     /**
-     * Records the number of snapshot and delta entries that are being dropped from the uid map.
+     * Records the number of delta entries that are being dropped from the uid map.
      */
-    void noteUidMapDropped(int snapshots, int deltas);
+    void noteUidMapDropped(int deltas);
 
     /**
-     * Updates the number of snapshots currently stored in the uid map.
+     * Records that an app was deleted (from statsd's map).
      */
-    void setUidMapSnapshots(int snapshots);
+    void noteUidMapAppDeletionDropped();
+
+    /**
+     * Updates the number of changes currently stored in the uid map.
+     */
     void setUidMapChanges(int changes);
     void setCurrentUidMapMemory(int bytes);
 
@@ -270,6 +278,11 @@
     void noteLoggerError(int error);
 
     /**
+     * Records statsd skipped an event.
+     */
+    void noteLogEventSkipped(int tag, int64_t timestamp);
+
+    /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
      * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
      * to collect stats after reset() has been called.
@@ -324,6 +337,9 @@
     // Logd errors. Size capped by kMaxLoggerErrors.
     std::list<const std::pair<int, int>> mLoggerErrors;
 
+    // Skipped log events.
+    std::list<const std::pair<int, int64_t>> mSkippedLogEvents;
+
     // Stores the number of times statsd modified the anomaly alarm registered with
     // StatsCompanionService.
     int mAnomalyAlarmRegisteredStats = 0;
@@ -339,7 +355,7 @@
 
     void noteDataDropped(const ConfigKey& key, int32_t timeSec);
 
-    void noteMetricsReportSent(const ConfigKey& key, int32_t timeSec);
+    void noteMetricsReportSent(const ConfigKey& key, const size_t num_bytes, int32_t timeSec);
 
     void noteBroadcastSent(const ConfigKey& key, int32_t timeSec);
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 98963bd..c77e07b 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -123,8 +123,13 @@
 }
 
 void CountMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
+                                             const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
-    flushIfNeededLocked(dumpTimeNs);
+    if (include_current_partial_bucket) {
+        flushLocked(dumpTimeNs);
+    } else {
+        flushIfNeededLocked(dumpTimeNs);
+    }
     if (mPastBuckets.empty()) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 5991c28..cafc882 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -54,7 +54,9 @@
             const LogEvent& event) override;
 
 private:
+
     void onDumpReportLocked(const int64_t dumpTimeNs,
+                            const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
     // Internal interface to handle condition change.
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 19155de..3125fa7 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -439,8 +439,13 @@
 }
 
 void DurationMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
+                                                const bool include_current_partial_bucket,
                                                 ProtoOutputStream* protoOutput) {
-    flushIfNeededLocked(dumpTimeNs);
+    if (include_current_partial_bucket) {
+        flushLocked(dumpTimeNs);
+    } else {
+        flushIfNeededLocked(dumpTimeNs);
+    }
     if (mPastBuckets.empty()) {
         VLOG(" Duration metric, empty return");
         return;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 80fb829..80fbdde 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -62,6 +62,7 @@
                           bool condition, const LogEvent& event);
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
+                            const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
     // Internal interface to handle condition change.
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index d55cb3f..33ab9aa 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -101,6 +101,7 @@
 }
 
 void EventMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
+                                             const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
     if (mProto->size() <= 0) {
         return;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index fbbc7e2..5c29174 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -47,6 +47,7 @@
             const LogEvent& event) override;
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
+                            const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
     // Internal interface to handle condition change.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 2561d1d..3c77aae 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -156,8 +156,14 @@
 }
 
 void GaugeMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
+                                             const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
     VLOG("Gauge metric %lld report now...", (long long)mMetricId);
+    if (include_current_partial_bucket) {
+        flushLocked(dumpTimeNs);
+    } else {
+        flushIfNeededLocked(dumpTimeNs);
+    }
 
     flushIfNeededLocked(dumpTimeNs);
     if (mPastBuckets.empty()) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index f49180f..04b7df9 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -88,6 +88,7 @@
 
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
+                            const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
     // for testing
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 139a407..f931e57 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -80,11 +80,13 @@
     };
 
     void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override{
-            // TODO: Implement me.
+        // Force buckets to split on removal also.
+        notifyAppUpgrade(eventTimeNs, apk, uid, 0);
     };
 
     void onUidMapReceived(const int64_t& eventTimeNs) override{
-            // TODO: Implement me.
+            // Purposefully don't flush partial buckets on a new snapshot.
+            // This occurs if a new user is added/removed or statsd crashes.
     };
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
@@ -110,9 +112,11 @@
 
     // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
     // This method clears all the past buckets.
-    void onDumpReport(const int64_t dumpTimeNs, android::util::ProtoOutputStream* protoOutput) {
+    void onDumpReport(const int64_t dumpTimeNs,
+                      const bool include_current_partial_bucket,
+                      android::util::ProtoOutputStream* protoOutput) {
         std::lock_guard<std::mutex> lock(mMutex);
-        return onDumpReportLocked(dumpTimeNs, protoOutput);
+        return onDumpReportLocked(dumpTimeNs, include_current_partial_bucket, protoOutput);
     }
 
     void dumpStates(FILE* out, bool verbose) const {
@@ -166,16 +170,26 @@
     virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
                                                   const int64_t eventTime) = 0;
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
+                                    const bool include_current_partial_bucket,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual size_t byteSizeLocked() const = 0;
     virtual void dumpStatesLocked(FILE* out, bool verbose) const = 0;
 
     /**
-     * Flushes the current bucket if the eventTime is after the current bucket's end time.
+     * Flushes the current bucket if the eventTime is after the current bucket's end time. This will
+       also flush the current partial bucket in memory.
      */
     virtual void flushIfNeededLocked(const int64_t& eventTime){};
 
     /**
+     * Flushes all the data including the current partial bucket.
+     */
+    virtual void flushLocked(const int64_t& eventTime) {
+        flushIfNeededLocked(eventTime);
+        flushCurrentBucketLocked(eventTime);
+    };
+
+    /**
      * For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
      * we need to be able to flush the current buckets on demand (ie, end the current bucket and
      * start new bucket). If this function is called when eventTimeNs is greater than the current
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 22827b0..b7f1bd5 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -190,14 +190,16 @@
     }
 }
 
-void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs, ProtoOutputStream* protoOutput) {
+void MetricsManager::onDumpReport(const int64_t dumpTimeStampNs,
+                                  const bool include_current_partial_bucket,
+                                  ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
     // one StatsLogReport per MetricProduer
     for (const auto& producer : mAllMetricProducers) {
         if (mNoReportMetricIds.find(producer->getMetricId()) == mNoReportMetricIds.end()) {
             uint64_t token =
                     protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_METRICS);
-            producer->onDumpReport(dumpTimeStampNs, protoOutput);
+            producer->onDumpReport(dumpTimeStampNs, include_current_partial_bucket, protoOutput);
             protoOutput->end(token);
         }
     }
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 9a9b33c..6aa260a 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -90,8 +90,8 @@
 
     virtual void dropData(const int64_t dropTimeNs);
 
-    // Config source owner can call onDumpReport() to get all the metrics collected.
     virtual void onDumpReport(const int64_t dumpTimeNs,
+                              const bool include_current_partial_bucket,
                               android::util::ProtoOutputStream* protoOutput);
 
     // Computes the total byte size of all metrics managed by a single config source.
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index fd623ca..51fac8c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -147,9 +147,14 @@
 }
 
 void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs,
+                                             const bool include_current_partial_bucket,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %lld dump report now...", (long long)mMetricId);
-    flushIfNeededLocked(dumpTimeNs);
+    if (include_current_partial_bucket) {
+        flushLocked(dumpTimeNs);
+    } else {
+        flushIfNeededLocked(dumpTimeNs);
+    }
     if (mPastBuckets.empty()) {
         return;
     }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c8f7062..b5f6429 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -87,6 +87,7 @@
 
 private:
     void onDumpReportLocked(const int64_t dumpTimeNs,
+                            const bool include_current_partial_bucket,
                             android::util::ProtoOutputStream* protoOutput) override;
 
     // Internal interface to handle condition change.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index b7c5795..566d34e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -263,15 +263,14 @@
 }
 
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
-                 const unordered_map<int64_t, int>& logTrackerMap,
+                 UidMap& uidMap, const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, std::vector<int>>& conditionToMetricMap,
                  unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                 unordered_map<int64_t, int>& metricMap,
-                 std::set<int64_t> &noReportMetricIds) {
+                 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds) {
     sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
     const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
                                 config.event_metric_size() + config.value_metric_size();
@@ -538,6 +537,9 @@
         }
         noReportMetricIds.insert(no_report_metric);
     }
+    for (auto it : allMetricProducers) {
+        uidMap.addListener(it);
+    }
     return true;
 }
 
@@ -642,12 +644,10 @@
     return true;
 }
 
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
-                      const UidMap& uidMap,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const long timeBaseSec, const long currentTimeSec,
-                      set<int>& allTagIds,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const long timeBaseSec,
+                      const long currentTimeSec, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -656,7 +656,7 @@
                       unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                      std::set<int64_t> &noReportMetricIds) {
+                      std::set<int64_t>& noReportMetricIds) {
     unordered_map<int64_t, int> logTrackerMap;
     unordered_map<int64_t, int> conditionTrackerMap;
     unordered_map<int64_t, int> metricProducerMap;
@@ -673,9 +673,10 @@
         return false;
     }
 
-    if (!initMetrics(key, config, timeBaseSec, logTrackerMap, conditionTrackerMap, allAtomMatchers,
-                     allConditionTrackers, allMetricProducers, conditionToMetricMap,
-                     trackerToMetricMap, metricProducerMap, noReportMetricIds)) {
+    if (!initMetrics(key, config, timeBaseSec, uidMap, logTrackerMap, conditionTrackerMap,
+                     allAtomMatchers, allConditionTrackers, allMetricProducers,
+                     conditionToMetricMap, trackerToMetricMap, metricProducerMap,
+                     noReportMetricIds)) {
         ALOGE("initMetricProducers failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3754ae0..0ebdcf9 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -81,7 +81,7 @@
 //                          the list of MetricProducer index
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
-        const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
+        const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, UidMap& uidMap,
         const std::unordered_map<int64_t, int>& logTrackerMap,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
@@ -90,16 +90,14 @@
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
         std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
-        std::set<int64_t> &noReportMetricIds);
+        std::set<int64_t>& noReportMetricIds);
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
-                      const UidMap& uidMap,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
                       const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor,
-                      const long timeBaseSec, const long currentTimeSec,
-                      std::set<int>& allTagIds,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const long timeBaseSec,
+                      const long currentTimeSec, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
@@ -108,7 +106,7 @@
                       std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                      std::set<int64_t> &noReportMetricIds);
+                      std::set<int64_t>& noReportMetricIds);
 
 bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
 
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 8c8152d..b3425a4 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -45,6 +45,7 @@
 const int FIELD_ID_SNAPSHOT_PACKAGE_NAME = 1;
 const int FIELD_ID_SNAPSHOT_PACKAGE_VERSION = 2;
 const int FIELD_ID_SNAPSHOT_PACKAGE_UID = 3;
+const int FIELD_ID_SNAPSHOT_PACKAGE_DELETED = 4;
 const int FIELD_ID_SNAPSHOT_TIMESTAMP = 1;
 const int FIELD_ID_SNAPSHOT_PACKAGE_INFO = 2;
 const int FIELD_ID_SNAPSHOTS = 1;
@@ -53,7 +54,8 @@
 const int FIELD_ID_CHANGE_TIMESTAMP = 2;
 const int FIELD_ID_CHANGE_PACKAGE = 3;
 const int FIELD_ID_CHANGE_UID = 4;
-const int FIELD_ID_CHANGE_VERSION = 5;
+const int FIELD_ID_CHANGE_NEW_VERSION = 5;
+const int FIELD_ID_CHANGE_PREV_VERSION = 6;
 
 UidMap::UidMap() : mBytesUsed(0) {}
 
@@ -62,13 +64,8 @@
 bool UidMap::hasApp(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
-    auto range = mMap.equal_range(uid);
-    for (auto it = range.first; it != range.second; ++it) {
-        if (it->second.packageName == packageName) {
-            return true;
-        }
-    }
-    return false;
+    auto it = mMap.find(std::make_pair(uid, packageName));
+    return it != mMap.end() && !it->second.deleted;
 }
 
 string UidMap::normalizeAppName(const string& appName) const {
@@ -84,10 +81,10 @@
 
 std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
     std::set<string> names;
-    auto range = mMap.equal_range(uid);
-    for (auto it = range.first; it != range.second; ++it) {
-        names.insert(returnNormalized ?
-            normalizeAppName(it->second.packageName) : it->second.packageName);
+    for (const auto& kv : mMap) {
+        if (kv.first.first == uid && !kv.second.deleted) {
+            names.insert(returnNormalized ? normalizeAppName(kv.first.second) : kv.first.second);
+        }
     }
     return names;
 }
@@ -95,18 +92,11 @@
 int64_t UidMap::getAppVersion(int uid, const string& packageName) const {
     lock_guard<mutex> lock(mMutex);
 
-    auto range = mMap.equal_range(uid);
-    for (auto it = range.first; it != range.second; ++it) {
-        if (it->second.packageName == packageName) {
-            return it->second.versionCode;
-        }
+    auto it = mMap.find(std::make_pair(uid, packageName));
+    if (it == mMap.end() || it->second.deleted) {
+        return 0;
     }
-    return 0;
-}
-
-void UidMap::updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
-                       const vector<String16>& packageName) {
-    updateMap(getElapsedRealtimeNs(), uid, versionCode, packageName);
+    return it->second.versionCode;
 }
 
 void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
@@ -115,20 +105,31 @@
     {
         lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
 
-        mMap.clear();
-        vector<const SnapshotPackageInfo> infos;
-        for (size_t j = 0; j < uid.size(); j++) {
-            string package = string(String8(packageName[j]).string());
-            mMap.insert(make_pair(uid[j], AppData(package, versionCode[j])));
-            infos.emplace_back(package, versionCode[j], uid[j]);
+        std::unordered_map<std::pair<int, string>, AppData, PairHash> deletedApps;
+
+        // Copy all the deleted apps.
+        for (const auto& kv : mMap) {
+            if (kv.second.deleted) {
+                deletedApps[kv.first] = kv.second;
+            }
         }
 
-        mSnapshots.emplace_back(timestamp, infos);
+        mMap.clear();
+        for (size_t j = 0; j < uid.size(); j++) {
+            string package = string(String8(packageName[j]).string());
+            mMap[std::make_pair(uid[j], package)] = AppData(versionCode[j]);
+        }
 
-        mBytesUsed += mSnapshots.back().bytes;
+        for (const auto& kv : deletedApps) {
+            auto mMapIt = mMap.find(kv.first);
+            if (mMapIt != mMap.end()) {
+                // Insert this deleted app back into the current map.
+                mMap[kv.first] = kv.second;
+            }
+        }
+
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
-        StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size());
         getListenerListCopyLocked(&broadcastList);
     }
     // To avoid invoking callback while holding the internal lock. we get a copy of the listener
@@ -144,38 +145,34 @@
     }
 }
 
-void UidMap::updateApp(const String16& app_16, const int32_t& uid, const int64_t& versionCode) {
-    updateApp(getElapsedRealtimeNs(), app_16, uid, versionCode);
-}
-
 void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
                        const int64_t& versionCode) {
     vector<wp<PackageInfoListener>> broadcastList;
     string appName = string(String8(app_16).string());
     {
         lock_guard<mutex> lock(mMutex);
-
-        mChanges.emplace_back(false, timestamp, appName, uid, versionCode);
+        int32_t prevVersion = 0;
+        bool found = false;
+        auto it = mMap.find(std::make_pair(uid, appName));
+        if (it != mMap.end()) {
+            found = true;
+            prevVersion = it->second.versionCode;
+            it->second.versionCode = versionCode;
+            it->second.deleted = false;
+        }
+        if (!found) {
+            // Otherwise, we need to add an app at this uid.
+            mMap[std::make_pair(uid, appName)] = AppData(versionCode);
+        } else {
+            // Only notify the listeners if this is an app upgrade. If this app is being installed
+            // for the first time, then we don't notify the listeners.
+            getListenerListCopyLocked(&broadcastList);
+        }
+        mChanges.emplace_back(false, timestamp, appName, uid, versionCode, prevVersion);
         mBytesUsed += kBytesChangeRecord;
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
         StatsdStats::getInstance().setUidMapChanges(mChanges.size());
-
-        auto range = mMap.equal_range(int(uid));
-        bool found = false;
-        for (auto it = range.first; it != range.second; ++it) {
-            // If we find the exact same app name and uid, update the app version directly.
-            if (it->second.packageName == appName) {
-                it->second.versionCode = versionCode;
-                found = true;
-                break;
-            }
-        }
-        if (!found) {
-            // Otherwise, we need to add an app at this uid.
-            mMap.insert(make_pair(uid, AppData(appName, versionCode)));
-        }
-        getListenerListCopyLocked(&broadcastList);
     }
 
     for (auto weakPtr : broadcastList) {
@@ -195,22 +192,14 @@
     }
     while (mBytesUsed > limit) {
         ALOGI("Bytes used %zu is above limit %zu, need to delete something", mBytesUsed, limit);
-        if (mSnapshots.size() > 0) {
-            mBytesUsed -= mSnapshots.front().bytes;
-            mSnapshots.pop_front();
-            StatsdStats::getInstance().noteUidMapDropped(1, 0);
-        } else if (mChanges.size() > 0) {
+        if (mChanges.size() > 0) {
             mBytesUsed -= kBytesChangeRecord;
             mChanges.pop_front();
-            StatsdStats::getInstance().noteUidMapDropped(0, 1);
+            StatsdStats::getInstance().noteUidMapDropped(1);
         }
     }
 }
 
-void UidMap::removeApp(const String16& app_16, const int32_t& uid) {
-    removeApp(getElapsedRealtimeNs(), app_16, uid);
-}
-
 void UidMap::getListenerListCopyLocked(vector<wp<PackageInfoListener>>* output) {
     for (auto weakIt = mSubscribers.begin(); weakIt != mSubscribers.end();) {
         auto strongPtr = weakIt->promote();
@@ -230,19 +219,26 @@
     {
         lock_guard<mutex> lock(mMutex);
 
-        mChanges.emplace_back(true, timestamp, app, uid, 0);
+        int32_t prevVersion = 0;
+        auto key = std::make_pair(uid, app);
+        auto it = mMap.find(key);
+        if (it != mMap.end() && !it->second.deleted) {
+            prevVersion = it->second.versionCode;
+            it->second.deleted = true;
+            mDeletedApps.push_back(key);
+        }
+        if (mDeletedApps.size() > StatsdStats::kMaxDeletedAppsInUidMap) {
+            // Delete the oldest one.
+            auto oldest = mDeletedApps.front();
+            mDeletedApps.pop_front();
+            mMap.erase(oldest);
+            StatsdStats::getInstance().noteUidMapAppDeletionDropped();
+        }
+        mChanges.emplace_back(true, timestamp, app, uid, 0, prevVersion);
         mBytesUsed += kBytesChangeRecord;
         ensureBytesUsedBelowLimit();
         StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
         StatsdStats::getInstance().setUidMapChanges(mChanges.size());
-
-        auto range = mMap.equal_range(int(uid));
-        for (auto it = range.first; it != range.second; ++it) {
-            if (it->second.packageName == app) {
-                mMap.erase(it);
-                break;
-            }
-        }
         getListenerListCopyLocked(&broadcastList);
     }
 
@@ -290,22 +286,20 @@
 }
 
 void UidMap::clearOutput() {
-    mSnapshots.clear();
     mChanges.clear();
     // Also update the guardrail trackers.
     StatsdStats::getInstance().setUidMapChanges(0);
-    StatsdStats::getInstance().setUidMapSnapshots(1);
     mBytesUsed = 0;
     StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
 }
 
 int64_t UidMap::getMinimumTimestampNs() {
     int64_t m = 0;
-    for (auto it : mLastUpdatePerConfigKey) {
+    for (const auto& kv : mLastUpdatePerConfigKey) {
         if (m == 0) {
-            m = it.second;
-        } else if (it.second < m) {
-            m = it.second;
+            m = kv.second;
+        } else if (kv.second < m) {
+            m = kv.second;
         }
     }
     return m;
@@ -315,10 +309,6 @@
     return mBytesUsed;
 }
 
-void UidMap::appendUidMap(const ConfigKey& key, ProtoOutputStream* proto) {
-    appendUidMap(getElapsedRealtimeNs(), key, proto);
-}
-
 void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
                           ProtoOutputStream* proto) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
@@ -332,34 +322,27 @@
                          (long long)record.timestampNs);
             proto->write(FIELD_TYPE_STRING | FIELD_ID_CHANGE_PACKAGE, record.package);
             proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_UID, (int)record.uid);
-            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_VERSION, (int)record.version);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_NEW_VERSION, (int)record.version);
+            proto->write(FIELD_TYPE_INT32 | FIELD_ID_CHANGE_PREV_VERSION, (int)record.prevVersion);
             proto->end(changesToken);
         }
     }
 
-    bool atLeastOneSnapshot = false;
-    unsigned int count = 0;
-    for (const SnapshotRecord& record : mSnapshots) {
-        // Ensure that we include at least the latest snapshot.
-        if ((count == mSnapshots.size() - 1 && !atLeastOneSnapshot) ||
-            record.timestampNs > mLastUpdatePerConfigKey[key]) {
-            uint64_t snapshotsToken =
-                    proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
-            atLeastOneSnapshot = true;
-            count++;
-            proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP,
-                         (long long)record.timestampNs);
-            for (const SnapshotPackageInfo& info : record.infos) {
-                uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
-                                              FIELD_ID_SNAPSHOT_PACKAGE_INFO);
-                proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, info.package);
-                proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION, info.version);
-                proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, info.uid);
-                proto->end(token);
-            }
-            proto->end(snapshotsToken);
-        }
+    // Write snapshot from current uid map state.
+    uint64_t snapshotsToken =
+            proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_SNAPSHOTS);
+    proto->write(FIELD_TYPE_INT64 | FIELD_ID_SNAPSHOT_TIMESTAMP, (long long)timestamp);
+    for (const auto& kv : mMap) {
+        uint64_t token = proto->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED |
+                                      FIELD_ID_SNAPSHOT_PACKAGE_INFO);
+        proto->write(FIELD_TYPE_STRING | FIELD_ID_SNAPSHOT_PACKAGE_NAME, kv.first.second);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_VERSION,
+                     (int)kv.second.versionCode);
+        proto->write(FIELD_TYPE_INT32 | FIELD_ID_SNAPSHOT_PACKAGE_UID, kv.first.first);
+        proto->write(FIELD_TYPE_BOOL | FIELD_ID_SNAPSHOT_PACKAGE_DELETED, kv.second.deleted);
+        proto->end(token);
     }
+    proto->end(snapshotsToken);
 
     int64_t prevMin = getMinimumTimestampNs();
     mLastUpdatePerConfigKey[key] = timestamp;
@@ -368,14 +351,6 @@
     if (newMin > prevMin) {  // Delete anything possible now that the minimum has
                              // moved forward.
         int64_t cutoff_nanos = newMin;
-        for (auto it_snapshots = mSnapshots.begin(); it_snapshots != mSnapshots.end();) {
-            if (it_snapshots->timestampNs < cutoff_nanos) {
-                mBytesUsed -= it_snapshots->bytes;
-                it_snapshots = mSnapshots.erase(it_snapshots);
-            } else {
-                ++it_snapshots;
-            }
-        }
         for (auto it_changes = mChanges.begin(); it_changes != mChanges.end();) {
             if (it_changes->timestampNs < cutoff_nanos) {
                 mBytesUsed -= kBytesChangeRecord;
@@ -384,53 +359,24 @@
                 ++it_changes;
             }
         }
-
-        if (mSnapshots.size() == 0) {
-            // Produce another snapshot. This results in extra data being uploaded but
-            // helps ensure we can re-construct the UID->app name, versionCode mapping
-            // in server.
-            vector<const SnapshotPackageInfo> infos;
-            for (const auto& it : mMap) {
-                infos.emplace_back(it.second.packageName, it.second.versionCode, it.first);
-            }
-
-            mSnapshots.emplace_back(timestamp, infos);
-            mBytesUsed += mSnapshots.back().bytes;
-        }
     }
     StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
     StatsdStats::getInstance().setUidMapChanges(mChanges.size());
-    StatsdStats::getInstance().setUidMapSnapshots(mSnapshots.size());
 }
 
 void UidMap::printUidMap(FILE* out) const {
     lock_guard<mutex> lock(mMutex);
 
-    for (auto it : mMap) {
-        fprintf(out, "%s, v%" PRId64 " (%i)\n", it.second.packageName.c_str(),
-                it.second.versionCode, it.first);
+    for (const auto& kv : mMap) {
+        if (!kv.second.deleted) {
+            fprintf(out, "%s, v%" PRId64 " (%i)\n", kv.first.second.c_str(), kv.second.versionCode,
+                    kv.first.first);
+        }
     }
 }
 
 void UidMap::OnConfigUpdated(const ConfigKey& key) {
     mLastUpdatePerConfigKey[key] = -1;
-
-    // Ensure there is at least one snapshot available since this configuration also needs to know
-    // what all the uid's represent.
-    if (mSnapshots.size() == 0) {
-        sp<IStatsCompanionService> statsCompanion = nullptr;
-        // Get statscompanion service from service manager
-        const sp<IServiceManager> sm(defaultServiceManager());
-        if (sm != nullptr) {
-            const String16 name("statscompanion");
-            statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
-            if (statsCompanion == nullptr) {
-                ALOGW("statscompanion service unavailable!");
-                return;
-            }
-            statsCompanion->triggerUidSnapshot();
-        }
-    }
 }
 
 void UidMap::OnConfigRemoved(const ConfigKey& key) {
@@ -441,9 +387,9 @@
     lock_guard<mutex> lock(mMutex);
 
     set<int32_t> results;
-    for (const auto& pair : mMap) {
-        if (pair.second.packageName == package) {
-            results.insert(pair.first);
+    for (const auto& kv : mMap) {
+        if (kv.first.second == package && !kv.second.deleted) {
+            results.insert(kv.first.first);
         }
     }
     return results;
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index a3632d2..7222e85 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -43,10 +43,13 @@
 namespace statsd {
 
 struct AppData {
-    const string packageName;
     int64_t versionCode;
+    bool deleted;
 
-    AppData(const string& a, const int64_t v) : packageName(a), versionCode(v){};
+    // Empty constructor needed for unordered map.
+    AppData() {
+    }
+    AppData(const int64_t v) : versionCode(v), deleted(false){};
 };
 
 // When calling appendUidMap, we retrieve all the ChangeRecords since the last
@@ -57,55 +60,21 @@
     const string package;
     const int32_t uid;
     const int32_t version;
+    const int32_t prevVersion;
 
     ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
-                 const int32_t uid, const int32_t version)
+                 const int32_t uid, const int32_t version, const int32_t prevVersion)
         : deletion(isDeletion),
           timestampNs(timestampNs),
           package(package),
           uid(uid),
-          version(version) {
+          version(version),
+          prevVersion(prevVersion) {
     }
 };
 
 const unsigned int kBytesChangeRecord = sizeof(struct ChangeRecord);
 
-// Storing the int64 for a timestamp is expected to take 10 bytes (could take
-// less because of varint encoding).
-const unsigned int kBytesTimestampField = 10;
-
-struct SnapshotPackageInfo {
-    const string package;
-    const int32_t version;
-    const int32_t uid;
-    SnapshotPackageInfo(const string& package, const int32_t version, const int32_t uid)
-        : package(package), version(version), uid(uid) {
-    }
-};
-
-const unsigned int kBytesSnapshotInfo = sizeof(struct SnapshotPackageInfo);
-
-// When calling appendUidMap, we retrieve all the snapshots since the last
-// timestamp we called appendUidMap for this configuration key.
-struct SnapshotRecord {
-    const int64_t timestampNs;
-
-    // All the package info known.
-    vector<const SnapshotPackageInfo> infos;
-
-    // Tracks the number of bytes this snapshot consumes.
-    uint32_t bytes;
-
-    SnapshotRecord(const int64_t timestampNs, vector<const SnapshotPackageInfo>& infos)
-        : timestampNs(timestampNs), infos(infos) {
-        bytes = 0;
-        for (auto info : infos) {
-            bytes += info.package.size() + kBytesSnapshotInfo;
-        }
-        bytes += kBytesTimestampField;
-    }
-};
-
 // UidMap keeps track of what the corresponding app name (APK name) and version code for every uid
 // at any given moment. This map must be updated by StatsCompanionService.
 class UidMap : public virtual android::RefBase {
@@ -117,11 +86,12 @@
      * All three inputs must be the same size, and the jth element in each array refers to the same
      * tuple, ie. uid[j] corresponds to packageName[j] with versionCode[j].
      */
-    void updateMap(const vector<int32_t>& uid, const vector<int64_t>& versionCode,
-                   const vector<String16>& packageName);
+    void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
+                   const vector<int64_t>& versionCode, const vector<String16>& packageName);
 
-    void updateApp(const String16& packageName, const int32_t& uid, const int64_t& versionCode);
-    void removeApp(const String16& packageName, const int32_t& uid);
+    void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
+                   const int64_t& versionCode);
+    void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
 
     // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
     bool hasApp(int uid, const string& packageName) const;
@@ -157,7 +127,8 @@
     // Gets all snapshots and changes that have occurred since the last output.
     // If every config key has received a change or snapshot record, then this
     // record is deleted.
-    void appendUidMap(const ConfigKey& key, util::ProtoOutputStream* proto);
+    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+                      util::ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -173,25 +144,20 @@
     std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
-    void updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
-                   const vector<int64_t>& versionCode, const vector<String16>& packageName);
-
-    void updateApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid,
-                   const int64_t& versionCode);
-    void removeApp(const int64_t& timestamp, const String16& packageName, const int32_t& uid);
-
-    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
-                      util::ProtoOutputStream* proto);
-
     void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
 
     // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
     mutable mutex mMutex;
     mutable mutex mIsolatedMutex;
 
-    // Maps uid to application data. This must be multimap since there is a feature in Android for
-    // multiple apps to share the same uid.
-    std::unordered_multimap<int, AppData> mMap;
+    struct PairHash {
+        size_t operator()(std::pair<int, string> p) const noexcept {
+            std::hash<std::string> hash_fn;
+            return hash_fn(std::to_string(p.first) + p.second);
+        }
+    };
+    // Maps uid and package name to application data.
+    std::unordered_map<std::pair<int, string>, AppData, PairHash> mMap;
 
     // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute
     // to the parent uid.
@@ -200,8 +166,8 @@
     // Record the changes that can be provided with the uploads.
     std::list<ChangeRecord> mChanges;
 
-    // Record the snapshots that can be provided with the uploads.
-    std::list<SnapshotRecord> mSnapshots;
+    // Store which uid and apps represent deleted ones.
+    std::list<std::pair<int, string>> mDeletedApps;
 
     // Metric producers that should be notified if there's an upgrade in any app.
     set<wp<PackageInfoListener>> mSubscribers;
@@ -228,6 +194,8 @@
 
     // Allows unit-test to access private methods.
     FRIEND_TEST(UidMapTest, TestClearingOutput);
+    FRIEND_TEST(UidMapTest, TestRemovedAppRetained);
+    FRIEND_TEST(UidMapTest, TestRemovedAppOverGuardrail);
     FRIEND_TEST(UidMapTest, TestOutputIncludesAtLeastOneSnapshot);
     FRIEND_TEST(UidMapTest, TestMemoryComputed);
     FRIEND_TEST(UidMapTest, TestMemoryGuardrail);
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 4aa3c973..36b24c8 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -155,6 +155,8 @@
             optional int64 version = 2;
 
             optional int32 uid = 3;
+
+            optional bool deleted = 4;
         }
         optional int64 elapsed_timestamp_nanos = 1;
 
@@ -169,7 +171,8 @@
         optional string app = 3;
         optional int32 uid = 4;
 
-        optional int64 version = 5;
+        optional int64 new_version = 5;
+        optional int64 prev_version = 6;
     }
     repeated Change changes = 2;
 }
@@ -240,10 +243,10 @@
         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 int32 dump_report_data_size = 20;
         repeated MatcherStats matcher_stats = 13;
         repeated ConditionStats condition_stats = 14;
         repeated MetricStats metric_stats = 15;
@@ -266,11 +269,10 @@
     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 int32 changes = 1;
+        optional int32 bytes_used = 2;
+        optional int32 dropped_changes = 3;
+        optional int32 deleted_apps = 4;
     }
     optional UidMapStats uidmap_stats = 8;
 
@@ -292,4 +294,15 @@
         optional int32 error_code = 2;
     }
     repeated LoggerErrorStats logger_error_stats = 11;
+
+    message PeriodicAlarmStats {
+        optional int32 alarms_registered = 1;
+    }
+    optional PeriodicAlarmStats periodic_alarm_stats = 12;
+
+    message  SkippedLogEventStats {
+        optional int32 tag = 1;
+        optional int64 elapsed_timestamp_nanos = 2;
+    }
+    repeated SkippedLogEventStats skipped_log_event_stats = 13;
 }
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index b0da07b..4c6671d 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -146,11 +146,10 @@
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
     EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
-    uidMap.updateMap({1111, 1111, 2222, 3333, 3333} /* uid list */,
-        {1, 1, 2, 1, 2} /* version list */,
-        {android::String16("pkg0"), android::String16("pkg1"),
-         android::String16("pkg1"), android::String16("Pkg2"),
-         android::String16("PkG3")} /* package name list */);
+    uidMap.updateMap(
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
+             android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
 
     EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
@@ -297,7 +296,7 @@
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
     UidMap uidMap;
     uidMap.updateMap(
-            {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
              android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
 
@@ -372,7 +371,7 @@
 TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
     UidMap uidMap;
     uidMap.updateMap(
-            {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
+            1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("pkg0"), android::String16("pkg1"), android::String16("pkg1"),
              android::String16("Pkg2"), android::String16("PkG3")} /* package name list */);
 
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index e6fe3d8..fb8877a 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -126,7 +126,7 @@
 TEST(StatsLogProcessorTest, TestUidMapHasSnapshot) {
     // Setup simple config key corresponding to empty config.
     sp<UidMap> m = new UidMap();
-    m->updateMap({1, 2}, {1, 2}, {String16("p1"), String16("p2")});
+    m->updateMap(1, {1, 2}, {1, 2}, {String16("p1"), String16("p2")});
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> subscriberAlarmMonitor;
     int broadcastCount = 0;
@@ -139,7 +139,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, &bytes);
+    p.onDumpReport(key, 1, false, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
@@ -167,7 +167,7 @@
 
     // Expect to get no metrics, but snapshot specified above in uidmap.
     vector<uint8_t> bytes;
-    p.onDumpReport(key, 1, &bytes);
+    p.onDumpReport(key, 1, false, &bytes);
 
     ConfigMetricsReportList output;
     output.ParseFromArray(bytes.data(), bytes.size());
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index a9b67e0..2fab975 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -76,7 +76,7 @@
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(4);
     versions.push_back(5);
-    m.updateMap(uids, versions, apps);
+    m.updateMap(1, uids, versions, apps);
     EXPECT_TRUE(m.hasApp(1000, kApp1));
     EXPECT_TRUE(m.hasApp(1000, kApp2));
     EXPECT_FALSE(m.hasApp(1000, "not.app"));
@@ -102,7 +102,7 @@
     apps.push_back(String16(kApp2.c_str()));
     versions.push_back(4);
     versions.push_back(5);
-    m.updateMap(uids, versions, apps);
+    m.updateMap(1, uids, versions, apps);
 
     std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 2u);
@@ -110,7 +110,7 @@
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
     // Update the app1 version.
-    m.updateApp(String16(kApp1.c_str()), 1000, 40);
+    m.updateApp(2, String16(kApp1.c_str()), 1000, 40);
     EXPECT_EQ(40, m.getAppVersion(1000, kApp1));
 
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
@@ -118,7 +118,7 @@
     EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
-    m.removeApp(String16(kApp1.c_str()), 1000);
+    m.removeApp(3, String16(kApp1.c_str()), 1000);
     EXPECT_FALSE(m.hasApp(1000, kApp1));
     EXPECT_TRUE(m.hasApp(1000, kApp2));
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
@@ -127,7 +127,7 @@
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
     // Remove app2.
-    m.removeApp(String16(kApp2.c_str()), 1000);
+    m.removeApp(4, String16(kApp2.c_str()), 1000);
     EXPECT_FALSE(m.hasApp(1000, kApp1));
     EXPECT_FALSE(m.hasApp(1000, kApp2));
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
@@ -136,14 +136,14 @@
 
 TEST(UidMapTest, TestUpdateApp) {
     UidMap m;
-    m.updateMap({1000, 1000}, {4, 5}, {String16(kApp1.c_str()), String16(kApp2.c_str())});
+    m.updateMap(1, {1000, 1000}, {4, 5}, {String16(kApp1.c_str()), String16(kApp2.c_str())});
     std::set<string> name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 2u);
     EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
     EXPECT_TRUE(name_set.find(kApp2) != name_set.end());
 
     // Adds a new name for uid 1000.
-    m.updateApp(String16("NeW_aPP1_NAmE"), 1000, 40);
+    m.updateApp(2, String16("NeW_aPP1_NAmE"), 1000, 40);
     name_set = m.getAppNamesFromUid(1000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 3u);
     EXPECT_TRUE(name_set.find(kApp1) != name_set.end());
@@ -152,7 +152,7 @@
     EXPECT_TRUE(name_set.find("new_app1_name") != name_set.end());
 
     // This name is also reused by another uid 2000.
-    m.updateApp(String16("NeW_aPP1_NAmE"), 2000, 1);
+    m.updateApp(3, String16("NeW_aPP1_NAmE"), 2000, 1);
     name_set = m.getAppNamesFromUid(2000, true /* returnNormalized */);
     EXPECT_EQ(name_set.size(), 1u);
     EXPECT_TRUE(name_set.find("NeW_aPP1_NAmE") == name_set.end());
@@ -200,6 +200,66 @@
     EXPECT_EQ(1, results.snapshots_size());
 }
 
+TEST(UidMapTest, TestRemovedAppRetained) {
+    UidMap m;
+    // Initialize single config key.
+    ConfigKey config1(1, StringToId("config1"));
+    m.OnConfigUpdated(config1);
+    vector<int32_t> uids;
+    vector<int64_t> versions;
+    vector<String16> apps;
+    uids.push_back(1000);
+    apps.push_back(String16(kApp2.c_str()));
+    versions.push_back(5);
+    m.updateMap(1, uids, versions, apps);
+    m.removeApp(2, String16(kApp2.c_str()), 1000);
+
+    ProtoOutputStream proto;
+    m.appendUidMap(3, config1, &proto);
+
+    // Snapshot should still contain this item as deleted.
+    UidMapping results;
+    protoOutputStreamToUidMapping(&proto, &results);
+    EXPECT_EQ(1, results.snapshots(0).package_info_size());
+    EXPECT_EQ(true, results.snapshots(0).package_info(0).deleted());
+}
+
+TEST(UidMapTest, TestRemovedAppOverGuardrail) {
+    UidMap m;
+    // Initialize single config key.
+    ConfigKey config1(1, StringToId("config1"));
+    m.OnConfigUpdated(config1);
+    vector<int32_t> uids;
+    vector<int64_t> versions;
+    vector<String16> apps;
+    const int maxDeletedApps = StatsdStats::kMaxDeletedAppsInUidMap;
+    for (int j = 0; j < maxDeletedApps + 10; j++) {
+        uids.push_back(j);
+        apps.push_back(String16(kApp1.c_str()));
+        versions.push_back(j);
+    }
+    m.updateMap(1, uids, versions, apps);
+
+    // First, verify that we have the expected number of items.
+    UidMapping results;
+    ProtoOutputStream proto;
+    m.appendUidMap(3, config1, &proto);
+    protoOutputStreamToUidMapping(&proto, &results);
+    EXPECT_EQ(maxDeletedApps + 10, results.snapshots(0).package_info_size());
+
+    // Now remove all the apps.
+    m.updateMap(1, uids, versions, apps);
+    for (int j = 0; j < maxDeletedApps + 10; j++) {
+        m.removeApp(4, String16(kApp1.c_str()), j);
+    }
+
+    proto.clear();
+    m.appendUidMap(5, config1, &proto);
+    // Snapshot drops the first nine items.
+    protoOutputStreamToUidMapping(&proto, &results);
+    EXPECT_EQ(maxDeletedApps, results.snapshots(0).package_info_size());
+}
+
 TEST(UidMapTest, TestClearingOutput) {
     UidMap m;
 
@@ -218,7 +278,6 @@
     versions.push_back(4);
     versions.push_back(5);
     m.updateMap(1, uids, versions, apps);
-    EXPECT_EQ(1U, m.mSnapshots.size());
 
     ProtoOutputStream proto;
     m.appendUidMap(2, config1, &proto);
@@ -227,7 +286,6 @@
     EXPECT_EQ(1, results.snapshots_size());
 
     // We have to keep at least one snapshot in memory at all times.
-    EXPECT_EQ(1U, m.mSnapshots.size());
     proto.clear();
     m.appendUidMap(2, config1, &proto);
     protoOutputStreamToUidMapping(&proto, &results);
@@ -262,7 +320,6 @@
     EXPECT_EQ(1, results.snapshots_size());
     EXPECT_EQ(2, results.changes_size());
     // At this point both should be cleared.
-    EXPECT_EQ(1U, m.mSnapshots.size());
     EXPECT_EQ(0U, m.mChanges.size());
 }
 
@@ -280,11 +337,8 @@
     apps.push_back(String16(kApp1.c_str()));
     versions.push_back(1);
     m.updateMap(1, uids, versions, apps);
-    size_t snapshot_bytes = m.mBytesUsed;
-    EXPECT_TRUE(snapshot_bytes > startBytes);
 
     m.updateApp(3, String16(kApp1.c_str()), 1000, 40);
-    EXPECT_TRUE(m.mBytesUsed > snapshot_bytes);
 
     ProtoOutputStream proto;
     vector<uint8_t> bytes;
@@ -313,16 +367,13 @@
         versions.push_back(1);
     }
     m.updateMap(1, uids, versions, apps);
-    EXPECT_EQ(1U, m.mSnapshots.size());
 
     m.updateApp(3, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 2);
-    EXPECT_EQ(1U, m.mSnapshots.size());
     EXPECT_EQ(1U, m.mChanges.size());
 
     // Now force deletion by limiting the memory to hold one delta change.
     m.maxBytesOverride = 80; // Since the app string alone requires >45 characters.
     m.updateApp(5, String16("EXTREMELY_LONG_STRING_FOR_APP_TO_WASTE_MEMORY.0"), 1000, 4);
-    EXPECT_EQ(0U, m.mSnapshots.size());
     EXPECT_EQ(1U, m.mChanges.size());
 }
 #else
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index a04a6f9..4dd0da8 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -18,6 +18,7 @@
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
+#include <iostream>
 #include <vector>
 
 namespace android {
@@ -66,14 +67,10 @@
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
     // Here it assumes that GMS core has two uids.
-    processor->getUidMap()->updateApp(
-        android::String16("com.android.gmscore"), 222 /* uid */, 1 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("com.android.gmscore"), 444 /* uid */, 1 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("app1"), 111 /* uid */, 2 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("APP3"), 333 /* uid */, 2 /* version code*/);
+    processor->getUidMap()->updateMap(
+            1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
+             String16("APP3")});
 
     // GMS core node is in the middle.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -147,7 +144,7 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
@@ -212,14 +209,10 @@
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
     // Here it assumes that GMS core has two uids.
-    processor->getUidMap()->updateApp(
-        android::String16("com.android.gmscore"), 222 /* uid */, 1 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("com.android.gmscore"), 444 /* uid */, 1 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("app1"), 111 /* uid */, 2 /* version code*/);
-    processor->getUidMap()->updateApp(
-        android::String16("APP3"), 333 /* uid */, 2 /* version code*/);
+    processor->getUidMap()->updateMap(
+            1, {222, 444, 111, 333}, {1, 1, 2, 2},
+            {String16("com.android.gmscore"), String16("com.android.gmscore"), String16("app1"),
+             String16("APP3")});
 
     // GMS core node is in the middle.
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -293,7 +286,7 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 4 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
index 63e23ce..eb57d470 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp
@@ -172,7 +172,8 @@
 
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1,
+                                    false, &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -488,7 +489,8 @@
 
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
+                                    &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -731,7 +733,7 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
index c2334d8..9729a2e 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp
@@ -130,7 +130,7 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -342,7 +342,7 @@
 
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -521,7 +521,7 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -718,7 +718,7 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
index ab37140..4e2c36e 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -142,7 +142,8 @@
 
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
+                                    &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -433,7 +434,8 @@
 
             ConfigMetricsReportList reports;
             vector<uint8_t> buffer;
-            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+            processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false,
+                                    &buffer);
             EXPECT_TRUE(buffer.size() > 0);
             EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -650,7 +652,7 @@
 
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 2b91324..18a0485 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -148,7 +148,7 @@
         }
         ConfigMetricsReportList reports;
         vector<uint8_t> buffer;
-        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, &buffer);
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 3 * bucketSizeNs, false, &buffer);
         EXPECT_TRUE(buffer.size() > 0);
         EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
         EXPECT_EQ(1, reports.reports_size());
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 1440f29..1952a6f 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -200,7 +200,7 @@
     }
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
@@ -314,7 +314,7 @@
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
new file mode 100644
index 0000000..d4892ed
--- /dev/null
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -0,0 +1,137 @@
+// 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.
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/StatsService.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+const string kApp1 = "app1.sharing.1";
+const int kConfigKey = 789130123;  // Randomly chosen to avoid collisions with existing configs.
+
+void SendConfig(StatsService& service, const StatsdConfig& config) {
+    string str;
+    config.SerializeToString(&str);
+    std::vector<uint8_t> configAsVec(str.begin(), str.end());
+    bool success;
+    service.addConfiguration(kConfigKey, configAsVec, &success);
+}
+
+ConfigMetricsReport GetReports(StatsService& service) {
+    vector<uint8_t> output;
+    service.getData(kConfigKey, &output);
+    ConfigMetricsReportList reports;
+    reports.ParseFromArray(output.data(), output.size());
+    EXPECT_EQ(1, reports.reports_size());
+    return reports.reports(0);
+}
+
+StatsdConfig MakeConfig() {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+    *config.add_atom_matcher() = appCrashMatcher;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(StringToId("AppCrashes"));
+    countMetric->set_what(appCrashMatcher.id());
+    countMetric->set_bucket(FIVE_MINUTES);
+    return config;
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
+    StatsService service(nullptr);
+    SendConfig(service, MakeConfig());
+    const long start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                                // initialized with.
+
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
+
+    ConfigMetricsReport report = GetReports(service);
+    // Expect no metrics since the bucket has not finished yet.
+    EXPECT_EQ(0, report.metrics_size());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
+    StatsService service(nullptr);
+    SendConfig(service, MakeConfig());
+    const long start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                                // initialized with.
+
+    // Force the uidmap to update at timestamp 2.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    // This is a new installation, so there shouldn't be a split (should be same as the without
+    // split case).
+    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+    // Goes into the second bucket.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+
+    ConfigMetricsReport report = GetReports(service);
+    EXPECT_EQ(0, report.metrics_size());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
+    StatsService service(nullptr);
+    SendConfig(service, MakeConfig());
+    const long start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                                // initialized with.
+    service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+
+    // Force the uidmap to update at timestamp 2.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2);
+    // Goes into the second bucket.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+
+    ConfigMetricsReport report = GetReports(service);
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
+}
+
+TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
+    StatsService service(nullptr);
+    SendConfig(service, MakeConfig());
+    const long start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
+                                                // initialized with.
+    service.mUidMap->updateMap(start, {1}, {1}, {String16(kApp1.c_str())});
+
+    // Force the uidmap to update at timestamp 2.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+    // Goes into the second bucket.
+    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+
+    ConfigMetricsReport report = GetReports(service);
+    EXPECT_EQ(1, report.metrics_size());
+    EXPECT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info(0).count());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index bfae8bc..f2d47c7 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -127,7 +127,7 @@
     FeedEvents(config, processor);
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
 
@@ -161,7 +161,7 @@
     vector<uint8_t> buffer;
     ConfigMetricsReportList reports;
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
@@ -208,7 +208,7 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
@@ -237,7 +237,7 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
 
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
@@ -262,7 +262,7 @@
     FeedEvents(config, processor);
     ConfigMetricsReportList reports;
     vector<uint8_t> buffer;
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
@@ -304,7 +304,7 @@
         processor->OnLogEvent(event.get());
     }
 
-    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, &buffer);
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 6 * bucketSizeNs + 1, false, &buffer);
     EXPECT_TRUE(buffer.size() > 0);
     EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
     EXPECT_EQ(reports.reports_size(), 1);
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 5c35d96..e99e402 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -129,9 +129,9 @@
     stats.noteDataDropped(key);
 
     // dump report -> 3
-    stats.noteMetricsReportSent(key);
-    stats.noteMetricsReportSent(key);
-    stats.noteMetricsReportSent(key);
+    stats.noteMetricsReportSent(key, 0);
+    stats.noteMetricsReportSent(key, 0);
+    stats.noteMetricsReportSent(key, 0);
 
     vector<uint8_t> output;
     stats.dumpStats(&output, true);  // Dump and reset stats
@@ -143,6 +143,7 @@
     EXPECT_EQ(2, configReport.broadcast_sent_time_sec_size());
     EXPECT_EQ(1, configReport.data_drop_time_sec_size());
     EXPECT_EQ(3, configReport.dump_report_time_sec_size());
+    EXPECT_EQ(3, configReport.dump_report_data_size_size());
     EXPECT_EQ(1, configReport.annotation_size());
     EXPECT_EQ(123, configReport.annotation(0).field_int64());
     EXPECT_EQ(456, configReport.annotation(0).field_int32());
@@ -268,7 +269,7 @@
     for (int i = 0; i < StatsdStats::kMaxTimestampCount; i++) {
         stats.noteDataDropped(key, timestamps[i]);
         stats.noteBroadcastSent(key, timestamps[i]);
-        stats.noteMetricsReportSent(key, timestamps[i]);
+        stats.noteMetricsReportSent(key, 0, timestamps[i]);
     }
 
     int32_t newTimestamp = 10000;
@@ -276,7 +277,7 @@
     // now it should trigger removing oldest timestamp
     stats.noteDataDropped(key, 10000);
     stats.noteBroadcastSent(key, 10000);
-    stats.noteMetricsReportSent(key, 10000);
+    stats.noteMetricsReportSent(key, 0, 10000);
 
     EXPECT_TRUE(stats.mConfigStats.find(key) != stats.mConfigStats.end());
     const auto& configStats = stats.mConfigStats[key];
@@ -284,7 +285,7 @@
     size_t maxCount = StatsdStats::kMaxTimestampCount;
     EXPECT_EQ(maxCount, configStats->broadcast_sent_time_sec.size());
     EXPECT_EQ(maxCount, configStats->data_drop_time_sec.size());
-    EXPECT_EQ(maxCount, configStats->dump_report_time_sec.size());
+    EXPECT_EQ(maxCount, configStats->dump_report_stats.size());
 
     // the oldest timestamp is the second timestamp in history
     EXPECT_EQ(1, configStats->broadcast_sent_time_sec.front());
@@ -294,7 +295,7 @@
     // the last timestamp is the newest timestamp.
     EXPECT_EQ(newTimestamp, configStats->broadcast_sent_time_sec.back());
     EXPECT_EQ(newTimestamp, configStats->data_drop_time_sec.back());
-    EXPECT_EQ(newTimestamp, configStats->dump_report_time_sec.back());
+    EXPECT_EQ(newTimestamp, configStats->dump_report_stats.back().first);
 }
 
 }  // namespace statsd
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 4dc3823..321baa8 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -3,6 +3,7 @@
 Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
 Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/animation/LayoutTransition;->cancel()V
+Landroid/animation/LayoutTransition;->cancel(I)V
 Landroid/animation/ValueAnimator;->animateValue(F)V
 Landroid/animation/ValueAnimator;->sDurationScale:F
 Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
@@ -394,6 +395,7 @@
 Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;Landroid/os/Bundle;)V
 Landroid/appwidget/AppWidgetManager;->bindAppWidgetId(ILandroid/content/ComponentName;)V
 Landroid/appwidget/AppWidgetManager;->mService:Lcom/android/internal/appwidget/IAppWidgetService;
+Landroid/appwidget/AppWidgetProviderInfo;->providerInfo:Landroid/content/pm/ActivityInfo;
 Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
 Landroid/bluetooth/BluetoothAdapter;->disable(Z)Z
 Landroid/bluetooth/BluetoothAdapter;->factoryReset()Z
@@ -667,12 +669,14 @@
 Landroid/database/sqlite/SQLiteCustomFunction;->name:Ljava/lang/String;
 Landroid/database/sqlite/SQLiteCustomFunction;->numArgs:I
 Landroid/database/sqlite/SQLiteDatabaseConfiguration;->maxSqlCacheSize:I
+Landroid/database/sqlite/SQLiteDatabase;->CONFLICT_VALUES:[Ljava/lang/String;
 Landroid/database/sqlite/SQLiteDatabase;->mConfigurationLocked:Landroid/database/sqlite/SQLiteDatabaseConfiguration;
 Landroid/database/sqlite/SQLiteDatabase;->mConnectionPoolLocked:Landroid/database/sqlite/SQLiteConnectionPool;
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->largestMemAlloc:I
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->memoryUsed:I
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
 Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
+Landroid/database/sqlite/SQLiteStatement;-><init>(Landroid/database/sqlite/SQLiteDatabase;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
 Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
@@ -762,7 +766,6 @@
 Landroid/graphics/GraphicBuffer;->CREATOR:Landroid/os/Parcelable$Creator;
 Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
 Landroid/graphics/GraphicBuffer;->mNativeObject:J
-Landroid/graphics/ImageDecoder;-><init>(JIIZ)V
 Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
 Landroid/graphics/LinearGradient;->mColors:[I
 Landroid/graphics/Matrix;->native_instance:J
@@ -1185,6 +1188,9 @@
 Landroid/net/Proxy;->getProxy(Landroid/content/Context;Ljava/lang/String;)Ljava/net/Proxy;
 Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;)V
 Landroid/net/SntpClient;-><init>()V
+Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
+Landroid/net/StaticIpConfiguration;->ipAddress:Landroid/net/LinkAddress;
+Landroid/net/StaticIpConfiguration;-><init>()V
 Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl;
 Landroid/net/SSLCertificateSocketFactory;->getAlpnSelectedProtocol(Ljava/net/Socket;)[B
 Landroid/net/SSLCertificateSocketFactory;->getDelegate()Ljavax/net/ssl/SSLSocketFactory;
@@ -1221,11 +1227,11 @@
 Landroid/net/wifi/p2p/WifiP2pGroup;->TEMPORARY_NET_ID:I
 Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
 Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I
+Landroid/net/wifi/p2p/WifiP2pManager;->CREATE_GROUP:I
 Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
-Landroid/net/wifi/p2p/WifiP2pManager;->CREATE_GROUP:I
 Landroid/net/wifi/ScanResult;->anqpDomainId:I
 Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List;
 Landroid/net/wifi/ScanResult;->distanceCm:I
@@ -1257,6 +1263,8 @@
 Landroid/net/wifi/WifiConfiguration;->defaultGwMacAddress:Ljava/lang/String;
 Landroid/net/wifi/WifiConfiguration;->lastConnectUid:I
 Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
+Landroid/net/wifi/WifiConfiguration;->setIpAssignment(Landroid/net/IpConfiguration$IpAssignment;)V
+Landroid/net/wifi/WifiConfiguration;->setStaticIpConfiguration(Landroid/net/StaticIpConfiguration;)V
 Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
 Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String;
 Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String;
@@ -1592,6 +1600,7 @@
 Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
 Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
 Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->HISTORY_PROJECTION:[Ljava/lang/String;
 Landroid/provider/Browser;->SEARCHES_URI:Landroid/net/Uri;
 Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
 Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V
@@ -2267,6 +2276,7 @@
 Landroid/view/View;->computeOpaqueFlags()V
 Landroid/view/ViewConfiguration;->getDeviceGlobalActionKeyTimeout()J
 Landroid/view/ViewConfiguration;->getDoubleTapMinTime()I
+Landroid/view/ViewConfiguration;->getScaledScrollFactor()I
 Landroid/view/ViewConfiguration;->mFadingMarqueeEnabled:Z
 Landroid/view/ViewConfiguration;->sHasPermanentMenuKeySet:Z
 Landroid/view/ViewConfiguration;->sHasPermanentMenuKey:Z
@@ -2524,6 +2534,7 @@
 Landroid/widget/HorizontalScrollView;->mEdgeGlowLeft:Landroid/widget/EdgeEffect;
 Landroid/widget/HorizontalScrollView;->mEdgeGlowRight:Landroid/widget/EdgeEffect;
 Landroid/widget/HorizontalScrollView;->mScroller:Landroid/widget/OverScroller;
+Landroid/widget/ImageView;->animateTransform(Landroid/graphics/Matrix;)V
 Landroid/widget/ImageView;->mAdjustViewBounds:Z
 Landroid/widget/ImageView;->mAlpha:I
 Landroid/widget/ImageView;->mDrawMatrix:Landroid/graphics/Matrix;
@@ -3002,6 +3013,8 @@
 Lcom/android/internal/telephony/ITelephony;->getCallState()I
 Lcom/android/internal/telephony/ITelephony;->getDataState()I
 Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
+Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V
+Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
 Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
@@ -3186,6 +3199,7 @@
 Ljava/lang/Float;->value:F
 Ljava/lang/Integer;->value:I
 Ljava/lang/Long;->value:J
+Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V
 Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
 Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
 Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index f3bc206..b598296 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -312,9 +312,7 @@
 Landroid/net/SntpClient;->requestTime(Ljava/lang/String;I)Z
 Landroid/net/StaticIpConfiguration;->dnsServers:Ljava/util/ArrayList;
 Landroid/net/StaticIpConfiguration;->domains:Ljava/lang/String;
-Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
 Landroid/net/StaticIpConfiguration;->getRoutes(Ljava/lang/String;)Ljava/util/List;
-Landroid/net/StaticIpConfiguration;->ipAddress:Landroid/net/LinkAddress;
 Landroid/net/StringNetworkSpecifier;->specifier:Ljava/lang/String;
 Landroid/net/TrafficStats;->getMobileTcpRxPackets()J
 Landroid/net/TrafficStats;->getMobileTcpTxPackets()J
@@ -717,6 +715,7 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
 Lcom/android/ims/internal/uce/presence/PresCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
 Lcom/android/ims/internal/uce/presence/PresCapInfo;->getContactUri()Ljava/lang/String;
+Lcom/android/ims/internal/uce/presence/PresCapInfo;->mContactUri:Ljava/lang/String;
 Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getMediaType()I
 Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceDesc()Ljava/lang/String;
 Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceId()Ljava/lang/String;
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a68136b..27bbc4b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2155,10 +2155,10 @@
     public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
             PersistableBundle appExtras, PersistableBundle launcherExtras,
             String dialogMessage) {
-        // TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
         try {
             return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
-                    launcherExtras, mContext.getOpPackageName(), mContext.getUserId());
+                    launcherExtras, dialogMessage, mContext.getOpPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4326ee3..86fedb1 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6678,7 +6678,8 @@
         public RemoteViews makeContentView(boolean increasedHeight) {
             mBuilder.mOriginalActions = mBuilder.mActions;
             mBuilder.mActions = new ArrayList<>();
-            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
+            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+                    true /* showReplyIcon */);
             mBuilder.mActions = mBuilder.mOriginalActions;
             mBuilder.mOriginalActions = null;
             return remoteViews;
@@ -6765,11 +6766,19 @@
          */
         @Override
         public RemoteViews makeBigContentView() {
-            return makeMessagingView(false /* isCollapsed */);
+            return makeMessagingView(false /* displayImagesAtEnd */, false /* showReplyIcon */);
         }
 
+        /**
+         * Create a messaging layout.
+         *
+         * @param displayImagesAtEnd should images be displayed at the end of the content instead
+         *                           of inline.
+         * @param showReplyIcon Should the reply affordance be shown at the end of the notification
+         * @return the created remoteView.
+         */
         @NonNull
-        private RemoteViews makeMessagingView(boolean isCollapsed) {
+        private RemoteViews makeMessagingView(boolean displayImagesAtEnd, boolean showReplyIcon) {
             CharSequence conversationTitle = !TextUtils.isEmpty(super.mBigContentTitle)
                     ? super.mBigContentTitle
                     : mConversationTitle;
@@ -6780,24 +6789,24 @@
                 nameReplacement = conversationTitle;
                 conversationTitle = null;
             }
-            boolean hideLargeIcon = !isCollapsed || isOneToOne;
+            boolean hideLargeIcon = !showReplyIcon || isOneToOne;
             RemoteViews contentView = mBuilder.applyStandardTemplateWithActions(
                     mBuilder.getMessagingLayoutResource(),
                     mBuilder.mParams.reset().hasProgress(false).title(conversationTitle).text(null)
                             .hideLargeIcon(hideLargeIcon)
                             .headerTextSecondary(conversationTitle)
-                            .alwaysShowReply(isCollapsed));
+                            .alwaysShowReply(showReplyIcon));
             addExtras(mBuilder.mN.extras);
             // also update the end margin if there is an image
             int endMargin = R.dimen.notification_content_margin_end;
-            if (isCollapsed) {
+            if (showReplyIcon) {
                 endMargin = R.dimen.notification_content_plus_picture_margin_end;
             }
             contentView.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.resolveContrastColor());
-            contentView.setBoolean(R.id.status_bar_latest_event_content, "setIsCollapsed",
-                    isCollapsed);
+            contentView.setBoolean(R.id.status_bar_latest_event_content, "setDisplayImagesAtEnd",
+                    displayImagesAtEnd);
             contentView.setIcon(R.id.status_bar_latest_event_content, "setLargeIcon",
                     mBuilder.mN.mLargeIcon);
             contentView.setCharSequence(R.id.status_bar_latest_event_content, "setNameReplacement",
@@ -6864,7 +6873,8 @@
          */
         @Override
         public RemoteViews makeHeadsUpContentView(boolean increasedHeight) {
-            RemoteViews remoteViews = makeMessagingView(true /* isCollapsed */);
+            RemoteViews remoteViews = makeMessagingView(true /* displayImagesAtEnd */,
+                    false /* showReplyIcon */);
             remoteViews.setInt(R.id.notification_messaging, "setMaxDisplayedLines", 1);
             return remoteViews;
         }
diff --git a/core/java/android/app/admin/FreezeInterval.java b/core/java/android/app/admin/FreezePeriod.java
similarity index 74%
rename from core/java/android/app/admin/FreezeInterval.java
rename to core/java/android/app/admin/FreezePeriod.java
index de5e21a..657f017 100644
--- a/core/java/android/app/admin/FreezeInterval.java
+++ b/core/java/android/app/admin/FreezePeriod.java
@@ -20,49 +20,88 @@
 import android.util.Pair;
 
 import java.time.LocalDate;
+import java.time.MonthDay;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.List;
 
 /**
- * An interval representing one freeze period which repeats annually. We use the number of days
- * since the start of (non-leap) year to define the start and end dates of an interval, both
- * inclusive. If the end date is smaller than the start date, the interval is considered wrapped
- * around the year-end. As far as an interval is concerned, February 29th should be treated as
- * if it were February 28th: so an interval starting or ending on February 28th are not
- * distinguishable from an interval on February 29th. When calulating interval length or
- * distance between two dates, February 29th is also disregarded.
+ * A class that represents one freeze period which repeats <em>annually</em>. A freeze period has
+ * two {@link java.time#MonthDay} values that define the start and end dates of the period, both
+ * inclusive. If the end date is earlier than the start date, the period is considered wrapped
+ * around the year-end. As far as freeze period is concerned, leap year is disregarded and February
+ * 29th should be treated as if it were February 28th: so a freeze starting or ending on February
+ * 28th is identical to a freeze starting or ending on February 29th. When calulating the length of
+ * a freeze or the distance bewteen two freee periods, February 29th is also ignored.
  *
  * @see SystemUpdatePolicy#setFreezePeriods
- * @hide
  */
-public class FreezeInterval {
-    private static final String TAG = "FreezeInterval";
+public class FreezePeriod {
+    private static final String TAG = "FreezePeriod";
 
     private static final int DUMMY_YEAR = 2001;
     static final int DAYS_IN_YEAR = 365; // 365 since DUMMY_YEAR is not a leap year
 
-    final int mStartDay; // [1,365]
-    final int mEndDay; // [1,365]
+    private final MonthDay mStart;
+    private final MonthDay mEnd;
 
-    FreezeInterval(int startDay, int endDay) {
-        if (startDay < 1 || startDay > 365 || endDay < 1 || endDay > 365) {
-            throw new RuntimeException("Bad dates for Interval: " + startDay + "," + endDay);
-        }
-        mStartDay = startDay;
-        mEndDay = endDay;
+    /*
+     * Start and end dates represented by number of days since the beginning of the year.
+     * They are internal representations of mStart and mEnd with normalized Leap year days
+     * (Feb 29 == Feb 28 == 59th day of year). All internal calclations are based on
+     * these two values so that leap year days are disregarded.
+     */
+    private final int mStartDay; // [1, 365]
+    private final int mEndDay; // [1, 365]
+
+    /**
+     * Creates a freeze period by its start and end dates. If the end date is earlier than the start
+     * date, the freeze period is considered wrapping year-end.
+     */
+    public FreezePeriod(MonthDay start, MonthDay end) {
+        mStart = start;
+        mStartDay = mStart.atYear(DUMMY_YEAR).getDayOfYear();
+        mEnd = end;
+        mEndDay = mEnd.atYear(DUMMY_YEAR).getDayOfYear();
     }
 
+    /**
+     * Returns the start date (inclusive) of this freeze period.
+     */
+    public MonthDay getStart() {
+        return mStart;
+    }
+
+    /**
+     * Returns the end date (inclusive) of this freeze period.
+     */
+    public MonthDay getEnd() {
+        return mEnd;
+    }
+
+    /**
+     * @hide
+     */
+    private FreezePeriod(int startDay, int endDay) {
+        mStartDay = startDay;
+        mStart = dayOfYearToMonthDay(startDay);
+        mEndDay = endDay;
+        mEnd = dayOfYearToMonthDay(endDay);
+    }
+
+    /** @hide */
     int getLength() {
         return getEffectiveEndDay() - mStartDay + 1;
     }
 
+    /** @hide */
     boolean isWrapped() {
         return mEndDay < mStartDay;
     }
 
     /**
      * Returns the effective end day, taking wrapping around year-end into consideration
+     * @hide
      */
     int getEffectiveEndDay() {
         if (!isWrapped()) {
@@ -72,6 +111,7 @@
         }
     }
 
+    /** @hide */
     boolean contains(LocalDate localDate) {
         final int daysOfYear = dayOfYearDisregardLeapYear(localDate);
         if (!isWrapped()) {
@@ -84,6 +124,7 @@
         }
     }
 
+    /** @hide */
     boolean after(LocalDate localDate) {
         return mStartDay > dayOfYearDisregardLeapYear(localDate);
     }
@@ -95,6 +136,7 @@
      * include now, the returned dates represents the next future interval.
      * The result will always have the same month and dayOfMonth value as the non-instantiated
      * interval itself.
+     * @hide
      */
     Pair<LocalDate, LocalDate> toCurrentOrFutureRealDates(LocalDate now) {
         final int nowDays = dayOfYearDisregardLeapYear(now);
@@ -138,14 +180,24 @@
                 + LocalDate.ofYearDay(DUMMY_YEAR, mEndDay).format(formatter);
     }
 
-    // Treat the supplied date as in a non-leap year and return its day of year.
-    static int dayOfYearDisregardLeapYear(LocalDate date) {
+    /** @hide */
+    private static MonthDay dayOfYearToMonthDay(int dayOfYear) {
+        LocalDate date = LocalDate.ofYearDay(DUMMY_YEAR, dayOfYear);
+        return MonthDay.of(date.getMonth(), date.getDayOfMonth());
+    }
+
+    /**
+     * Treat the supplied date as in a non-leap year and return its day of year.
+     * @hide
+     */
+    private static int dayOfYearDisregardLeapYear(LocalDate date) {
         return date.withYear(DUMMY_YEAR).getDayOfYear();
     }
 
     /**
      * Compute the number of days between first (inclusive) and second (exclusive),
      * treating all years in between as non-leap.
+     * @hide
      */
     public static int distanceWithoutLeapYear(LocalDate first, LocalDate second) {
         return dayOfYearDisregardLeapYear(first) - dayOfYearDisregardLeapYear(second)
@@ -165,16 +217,16 @@
      *     3. At most one wrapped Interval remains, and it will be at the end of the list
      * @hide
      */
-    protected static List<FreezeInterval> canonicalizeIntervals(List<FreezeInterval> intervals) {
+    static List<FreezePeriod> canonicalizePeriods(List<FreezePeriod> intervals) {
         boolean[] taken = new boolean[DAYS_IN_YEAR];
         // First convert the intervals into flat array
-        for (FreezeInterval interval : intervals) {
+        for (FreezePeriod interval : intervals) {
             for (int i = interval.mStartDay; i <= interval.getEffectiveEndDay(); i++) {
                 taken[(i - 1) % DAYS_IN_YEAR] = true;
             }
         }
         // Then reconstruct intervals from the array
-        List<FreezeInterval> result = new ArrayList<>();
+        List<FreezePeriod> result = new ArrayList<>();
         int i = 0;
         while (i < DAYS_IN_YEAR) {
             if (!taken[i]) {
@@ -183,14 +235,14 @@
             }
             final int intervalStart = i + 1;
             while (i < DAYS_IN_YEAR && taken[i]) i++;
-            result.add(new FreezeInterval(intervalStart, i));
+            result.add(new FreezePeriod(intervalStart, i));
         }
         // Check if the last entry can be merged to the first entry to become one single
         // wrapped interval
         final int lastIndex = result.size() - 1;
         if (lastIndex > 0 && result.get(lastIndex).mEndDay == DAYS_IN_YEAR
                 && result.get(0).mStartDay == 1) {
-            FreezeInterval wrappedInterval = new FreezeInterval(result.get(lastIndex).mStartDay,
+            FreezePeriod wrappedInterval = new FreezePeriod(result.get(lastIndex).mStartDay,
                     result.get(0).mEndDay);
             result.set(lastIndex, wrappedInterval);
             result.remove(0);
@@ -207,18 +259,18 @@
      *
      * @hide
      */
-    protected static void validatePeriods(List<FreezeInterval> periods) {
-        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+    static void validatePeriods(List<FreezePeriod> periods) {
+        List<FreezePeriod> allPeriods = FreezePeriod.canonicalizePeriods(periods);
         if (allPeriods.size() != periods.size()) {
             throw SystemUpdatePolicy.ValidationFailedException.duplicateOrOverlapPeriods();
         }
         for (int i = 0; i < allPeriods.size(); i++) {
-            FreezeInterval current = allPeriods.get(i);
+            FreezePeriod current = allPeriods.get(i);
             if (current.getLength() > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
                 throw SystemUpdatePolicy.ValidationFailedException.freezePeriodTooLong("Freeze "
                         + "period " + current + " is too long: " + current.getLength() + " days");
             }
-            FreezeInterval previous = i > 0 ? allPeriods.get(i - 1)
+            FreezePeriod previous = i > 0 ? allPeriods.get(i - 1)
                     : allPeriods.get(allPeriods.size() - 1);
             if (previous != current) {
                 final int separation;
@@ -247,7 +299,7 @@
      *
      * @hide
      */
-    protected static void validateAgainstPreviousFreezePeriod(List<FreezeInterval> periods,
+    static void validateAgainstPreviousFreezePeriod(List<FreezePeriod> periods,
             LocalDate prevPeriodStart, LocalDate prevPeriodEnd, LocalDate now) {
         if (periods.size() == 0 || prevPeriodStart == null || prevPeriodEnd == null) {
             return;
@@ -258,14 +310,14 @@
             // Clock was adjusted backwards. We can continue execution though, the separation
             // and length validation below still works under this condition.
         }
-        List<FreezeInterval> allPeriods = FreezeInterval.canonicalizeIntervals(periods);
+        List<FreezePeriod> allPeriods = FreezePeriod.canonicalizePeriods(periods);
         // Given current time now, find the freeze period that's either current, or the one
         // that's immediately afterwards. For the later case, it might be after the year-end,
         // but this can only happen if there is only one freeze period.
-        FreezeInterval curOrNextFreezePeriod = allPeriods.get(0);
-        for (FreezeInterval interval : allPeriods) {
+        FreezePeriod curOrNextFreezePeriod = allPeriods.get(0);
+        for (FreezePeriod interval : allPeriods) {
             if (interval.contains(now)
-                    || interval.mStartDay > FreezeInterval.dayOfYearDisregardLeapYear(now)) {
+                    || interval.mStartDay > FreezePeriod.dayOfYearDisregardLeapYear(now)) {
                 curOrNextFreezePeriod = interval;
                 break;
             }
@@ -282,7 +334,7 @@
         // Now validate [prevPeriodStart, prevPeriodEnd] against curOrNextFreezeDates
         final String periodsDescription = "Prev: " + prevPeriodStart + "," + prevPeriodEnd
                 + "; cur: " + curOrNextFreezeDates.first + "," + curOrNextFreezeDates.second;
-        long separation = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.first,
+        long separation = FreezePeriod.distanceWithoutLeapYear(curOrNextFreezeDates.first,
                 prevPeriodEnd) - 1;
         if (separation > 0) {
             // Two intervals do not overlap, check separation
@@ -292,7 +344,7 @@
             }
         } else {
             // Two intervals overlap, check combined length
-            long length = FreezeInterval.distanceWithoutLeapYear(curOrNextFreezeDates.second,
+            long length = FreezePeriod.distanceWithoutLeapYear(curOrNextFreezeDates.second,
                     prevPeriodStart) + 1;
             if (length > SystemUpdatePolicy.FREEZE_PERIOD_MAX_LENGTH) {
                 throw ValidationFailedException.combinedPeriodTooLong("Combined freeze period "
diff --git a/core/java/android/app/admin/SystemUpdatePolicy.java b/core/java/android/app/admin/SystemUpdatePolicy.java
index 47b3a81..20eef6c 100644
--- a/core/java/android/app/admin/SystemUpdatePolicy.java
+++ b/core/java/android/app/admin/SystemUpdatePolicy.java
@@ -38,9 +38,11 @@
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
+import java.time.MonthDay;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
@@ -51,7 +53,7 @@
  * @see DevicePolicyManager#setSystemUpdatePolicy
  * @see DevicePolicyManager#getSystemUpdatePolicy
  */
-public class SystemUpdatePolicy implements Parcelable {
+public final class SystemUpdatePolicy implements Parcelable {
     private static final String TAG = "SystemUpdatePolicy";
 
     /** @hide */
@@ -163,6 +165,7 @@
                 ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE,
                 ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG,
                 ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE,
+                ERROR_UNKNOWN,
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface ValidationFailureType {}
@@ -171,33 +174,38 @@
         public static final int ERROR_NONE = 0;
 
         /**
+         * Validation failed with unknown error.
+         */
+        public static final int ERROR_UNKNOWN = 1;
+
+        /**
          * The freeze periods contains duplicates, periods that overlap with each
          * other or periods whose start and end joins.
          */
-        public static final int ERROR_DUPLICATE_OR_OVERLAP = 1;
+        public static final int ERROR_DUPLICATE_OR_OVERLAP = 2;
 
         /**
          * There exists at least one freeze period whose length exceeds 90 days.
          */
-        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 2;
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_LONG = 3;
 
         /**
          * There exists some freeze period which starts within 60 days of the preceding period's
          * end time.
          */
-        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 3;
+        public static final int ERROR_NEW_FREEZE_PERIOD_TOO_CLOSE = 4;
 
         /**
          * The device has been in a freeze period and when combining with the new freeze period
          * to be set, it will result in the total freeze period being longer than 90 days.
          */
-        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 4;
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_LONG = 5;
 
         /**
          * The device has been in a freeze period and some new freeze period to be set is less
          * than 60 days from the end of the last freeze period the device went through.
          */
-        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 5;
+        public static final int ERROR_COMBINED_FREEZE_PERIOD_TOO_CLOSE = 6;
 
         @ValidationFailureType
         private final int mErrorCode;
@@ -272,7 +280,7 @@
     private int mMaintenanceWindowStart;
     private int mMaintenanceWindowEnd;
 
-    private final ArrayList<FreezeInterval> mFreezePeriods;
+    private final ArrayList<FreezePeriod> mFreezePeriods;
 
     private SystemUpdatePolicy() {
         mPolicyType = TYPE_UNKNOWN;
@@ -444,12 +452,10 @@
      *         requirement set above
      * @return this instance
      */
-    public SystemUpdatePolicy setFreezePeriods(List<Pair<Integer, Integer>> freezePeriods) {
-        List<FreezeInterval> newPeriods = freezePeriods.stream().map(
-                p -> new FreezeInterval(p.first, p.second)).collect(Collectors.toList());
-        FreezeInterval.validatePeriods(newPeriods);
+    public SystemUpdatePolicy setFreezePeriods(List<FreezePeriod> freezePeriods) {
+        FreezePeriod.validatePeriods(freezePeriods);
         mFreezePeriods.clear();
-        mFreezePeriods.addAll(newPeriods);
+        mFreezePeriods.addAll(freezePeriods);
         return this;
     }
 
@@ -458,12 +464,8 @@
      *
      * @return the list of freeze periods, or an empty list if none was set.
      */
-    public List<Pair<Integer, Integer>> getFreezePeriods() {
-        List<Pair<Integer, Integer>> result = new ArrayList<>(mFreezePeriods.size());
-        for (FreezeInterval interval : mFreezePeriods) {
-            result.add(new Pair<>(interval.mStartDay, interval.mEndDay));
-        }
-        return result;
+    public List<FreezePeriod> getFreezePeriods() {
+        return Collections.unmodifiableList(mFreezePeriods);
     }
 
     /**
@@ -472,7 +474,7 @@
      * @hide
      */
     public Pair<LocalDate, LocalDate> getCurrentFreezePeriod(LocalDate now) {
-        for (FreezeInterval interval : mFreezePeriods) {
+        for (FreezePeriod interval : mFreezePeriods) {
             if (interval.contains(now)) {
                 return interval.toCurrentOrFutureRealDates(now);
             }
@@ -485,10 +487,10 @@
      * is not within a freeze period.
      */
     private long timeUntilNextFreezePeriod(long now) {
-        List<FreezeInterval> sortedPeriods = FreezeInterval.canonicalizeIntervals(mFreezePeriods);
+        List<FreezePeriod> sortedPeriods = FreezePeriod.canonicalizePeriods(mFreezePeriods);
         LocalDate nowDate = millisToDate(now);
         LocalDate nextFreezeStart = null;
-        for (FreezeInterval interval : sortedPeriods) {
+        for (FreezePeriod interval : sortedPeriods) {
             if (interval.after(nowDate)) {
                 nextFreezeStart = interval.toCurrentOrFutureRealDates(nowDate).first;
                 break;
@@ -506,13 +508,13 @@
 
     /** @hide */
     public void validateFreezePeriods() {
-        FreezeInterval.validatePeriods(mFreezePeriods);
+        FreezePeriod.validatePeriods(mFreezePeriods);
     }
 
     /** @hide */
     public void validateAgainstPreviousFreezePeriod(LocalDate prevPeriodStart,
             LocalDate prevPeriodEnd, LocalDate now) {
-        FreezeInterval.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
+        FreezePeriod.validateAgainstPreviousFreezePeriod(mFreezePeriods, prevPeriodStart,
                 prevPeriodEnd, now);
     }
 
@@ -521,10 +523,10 @@
      * updates and how long this action is valid for, given the current system update policy. Its
      * action could be one of the following
      * <ul>
-     * <li> {@code TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and without
-     * user intervention as soon as they become available.
-     * <li> {@code TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
-     * <li> {@code TYPE_PAUSE} system updates should be postponed indefinitely until further notice
+     * <li> {@link #TYPE_INSTALL_AUTOMATIC} system updates should be installed immedately and
+     * without user intervention as soon as they become available.
+     * <li> {@link #TYPE_POSTPONE} system updates should be postponed for a maximum of 30 days
+     * <li> {@link #TYPE_PAUSE} system updates should be postponed indefinitely until further notice
      * </ul>
      *
      * The effective time measures how long this installation option is valid for from the queried
@@ -535,18 +537,38 @@
      */
     @SystemApi
     public static class InstallationOption {
+        /** @hide */
+        @IntDef(prefix = { "TYPE_" }, value = {
+                TYPE_INSTALL_AUTOMATIC,
+                TYPE_PAUSE,
+                TYPE_POSTPONE
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface InstallationOptionType {}
+
+        @InstallationOptionType
         private final int mType;
         private long mEffectiveTime;
 
-        InstallationOption(int type, long effectiveTime) {
+        InstallationOption(@InstallationOptionType int type, long effectiveTime) {
             this.mType = type;
             this.mEffectiveTime = effectiveTime;
         }
 
-        public int getType() {
+        /**
+         * Returns the type of the current installation option, could be one of
+         * {@link #TYPE_INSTALL_AUTOMATIC}, {@link #TYPE_POSTPONE} and {@link #TYPE_PAUSE}.
+         * @return type of installation option.
+         */
+        public @InstallationOptionType int getType() {
             return mType;
         }
 
+        /**
+         * Returns how long the current installation option in effective for, starting from the time
+         * of query.
+         * @return the effective time in milliseconds.
+         */
         public long getEffectiveTime() {
             return mEffectiveTime;
         }
@@ -667,9 +689,11 @@
         int freezeCount = mFreezePeriods.size();
         dest.writeInt(freezeCount);
         for (int i = 0; i < freezeCount; i++) {
-            FreezeInterval interval = mFreezePeriods.get(i);
-            dest.writeInt(interval.mStartDay);
-            dest.writeInt(interval.mEndDay);
+            FreezePeriod interval = mFreezePeriods.get(i);
+            dest.writeInt(interval.getStart().getMonthValue());
+            dest.writeInt(interval.getStart().getDayOfMonth());
+            dest.writeInt(interval.getEnd().getMonthValue());
+            dest.writeInt(interval.getEnd().getDayOfMonth());
         }
     }
 
@@ -686,8 +710,9 @@
                     int freezeCount = source.readInt();
                     policy.mFreezePeriods.ensureCapacity(freezeCount);
                     for (int i = 0; i < freezeCount; i++) {
-                        policy.mFreezePeriods.add(
-                                new FreezeInterval(source.readInt(), source.readInt()));
+                        MonthDay start = MonthDay.of(source.readInt(), source.readInt());
+                        MonthDay end = MonthDay.of(source.readInt(), source.readInt());
+                        policy.mFreezePeriods.add(new FreezePeriod(start, end));
                     }
                     return policy;
                 }
@@ -730,9 +755,9 @@
                     if (!parser.getName().equals(KEY_FREEZE_TAG)) {
                         continue;
                     }
-                    policy.mFreezePeriods.add(new FreezeInterval(
-                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_START)),
-                            Integer.parseInt(parser.getAttributeValue(null, KEY_FREEZE_END))));
+                    policy.mFreezePeriods.add(new FreezePeriod(
+                            MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_START)),
+                            MonthDay.parse(parser.getAttributeValue(null, KEY_FREEZE_END))));
                 }
                 return policy;
             }
@@ -751,10 +776,10 @@
         out.attribute(null, KEY_INSTALL_WINDOW_START, Integer.toString(mMaintenanceWindowStart));
         out.attribute(null, KEY_INSTALL_WINDOW_END, Integer.toString(mMaintenanceWindowEnd));
         for (int i = 0; i < mFreezePeriods.size(); i++) {
-            FreezeInterval interval = mFreezePeriods.get(i);
+            FreezePeriod interval = mFreezePeriods.get(i);
             out.startTag(null, KEY_FREEZE_TAG);
-            out.attribute(null, KEY_FREEZE_START, Integer.toString(interval.mStartDay));
-            out.attribute(null, KEY_FREEZE_END, Integer.toString(interval.mEndDay));
+            out.attribute(null, KEY_FREEZE_START, interval.getStart().toString());
+            out.attribute(null, KEY_FREEZE_END, interval.getEnd().toString());
             out.endTag(null, KEY_FREEZE_TAG);
         }
     }
diff --git a/core/java/android/app/usage/TimeSparseArray.java b/core/java/android/app/usage/TimeSparseArray.java
index 9ef88e4..4ec0e9e 100644
--- a/core/java/android/app/usage/TimeSparseArray.java
+++ b/core/java/android/app/usage/TimeSparseArray.java
@@ -88,7 +88,7 @@
                 key++;
                 keyIndex++;
             }
-            if (key >= origKey + 10) {
+            if (key >= origKey + 100) {
                 Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
                         + " displaced to " + key);
             }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 000912c..efc9b6d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2282,6 +2282,28 @@
     public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
 
     /**
+     * Activity Action: Started to show more details about why an application was suspended.
+     *
+     * <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
+     * handling this intent and protect it with
+     * {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
+     *
+     * <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the suspended package.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see PackageManager#isPackageSuspended()
+     * @see #ACTION_PACKAGES_SUSPENDED
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS =
+            "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
+
+    /**
      * Broadcast Action: Sent to a package that has been unsuspended.
      *
      * <p class="note">This is a protected intent that can only be sent
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index e85058d..49b1efd 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -590,26 +590,33 @@
     public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * OEM partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_OEM = 1 << 17;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * vendor partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_VENDOR = 1 << 18;
 
     /**
-     * Value for {@linl #privateFlags}: whether this app is pre-installed on the
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
      * product partition of the system image.
      * @hide
      */
     public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
 
+    /**
+     * Value for {@link #privateFlags}: whether this app is signed with the
+     * platform key.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY = 1 << 20;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
             PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
@@ -629,6 +636,7 @@
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+            PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
             PRIVATE_FLAG_VENDOR,
             PRIVATE_FLAG_VIRTUAL_PRELOAD,
@@ -904,7 +912,17 @@
      * The app's declared version code.
      * @hide
      */
-    public long versionCode;
+    public long longVersionCode;
+
+    /**
+     * An integer representation of the app's declared version code. This is being left in place as
+     * some apps were using reflection to access it before the move to long in
+     * {@link android.os.Build.VERSION_CODES#P}
+     * @deprecated Use {@link #longVersionCode} instead.
+     * @hide
+     */
+    @Deprecated
+    public int versionCode;
 
     /**
      * The user-visible SDK version (ex. 26) of the framework against which the application claims
@@ -1114,11 +1132,12 @@
      */
     public static final int HIDDEN_API_ENFORCEMENT_NONE = 0;
     /**
-     * Light grey list enforcement, the strictest option. Enforces the light grey, dark grey and
-     * black lists.
+     * No API enforcement, but enable the detection logic and warnings. Observed behaviour is the
+     * same as {@link #HIDDEN_API_ENFORCEMENT_NONE} but you may see warnings in the log when APIs
+     * are accessed.
      * @hide
      * */
-    public static final int HIDDEN_API_ENFORCEMENT_ALL_LISTS = 1;
+    public static final int HIDDEN_API_ENFORCEMENT_JUST_WARN = 1;
     /**
      * Dark grey list enforcement. Enforces the dark grey and black lists
      * @hide
@@ -1140,7 +1159,7 @@
     @IntDef(prefix = { "HIDDEN_API_ENFORCEMENT_" }, value = {
             HIDDEN_API_ENFORCEMENT_DEFAULT,
             HIDDEN_API_ENFORCEMENT_NONE,
-            HIDDEN_API_ENFORCEMENT_ALL_LISTS,
+            HIDDEN_API_ENFORCEMENT_JUST_WARN,
             HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK,
             HIDDEN_API_ENFORCEMENT_BLACK,
     })
@@ -1214,7 +1233,7 @@
         pw.println(prefix + "enabled=" + enabled
                 + " minSdkVersion=" + minSdkVersion
                 + " targetSdkVersion=" + targetSdkVersion
-                + " versionCode=" + versionCode
+                + " versionCode=" + longVersionCode
                 + " targetSandboxVersion=" + targetSandboxVersion);
         if ((dumpFlags & DUMP_FLAG_DETAILS) != 0) {
             if (manageSpaceActivityName != null) {
@@ -1287,7 +1306,7 @@
         proto.write(ApplicationInfoProto.Version.ENABLED, enabled);
         proto.write(ApplicationInfoProto.Version.MIN_SDK_VERSION, minSdkVersion);
         proto.write(ApplicationInfoProto.Version.TARGET_SDK_VERSION, targetSdkVersion);
-        proto.write(ApplicationInfoProto.Version.VERSION_CODE, versionCode);
+        proto.write(ApplicationInfoProto.Version.VERSION_CODE, longVersionCode);
         proto.write(ApplicationInfoProto.Version.TARGET_SANDBOX_VERSION, targetSandboxVersion);
         proto.end(versionToken);
 
@@ -1421,7 +1440,7 @@
         uid = orig.uid;
         minSdkVersion = orig.minSdkVersion;
         targetSdkVersion = orig.targetSdkVersion;
-        versionCode = orig.versionCode;
+        setVersionCode(orig.longVersionCode);
         enabled = orig.enabled;
         enabledSetting = orig.enabledSetting;
         installLocation = orig.installLocation;
@@ -1495,7 +1514,7 @@
         dest.writeInt(uid);
         dest.writeInt(minSdkVersion);
         dest.writeInt(targetSdkVersion);
-        dest.writeLong(versionCode);
+        dest.writeLong(longVersionCode);
         dest.writeInt(enabled ? 1 : 0);
         dest.writeInt(enabledSetting);
         dest.writeInt(installLocation);
@@ -1566,7 +1585,7 @@
         uid = source.readInt();
         minSdkVersion = source.readInt();
         targetSdkVersion = source.readInt();
-        versionCode = source.readLong();
+        setVersionCode(source.readLong());
         enabled = source.readInt() != 0;
         enabledSetting = source.readInt();
         installLocation = source.readInt();
@@ -1658,6 +1677,11 @@
         return SystemConfig.getInstance().getHiddenApiWhitelistedApps().contains(packageName);
     }
 
+    private boolean isAllowedToUseHiddenApis() {
+        return isSignedWithPlatformKey()
+            || (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp()));
+    }
+
     /**
      * @hide
      */
@@ -1665,7 +1689,7 @@
         if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
             return mHiddenApiPolicy;
         }
-        if (isPackageWhitelistedForHiddenApis() && (isSystemApp() || isUpdatedSystemApp())) {
+        if (isAllowedToUseHiddenApis()) {
             return HIDDEN_API_ENFORCEMENT_NONE;
         }
         return HIDDEN_API_ENFORCEMENT_BLACK;
@@ -1684,6 +1708,14 @@
     /**
      * @hide
      */
+    public void setVersionCode(long newVersionCode) {
+        longVersionCode = newVersionCode;
+        versionCode = (int) newVersionCode;
+    }
+
+    /**
+     * @hide
+     */
     @Override
     public Drawable loadDefaultIcon(PackageManager pm) {
         if ((flags & FLAG_EXTERNAL_STORAGE) != 0
@@ -1758,6 +1790,11 @@
     }
 
     /** @hide */
+    public boolean isSignedWithPlatformKey() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY) != 0;
+    }
+
+    /** @hide */
     @TestApi
     public boolean isPrivilegedApp() {
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 277738b..02ce47b8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -273,8 +273,8 @@
     void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
 
     String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
-            in PersistableBundle launcherExtras, in PersistableBundle appExtras,
-            String callingPackage, int userId);
+            in PersistableBundle appExtras, in PersistableBundle launcherExtras,
+            String dialogMessage, String callingPackage, int userId);
 
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index 1c3c5c7..a9d0911 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -191,10 +191,10 @@
     /**
      * Retrieve launcher extras for a suspended package provided to the system in
      * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
-     * PersistableBundle, String)}
+     * PersistableBundle, String)}.
      *
      * @param packageName The package for which to return launcher extras.
-     * @param userId The user for which to check,
+     * @param userId The user for which to check.
      * @return The launcher extras.
      *
      * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
@@ -214,6 +214,29 @@
     public abstract boolean isPackageSuspended(String packageName, int userId);
 
     /**
+     * Get the name of the package that suspended the given package. Packages can be suspended by
+     * device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
+     * {@link android.Manifest.permission#SUSPEND_APPS}.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param userId The user for which to check.
+     * @return Name of the package that suspended the given package. Returns {@code null} if the
+     * given package is not currently suspended and the platform package name - i.e.
+     * {@code "android"} - if the package was suspended by a device admin.
+     */
+    public abstract String getSuspendingPackage(String suspendedPackage, int userId);
+
+    /**
+     * Get the dialog message to be shown to the user when they try to launch a suspended
+     * application.
+     *
+     * @param suspendedPackage The package that has been suspended.
+     * @param userId The user for which to check.
+     * @return The dialog message to be shown to the user.
+     */
+    public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
+
+    /**
      * Do a straight uid lookup for the given package/application in the given user.
      * @see PackageManager#getPackageUidAsUser(String, int, int)
      * @return The app's uid, or < 0 if the package was not found in that user
@@ -438,7 +461,7 @@
      * Resolves an activity intent, allowing instant apps to be resolved.
      */
     public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
-            int flags, int userId, boolean resolveForStart);
+            int flags, int userId, boolean resolveForStart, int filterCallingUid);
 
     /**
     * Resolves a service intent, allowing instant apps to be resolved.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2f0faf2..3e0db60 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1918,7 +1918,7 @@
                 com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
         pkg.mVersionCodeMajor = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_versionCodeMajor, 0);
-        pkg.applicationInfo.versionCode = pkg.getLongVersionCode();
+        pkg.applicationInfo.setVersionCode(pkg.getLongVersionCode());
         pkg.baseRevisionCode = sa.getInteger(
                 com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
         pkg.mVersionName = sa.getNonConfigurationString(
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index f7b6e09..f471a1d 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -34,6 +34,7 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.Arrays;
+import java.util.Objects;
 
 /**
  * Per-user state information about a package.
@@ -47,6 +48,7 @@
     public boolean hidden; // Is the app restricted by owner / admin
     public boolean suspended;
     public String suspendingPackage;
+    public String dialogMessage; // Message to show when a suspended package launch attempt is made
     public PersistableBundle suspendedAppExtras;
     public PersistableBundle suspendedLauncherExtras;
     public boolean instantApp;
@@ -82,6 +84,7 @@
         hidden = o.hidden;
         suspended = o.suspended;
         suspendingPackage = o.suspendingPackage;
+        dialogMessage = o.dialogMessage;
         suspendedAppExtras = o.suspendedAppExtras;
         suspendedLauncherExtras = o.suspendedLauncherExtras;
         instantApp = o.instantApp;
@@ -208,6 +211,9 @@
                     || !suspendingPackage.equals(oldState.suspendingPackage)) {
                 return false;
             }
+            if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
+                return false;
+            }
             if (!BaseBundle.kindofEquals(suspendedAppExtras,
                     oldState.suspendedAppExtras)) {
                 return false;
diff --git a/core/java/android/hardware/biometrics/BiometricDialog.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
similarity index 93%
rename from core/java/android/hardware/biometrics/BiometricDialog.java
rename to core/java/android/hardware/biometrics/BiometricPrompt.java
index dd848a3..1c9de45 100644
--- a/core/java/android/hardware/biometrics/BiometricDialog.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -38,7 +38,7 @@
 /**
  * A class that manages a system-provided biometric dialog.
  */
-public class BiometricDialog implements BiometricAuthenticator, BiometricConstants {
+public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants {
 
     /**
      * @hide
@@ -190,11 +190,11 @@
         }
 
         /**
-         * Creates a {@link BiometricDialog}.
-         * @return a {@link BiometricDialog}
+         * Creates a {@link BiometricPrompt}.
+         * @return a {@link BiometricPrompt}
          * @throws IllegalArgumentException if any of the required fields are not set.
          */
-        public BiometricDialog build() {
+        public BiometricPrompt build() {
             final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
             final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
 
@@ -203,7 +203,7 @@
             } else if (TextUtils.isEmpty(negative)) {
                 throw new IllegalArgumentException("Negative text must be set and non-empty");
             }
-            return new BiometricDialog(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
+            return new BiometricPrompt(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
         }
     }
 
@@ -213,7 +213,7 @@
     private ButtonInfo mPositiveButtonInfo;
     private ButtonInfo mNegativeButtonInfo;
 
-    IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
+    IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
         @Override
         public void onDialogDismissed(int reason) {
             // Check the reason and invoke OnClickListener(s) if necessary
@@ -229,7 +229,7 @@
         }
     };
 
-    private BiometricDialog(Context context, Bundle bundle,
+    private BiometricPrompt(Context context, Bundle bundle,
             ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
         mBundle = bundle;
         mPositiveButtonInfo = positiveButtonInfo;
@@ -239,7 +239,7 @@
     }
 
     /**
-     * A wrapper class for the crypto objects supported by BiometricDialog. Currently the framework
+     * A wrapper class for the crypto objects supported by BiometricPrompt. Currently the framework
      * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
      */
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -308,8 +308,8 @@
     }
 
     /**
-     * Callback structure provided to {@link BiometricDialog#authenticate(CancellationSignal,
-     * Executor, AuthenticationCallback)} or {@link BiometricDialog#authenticate(CryptoObject,
+     * Callback structure provided to {@link BiometricPrompt#authenticate(CancellationSignal,
+     * Executor, AuthenticationCallback)} or {@link BiometricPrompt#authenticate(CryptoObject,
      * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
      * of this for listening to authentication events.
      */
@@ -378,7 +378,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
@@ -395,7 +395,7 @@
     public void authenticate(@NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(cancel, executor, (AuthenticationCallback) callback);
@@ -410,8 +410,8 @@
      * operation can be canceled by using the provided cancel object. The application will receive
      * authentication errors through {@link AuthenticationCallback}, and button events through the
      * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
-     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricDialog} object,
-     * and calling {@link BiometricDialog#authenticate( CancellationSignal, Executor,
+     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricPrompt} object,
+     * and calling {@link BiometricPrompt#authenticate( CancellationSignal, Executor,
      * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
      * previous client and start a new authentication. The interrupted client will receive a
      * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
@@ -445,8 +445,8 @@
      * provided cancel object. The application will receive authentication errors through {@link
      * AuthenticationCallback}, and button events through the corresponding callback set in {@link
      * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.  It is
-     * safe to reuse the {@link BiometricDialog} object, and calling {@link
-     * BiometricDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
+     * safe to reuse the {@link BiometricPrompt} object, and calling {@link
+     * BiometricPrompt#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
      * an existing authentication attempt is occurring will stop the previous client and start a new
      * authentication. The interrupted client will receive a cancelled notification through {@link
      * AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
@@ -470,15 +470,15 @@
     private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
             Executor executor) {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
                       executor);
             return true;
         } else if (!mFingerprintManager.isHardwareDetected()) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
                       executor);
             return true;
         } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
-            sendError(BiometricDialog.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
+            sendError(BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
                       executor);
             return true;
         }
diff --git a/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
similarity index 87%
rename from core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
rename to core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
index e528aa7..67c9346 100644
--- a/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
@@ -19,9 +19,9 @@
 import android.os.UserHandle;
 
 /**
- * Communication channel from the BiometricDialog (SysUI) back to AuthenticationClient.
+ * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
  * @hide
  */
-oneway interface IBiometricDialogReceiver {
+oneway interface IBiometricPromptReceiver {
     void onDialogDismissed(int reason);
 }
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a6c8c67..40d31bf 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -31,9 +31,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricDialog;
 import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -57,7 +57,7 @@
 
 /**
  * A class that coordinates access to the fingerprint hardware.
- * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
+ * @deprecated See {@link BiometricPrompt} which shows a system-provided dialog upon starting
  * authentication. In a world where devices may have different types of biometric authentication,
  * it's much more realistic to have a system-provided authentication dialog since the method may
  * vary by vendor/device.
@@ -111,7 +111,7 @@
     /**
      * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
      * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
      */
     @Deprecated
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -155,7 +155,7 @@
     /**
      * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
      *     CancellationSignal, int, AuthenticationCallback, Handler)}.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
      */
     @Deprecated
     public static class AuthenticationResult {
@@ -204,7 +204,7 @@
      * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
      * fingerprint events.
-     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
+     * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
      */
     @Deprecated
     public static abstract class AuthenticationCallback
@@ -378,10 +378,10 @@
      *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
      *         facility</a>.
      * @throws IllegalStateException if the crypto primitive is not initialized.
-     * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
-     * BiometricDialog.CryptoObject, CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)}
+     * @deprecated See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)} and {@link BiometricPrompt#authenticate(
+     * BiometricPrompt.CryptoObject, CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)}
      */
     @Deprecated
     @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
@@ -444,7 +444,7 @@
 
     /**
      * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
+     * CancellationSignal, Bundle, Executor, IBiometricPromptReceiver, AuthenticationCallback)}
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      */
     private void authenticate(int userId,
@@ -452,7 +452,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         mCryptoObject = crypto;
         if (cancel.isCanceled()) {
@@ -480,8 +480,8 @@
     }
 
     /**
-     * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
-     * BiometricDialog.AuthenticationCallback)}
+     * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+     * BiometricPrompt.AuthenticationCallback)}
      * @param cancel
      * @param executor
      * @param callback
@@ -491,7 +491,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
@@ -512,8 +512,8 @@
     }
 
     /**
-     * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
-     * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
+     * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
+     * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
      * @param crypto
      * @param cancel
      * @param executor
@@ -524,7 +524,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IBiometricDialogReceiver receiver,
+            @NonNull IBiometricPromptReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (crypto == null) {
             throw new IllegalArgumentException("Must supply a crypto object");
@@ -743,7 +743,7 @@
      * Determine if there is at least one fingerprint enrolled.
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
-     * @deprecated See {@link BiometricDialog} and
+     * @deprecated See {@link BiometricPrompt} and
      * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
      */
     @Deprecated
@@ -777,7 +777,7 @@
      * Determine if fingerprint hardware is present and functional.
      *
      * @return true if hardware is present and functional, false otherwise.
-     * @deprecated See {@link BiometricDialog} and
+     * @deprecated See {@link BiometricPrompt} and
      * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
      */
     @Deprecated
@@ -1158,7 +1158,7 @@
         @Override // binder call
         public void onError(long deviceId, int error, int vendorCode) {
             if (mExecutor != null) {
-                // BiometricDialog case
+                // BiometricPrompt case
                 if (error == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED) {
                     // User tapped somewhere to cancel, the biometric dialog is already dismissed.
                     mExecutor.execute(() -> {
@@ -1172,7 +1172,7 @@
                         mExecutor.execute(() -> {
                             sendErrorResult(deviceId, error, vendorCode);
                         });
-                    }, BiometricDialog.HIDE_DIALOG_DELAY);
+                    }, BiometricPrompt.HIDE_DIALOG_DELAY);
                 }
             } else {
                 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 78d01e5..8aa2183 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -16,7 +16,7 @@
 package android.hardware.fingerprint;
 
 import android.os.Bundle;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
@@ -31,7 +31,7 @@
     // Authenticate the given sessionId with a fingerprint
     void authenticate(IBinder token, long sessionId, int userId,
             IFingerprintServiceReceiver receiver, int flags, String opPackageName,
-            in Bundle bundle, IBiometricDialogReceiver dialogReceiver);
+            in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
 
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index e0654fd..ade6374 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -684,14 +684,15 @@
          * tunneled traffic.
          *
          * @param address the local address for traffic inside the tunnel
+         * @param prefixLen length of the InetAddress prefix
          * @hide
          */
         @SystemApi
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
-        public void addAddress(@NonNull LinkAddress address) throws IOException {
+        public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
             try {
                 mService.addAddressToTunnelInterface(
-                        mResourceId, address, mOpPackageName);
+                        mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -703,14 +704,15 @@
          * <p>Remove an address which was previously added to the IpSecTunnelInterface
          *
          * @param address to be removed
+         * @param prefixLen length of the InetAddress prefix
          * @hide
          */
         @SystemApi
         @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
-        public void removeAddress(@NonNull LinkAddress address) throws IOException {
+        public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
             try {
                 mService.removeAddressFromTunnelInterface(
-                        mResourceId, address, mOpPackageName);
+                        mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index a93e25a..59380fd 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -39,6 +39,7 @@
 import android.os.strictmode.IntentReceiverLeakedViolation;
 import android.os.strictmode.LeakedClosableViolation;
 import android.os.strictmode.NetworkViolation;
+import android.os.strictmode.NonSdkApiUsedViolation;
 import android.os.strictmode.ResourceMismatchViolation;
 import android.os.strictmode.ServiceConnectionLeakedViolation;
 import android.os.strictmode.SqliteObjectLeakedViolation;
@@ -76,6 +77,7 @@
 import java.util.concurrent.Executor;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
 
 /**
  * StrictMode is a developer tool which detects things you might be doing by accident and brings
@@ -262,6 +264,9 @@
     /** @hide */
     @TestApi public static final int DETECT_VM_UNTAGGED_SOCKET = 0x80 << 24; // for VmPolicy
 
+    /** @hide */
+    @TestApi public static final int DETECT_VM_NON_SDK_API_USAGE = 0x40 << 24; // for VmPolicy
+
     private static final int ALL_VM_DETECT_BITS =
             DETECT_VM_CURSOR_LEAKS
                     | DETECT_VM_CLOSABLE_LEAKS
@@ -271,7 +276,9 @@
                     | DETECT_VM_FILE_URI_EXPOSURE
                     | DETECT_VM_CLEARTEXT_NETWORK
                     | DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION
-                    | DETECT_VM_UNTAGGED_SOCKET;
+                    | DETECT_VM_UNTAGGED_SOCKET
+                    | DETECT_VM_NON_SDK_API_USAGE;
+
 
     // Byte 3: Penalty
 
@@ -413,6 +420,13 @@
      */
     private static final AtomicInteger sDropboxCallsInFlight = new AtomicInteger(0);
 
+    /**
+     * Callback supplied to dalvik / libcore to get informed of usages of java API that are not
+     * a part of the public SDK.
+     */
+    private static final Consumer<String> sNonSdkApiUsageConsumer =
+            message -> onVmPolicyViolation(new NonSdkApiUsedViolation(message));
+
     private StrictMode() {}
 
     /**
@@ -796,6 +810,23 @@
             }
 
             /**
+             * Detect reflective usage of APIs that are not part of the public Android SDK.
+             */
+            public Builder detectNonSdkApiUsage() {
+                return enable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
+             * Permit reflective usage of APIs that are not part of the public Android SDK. Note
+             * that this <b>only</b> affects {@code StrictMode}, the underlying runtime may
+             * continue to restrict or warn on access to methods that are not part of the
+             * public SDK.
+             */
+            public Builder permitNonSdkApiUsage() {
+                return disable(DETECT_VM_NON_SDK_API_USAGE);
+            }
+
+            /**
              * Detect everything that's potentially suspect.
              *
              * <p>In the Honeycomb release this includes leaks of SQLite cursors, Activities, and
@@ -826,6 +857,8 @@
                     detectContentUriWithoutPermission();
                     detectUntaggedSockets();
                 }
+
+                // TODO: Decide whether to detect non SDK API usage beyond a certain API level.
                 return this;
             }
 
@@ -1848,6 +1881,13 @@
             } else if (networkPolicy != NETWORK_POLICY_ACCEPT) {
                 Log.w(TAG, "Dropping requested network policy due to missing service!");
             }
+
+
+            if ((sVmPolicy.mask & DETECT_VM_NON_SDK_API_USAGE) != 0) {
+                VMRuntime.setNonSdkApiUsageConsumer(sNonSdkApiUsageConsumer);
+            } else {
+                VMRuntime.setNonSdkApiUsageConsumer(null);
+            }
         }
     }
 
@@ -2576,6 +2616,8 @@
                 return DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION;
             } else if (mViolation instanceof UntaggedSocketViolation) {
                 return DETECT_VM_UNTAGGED_SOCKET;
+            } else if (mViolation instanceof NonSdkApiUsedViolation) {
+                return DETECT_VM_NON_SDK_API_USAGE;
             }
             throw new IllegalStateException("missing violation bit");
         }
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index b9dd376..d1d5d8e 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -467,7 +467,8 @@
      * <p>The list of exemptions will take affect for all new processes forked from the zygote after
      * this call.
      *
-     * @param exemptions List of hidden API exemption prefixes.
+     * @param exemptions List of hidden API exemption prefixes. Any matching members are treated as
+     *        whitelisted/public APIs (i.e. allowed, no logging of usage).
      */
     public void setApiBlacklistExemptions(List<String> exemptions) {
         synchronized (mLock) {
diff --git a/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl b/core/java/android/os/strictmode/NonSdkApiUsedViolation.java
similarity index 68%
copy from core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
copy to core/java/android/os/strictmode/NonSdkApiUsedViolation.java
index e528aa7..2f0cb50 100644
--- a/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
+++ b/core/java/android/os/strictmode/NonSdkApiUsedViolation.java
@@ -13,15 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.biometrics;
 
-import android.os.Bundle;
-import android.os.UserHandle;
+package android.os.strictmode;
 
 /**
- * Communication channel from the BiometricDialog (SysUI) back to AuthenticationClient.
- * @hide
+ * Subclass of {@code Violation} that is used when a process accesses
+ * a non SDK API.
  */
-oneway interface IBiometricDialogReceiver {
-    void onDialogDismissed(int reason);
+public final class NonSdkApiUsedViolation extends Violation {
+    /** @hide */
+    public NonSdkApiUsedViolation(String message) {
+        super(message);
+    }
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98d8666..b2a2c60 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3753,17 +3753,6 @@
                 new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
 
         /**
-         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
-         * calls when supported by the device and carrier. Boolean value.
-         * 0 = OFF
-         * 1 = ON
-         */
-        public static final String RTT_CALLING_MODE = "rtt_calling_mode";
-
-        /** @hide */
-        public static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
-
-        /**
          * Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
          * boolean (1 or 0).
          */
@@ -4088,7 +4077,6 @@
             DTMF_TONE_WHEN_DIALING,
             DTMF_TONE_TYPE_WHEN_DIALING,
             HEARING_AID,
-            RTT_CALLING_MODE,
             TTY_MODE,
             MASTER_MONO,
             SOUND_EFFECTS_ENABLED,
@@ -4287,7 +4275,6 @@
             VALIDATORS.put(DTMF_TONE_TYPE_WHEN_DIALING, DTMF_TONE_TYPE_WHEN_DIALING_VALIDATOR);
             VALIDATORS.put(HEARING_AID, HEARING_AID_VALIDATOR);
             VALIDATORS.put(TTY_MODE, TTY_MODE_VALIDATOR);
-            VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_LIGHT_PULSE, NOTIFICATION_LIGHT_PULSE_VALIDATOR);
             VALIDATORS.put(POINTER_LOCATION, POINTER_LOCATION_VALIDATOR);
             VALIDATORS.put(SHOW_TOUCHES, SHOW_TOUCHES_VALIDATOR);
@@ -6660,6 +6647,17 @@
         private static final Validator TTY_MODE_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
 
         /**
+         * User-selected RTT mode. When on, outgoing and incoming calls will be answered as RTT
+         * calls when supported by the device and carrier. Boolean value.
+         * 0 = OFF
+         * 1 = ON
+         */
+        public static final String RTT_CALLING_MODE = "rtt_calling_mode";
+
+        private static final Validator RTT_CALLING_MODE_VALIDATOR = BOOLEAN_VALIDATOR;
+
+        /**
+        /**
          * Controls whether settings backup is enabled.
          * Type: int ( 0 = disabled, 1 = enabled )
          * @hide
@@ -7885,6 +7883,7 @@
             PREFERRED_TTY_MODE,
             ENHANCED_VOICE_PRIVACY_ENABLED,
             TTY_MODE_ENABLED,
+            RTT_CALLING_MODE,
             INCALL_POWER_BUTTON_BEHAVIOR,
             NIGHT_DISPLAY_CUSTOM_START_TIME,
             NIGHT_DISPLAY_CUSTOM_END_TIME,
@@ -8014,6 +8013,7 @@
             VALIDATORS.put(ENHANCED_VOICE_PRIVACY_ENABLED,
                     ENHANCED_VOICE_PRIVACY_ENABLED_VALIDATOR);
             VALIDATORS.put(TTY_MODE_ENABLED, TTY_MODE_ENABLED_VALIDATOR);
+            VALIDATORS.put(RTT_CALLING_MODE, RTT_CALLING_MODE_VALIDATOR);
             VALIDATORS.put(INCALL_POWER_BUTTON_BEHAVIOR, INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR);
             VALIDATORS.put(NIGHT_DISPLAY_CUSTOM_START_TIME,
                     NIGHT_DISPLAY_CUSTOM_START_TIME_VALIDATOR);
@@ -12541,6 +12541,19 @@
          */
          public static final String SWAP_ENABLED = "swap_enabled";
 
+        /**
+         * Blacklist of GNSS satellites.
+         *
+         * This is a list of integers separated by commas to represent pairs of (constellation,
+         * svid). Thus, the number of integers should be even.
+         *
+         * E.g.: "3,0,5,24" denotes (constellation=3, svid=0) and (constellation=5, svid=24) are
+         * blacklisted. Note that svid=0 denotes all svids in the
+         * constellation are blacklisted.
+         *
+         * @hide
+         */
+        public static final String GNSS_SATELLITE_BLACKLIST = "gnss_satellite_blacklist";
     }
 
     /**
diff --git a/core/java/android/security/ConfirmationCallback.java b/core/java/android/security/ConfirmationCallback.java
index 4670bce..fd027f0 100644
--- a/core/java/android/security/ConfirmationCallback.java
+++ b/core/java/android/security/ConfirmationCallback.java
@@ -33,22 +33,22 @@
      *
      * @param dataThatWasConfirmed the data that was confirmed, see above for the format.
      */
-    public void onConfirmedByUser(@NonNull byte[] dataThatWasConfirmed) {}
+    public void onConfirmed(@NonNull byte[] dataThatWasConfirmed) {}
 
     /**
      * Called when the requested prompt was dismissed (not accepted) by the user.
      */
-    public void onDismissedByUser() {}
+    public void onDismissed() {}
 
     /**
      * Called when the requested prompt was dismissed by the application.
      */
-    public void onDismissedByApplication() {}
+    public void onCanceled() {}
 
     /**
      * Called when the requested prompt was dismissed because of a low-level error.
      *
-     * @param e an exception representing the error.
+     * @param e a throwable representing the error.
      */
-    public void onError(Exception e) {}
+    public void onError(Throwable e) {}
 }
diff --git a/core/java/android/security/ConfirmationDialog.java b/core/java/android/security/ConfirmationPrompt.java
similarity index 88%
rename from core/java/android/security/ConfirmationDialog.java
rename to core/java/android/security/ConfirmationPrompt.java
index 1697106..5330cff 100644
--- a/core/java/android/security/ConfirmationDialog.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -68,7 +68,7 @@
  * {@link #presentPrompt presentPrompt()} method. The <i>Relying Party</i> stores the nonce locally
  * since it'll use it in a later step.
  * <li> If the user approves the prompt a <i>Confirmation Response</i> is returned in the
- * {@link ConfirmationCallback#onConfirmedByUser onConfirmedByUser(byte[])} callback as the
+ * {@link ConfirmationCallback#onConfirmed onConfirmed(byte[])} callback as the
  * <code>dataThatWasConfirmed</code> parameter. This blob contains the text that was shown to the
  * user, the <code>extraData</code> parameter, and possibly other data.
  * <li> The application signs the <i>Confirmation Response</i> with the previously created key and
@@ -82,8 +82,8 @@
  * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
  * along the nonce in the <code>extraData</code> blob.
  */
-public class ConfirmationDialog {
-    private static final String TAG = "ConfirmationDialog";
+public class ConfirmationPrompt {
+    private static final String TAG = "ConfirmationPrompt";
 
     private CharSequence mPromptText;
     private byte[] mExtraData;
@@ -97,15 +97,15 @@
             ConfirmationCallback callback) {
         switch (responseCode) {
             case KeyStore.CONFIRMATIONUI_OK:
-                callback.onConfirmedByUser(dataThatWasConfirmed);
+                callback.onConfirmed(dataThatWasConfirmed);
                 break;
 
             case KeyStore.CONFIRMATIONUI_CANCELED:
-                callback.onDismissedByUser();
+                callback.onDismissed();
                 break;
 
             case KeyStore.CONFIRMATIONUI_ABORTED:
-                callback.onDismissedByApplication();
+                callback.onCanceled();
                 break;
 
             case KeyStore.CONFIRMATIONUI_SYSTEM_ERROR:
@@ -145,21 +145,25 @@
             };
 
     /**
-     * A builder that collects arguments, to be shown on the system-provided confirmation dialog.
+     * A builder that collects arguments, to be shown on the system-provided confirmation prompt.
      */
-    public static class Builder {
+    public static final class Builder {
 
+        private Context mContext;
         private CharSequence mPromptText;
         private byte[] mExtraData;
 
         /**
-         * Creates a builder for the confirmation dialog.
+         * Creates a builder for the confirmation prompt.
+         *
+         * @param context the application context
          */
-        public Builder() {
+        public Builder(Context context) {
+            mContext = context;
         }
 
         /**
-         * Sets the prompt text for the dialog.
+         * Sets the prompt text for the prompt.
          *
          * @param promptText the text to present in the prompt.
          * @return the builder.
@@ -170,7 +174,7 @@
         }
 
         /**
-         * Sets the extra data for the dialog.
+         * Sets the extra data for the prompt.
          *
          * @param extraData data to include in the response data.
          * @return the builder.
@@ -181,24 +185,23 @@
         }
 
         /**
-         * Creates a {@link ConfirmationDialog} with the arguments supplied to this builder.
+         * Creates a {@link ConfirmationPrompt} with the arguments supplied to this builder.
          *
-         * @param context the application context
-         * @return a {@link ConfirmationDialog}
+         * @return a {@link ConfirmationPrompt}
          * @throws IllegalArgumentException if any of the required fields are not set.
          */
-        public ConfirmationDialog build(Context context) {
+        public ConfirmationPrompt build() {
             if (TextUtils.isEmpty(mPromptText)) {
                 throw new IllegalArgumentException("prompt text must be set and non-empty");
             }
             if (mExtraData == null) {
                 throw new IllegalArgumentException("extraData must be set");
             }
-            return new ConfirmationDialog(context, mPromptText, mExtraData);
+            return new ConfirmationPrompt(mContext, mPromptText, mExtraData);
         }
     }
 
-    private ConfirmationDialog(Context context, CharSequence promptText, byte[] extraData) {
+    private ConfirmationPrompt(Context context, CharSequence promptText, byte[] extraData) {
         mContext = context;
         mPromptText = promptText;
         mExtraData = extraData;
@@ -227,10 +230,10 @@
         return uiOptionsAsFlags;
     }
 
-    private boolean isAccessibilityServiceRunning() {
+    private static boolean isAccessibilityServiceRunning(Context context) {
         boolean serviceRunning = false;
         try {
-            ContentResolver contentResolver = mContext.getContentResolver();
+            ContentResolver contentResolver = context.getContentResolver();
             int a11yEnabled = Settings.Secure.getInt(contentResolver,
                     Settings.Secure.ACCESSIBILITY_ENABLED);
             if (a11yEnabled == 1) {
@@ -249,12 +252,12 @@
      * When the prompt is no longer being presented, one of the methods in
      * {@link ConfirmationCallback} is called on the supplied callback object.
      *
-     * Confirmation dialogs may not be available when accessibility services are running so this
+     * Confirmation prompts may not be available when accessibility services are running so this
      * may fail with a {@link ConfirmationNotAvailableException} exception even if
      * {@link #isSupported} returns {@code true}.
      *
      * @param executor the executor identifying the thread that will receive the callback.
-     * @param callback the callback to use when the dialog is done showing.
+     * @param callback the callback to use when the prompt is done showing.
      * @throws IllegalArgumentException if the prompt text is too long or malfomed.
      * @throws ConfirmationAlreadyPresentingException if another prompt is being presented.
      * @throws ConfirmationNotAvailableException if confirmation prompts are not supported.
@@ -265,7 +268,7 @@
         if (mCallback != null) {
             throw new ConfirmationAlreadyPresentingException();
         }
-        if (isAccessibilityServiceRunning()) {
+        if (isAccessibilityServiceRunning(mContext)) {
             throw new ConfirmationNotAvailableException();
         }
         mCallback = callback;
@@ -301,7 +304,7 @@
      * Cancels a prompt currently being displayed.
      *
      * On success, the
-     * {@link ConfirmationCallback#onDismissedByApplication onDismissedByApplication()} method on
+     * {@link ConfirmationCallback#onCanceled onCanceled()} method on
      * the supplied callback object will be called asynchronously.
      *
      * @throws IllegalStateException if no prompt is currently being presented.
@@ -324,9 +327,13 @@
     /**
      * Checks if the device supports confirmation prompts.
      *
+     * @param context the application context.
      * @return true if confirmation prompts are supported by the device.
      */
-    public static boolean isSupported() {
+    public static boolean isSupported(Context context) {
+        if (isAccessibilityServiceRunning(context)) {
+            return false;
+        }
         return KeyStore.getInstance().isConfirmationPromptSupported();
     }
 }
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 73a6a74..9334aa9 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -118,7 +118,7 @@
      *
      * See implementation for binary key format.
      *
-     * @removed Use {@link #getTrustedHardwareCertPath} instead.
+     * @deprecated Use {@link #getTrustedHardwareCertPath} instead.
      */
     @Deprecated
     public @NonNull byte[] getTrustedHardwarePublicKey() {
@@ -227,7 +227,7 @@
          *
          * @param publicKey The public key
          * @return This builder.
-         * @removed Use {@link #setTrustedHardwareCertPath} instead.
+         * @deprecated Use {@link #setTrustedHardwareCertPath} instead.
          */
         @Deprecated
         public Builder setTrustedHardwarePublicKey(byte[] publicKey) {
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index 2c48bf4..f351c5a 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -288,7 +288,7 @@
     }
 
     /**
-     * @removed Use {@link #initRecoveryService(String, byte[], byte[])} instead.
+     * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -359,7 +359,7 @@
     }
 
     /**
-     * @removed Use {@link #getKeyChainSnapshot()}
+     * @deprecated Use {@link #getKeyChainSnapshot()}
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -435,7 +435,7 @@
     }
 
     /**
-     * @removed Use {@link #getAliases()}.
+     * @deprecated Use {@link #getAliases()}.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -460,7 +460,7 @@
     }
 
     /**
-     * @removed Use {@link #setRecoveryStatus(String, int)}
+     * @deprecated Use {@link #setRecoveryStatus(String, int)}
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -494,7 +494,7 @@
     }
 
     /**
-     * @removed Use {@link #getRecoveryStatus(String)}.
+     * @deprecated Use {@link #getRecoveryStatus(String)}.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -576,7 +576,26 @@
     }
 
     /**
-     * @removed Use {@link #generateKey(String)}.
+     * Deprecated.
+     * Generates a AES256/GCM/NoPADDING 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.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
+            throws InternalRecoveryServiceException, LockScreenRequiredException {
+        throw new UnsupportedOperationException("Operation is not supported, use generateKey");
+    }
+
+    /**
+     * @deprecated Use {@link #generateKey(String)}.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 87dc6b4..8353389 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -78,7 +78,7 @@
     }
 
     /**
-     * @removed Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -109,7 +109,7 @@
     }
 
     /**
-     * @removed Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
+     * @deprecated Use {@link #start(String, CertPath, byte[], byte[], List)} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
@@ -198,7 +198,7 @@
     }
 
     /**
-     * @removed Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
+     * @deprecated Use {@link #recoverKeyChainSnapshot(byte[], List)} instead.
      */
     @Deprecated
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index 86419d8..32952db 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -75,7 +75,7 @@
         }
 
         /**
-         * @removed AOSP does not associate keys with accounts. This may be done by system app.
+         * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
          */
         @Deprecated
         public Builder setAccount(@NonNull byte[] account) {
@@ -133,7 +133,7 @@
     }
 
     /**
-     * @removed AOSP does not associate keys with accounts. This may be done by system app.
+     * @deprecated AOSP does not associate keys with accounts. This may be done by system app.
      */
     @Deprecated
     public @NonNull byte[] getAccount() {
diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java
index 7b9af08..b461c0d 100644
--- a/core/java/android/service/textclassifier/TextClassifierService.java
+++ b/core/java/android/service/textclassifier/TextClassifierService.java
@@ -343,17 +343,17 @@
      * @param context the text classification context
      * @param sessionId the session's Id
      */
-    public abstract void onCreateTextClassificationSession(
+    public void onCreateTextClassificationSession(
             @NonNull TextClassificationContext context,
-            @NonNull TextClassificationSessionId sessionId);
+            @NonNull TextClassificationSessionId sessionId) {}
 
     /**
      * Destroys the text classification session identified by the specified sessionId.
      *
      * @param sessionId the id of the session to destroy
      */
-    public abstract void onDestroyTextClassificationSession(
-            @NonNull TextClassificationSessionId sessionId);
+    public void onDestroyTextClassificationSession(
+            @NonNull TextClassificationSessionId sessionId) {}
 
     /**
      * Returns a TextClassifier that runs in this service's process.
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 9c6a3f5..c905f49 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -100,13 +100,20 @@
      *  take an options mask. Note that this uses the
      *  {@link android.webkit.WebView#findAddress(String) findAddress()} method in
      *  {@link android.webkit.WebView} for finding addresses, which has various
-     *  limitations.
+     *  limitations and has been deprecated.
+     *  @deprecated use {@link android.view.textclassifier.TextClassifier#generateLinks(
+     *  TextLinks.Request)} instead and avoid it even when targeting API levels where no alternative
+     *  is available.
      */
+    @Deprecated
     public static final int MAP_ADDRESSES = 0x08;
 
     /**
      *  Bit mask indicating that all available patterns should be matched in
      *  methods that take an options mask
+     *  <p><strong>Note:</strong></p> {@link #MAP_ADDRESSES} is deprecated.
+     *  Use {@link android.view.textclassifier.TextClassifier#generateLinks(TextLinks.Request)}
+     *  instead and avoid it even when targeting API levels where no alternative is available.
      */
     public static final int ALL = WEB_URLS | EMAIL_ADDRESSES | PHONE_NUMBERS | MAP_ADDRESSES;
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 5eb7e9c..e03f5fa 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -190,6 +190,10 @@
      */
     public static final String DEBUG_FPS_DIVISOR = "debug.hwui.fps_divisor";
 
+    public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
+    public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
+    public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+
     static {
         // Try to check OpenGL support early if possible.
         isAvailable();
@@ -1140,6 +1144,16 @@
         nHackySetRTAnimationsEnabled(divisor <= 1);
     }
 
+    /**
+     * Changes the OpenGL context priority if IMG_context_priority extension is available. Must be
+     * called before any OpenGL context is created.
+     *
+     * @param priority The priority to use. Must be one of EGL_CONTEXT_PRIORITY_* values.
+     */
+    public static void setContextPriority(int priority) {
+        nSetContextPriority(priority);
+    }
+
     /** Not actually public - internal use only. This doc to make lint happy */
     public static native void disableVsync();
 
@@ -1213,4 +1227,5 @@
     private static native void nHackySetRTAnimationsEnabled(boolean enabled);
     private static native void nSetDebuggingEnabled(boolean enabled);
     private static native void nSetIsolatedProcess(boolean enabled);
+    private static native void nSetContextPriority(int priority);
 }
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index cca66d6..08c2d0b 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -610,6 +610,10 @@
                     ViewRootImpl root = mRoots.get(i);
                     // Client might remove the view by "stopped" event.
                     root.setWindowStopped(stopped);
+                    // Recursively forward stopped state to View's attached
+                    // to this Window rather than the root application token,
+                    // e.g. PopupWindow's.
+                    setStoppedState(root.mAttachInfo.mWindowToken, stopped);
                 }
             }
         }
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 4710671..f048d29 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -216,13 +216,17 @@
             @IntRange(from = 0) int selectionStartIndex,
             @IntRange(from = 0) int selectionEndIndex,
             @Nullable TextSelection.Options options) {
-        final TextSelection.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextSelection.Request.Builder(
-                        text, selectionStartIndex, selectionEndIndex)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .build();
-        return suggestSelection(request);
+        if (options == null) {
+            return suggestSelection(new TextSelection.Request.Builder(
+                    text, selectionStartIndex, selectionEndIndex).build());
+        } else if (options.getRequest() != null) {
+            return suggestSelection(options.getRequest());
+        } else {
+            return suggestSelection(
+                    new TextSelection.Request.Builder(text, selectionStartIndex, selectionEndIndex)
+                            .setDefaultLocales(options.getDefaultLocales())
+                            .build());
+        }
     }
 
     /**
@@ -291,14 +295,17 @@
             @IntRange(from = 0) int startIndex,
             @IntRange(from = 0) int endIndex,
             @Nullable TextClassification.Options options) {
-        final TextClassification.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextClassification.Request.Builder(
-                        text, startIndex, endIndex)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .setReferenceTime(options.getReferenceTime())
-                        .build();
-        return classifyText(request);
+        if (options == null) {
+            return classifyText(
+                    new TextClassification.Request.Builder(text, startIndex, endIndex).build());
+        } else if (options.getRequest() != null) {
+            return classifyText(options.getRequest());
+        } else {
+            return classifyText(new TextClassification.Request.Builder(text, startIndex, endIndex)
+                    .setDefaultLocales(options.getDefaultLocales())
+                    .setReferenceTime(options.getReferenceTime())
+                    .build());
+        }
     }
 
     /**
@@ -326,13 +333,16 @@
     /** @hide */
     default TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
-        final TextLinks.Request request = options.getRequest() != null
-                ? options.getRequest()
-                : new TextLinks.Request.Builder(text)
-                        .setDefaultLocales(options.getDefaultLocales())
-                        .setEntityConfig(options.getEntityConfig())
-                        .build();
-        return generateLinks(request);
+        if (options == null) {
+            return generateLinks(new TextLinks.Request.Builder(text).build());
+        } else if (options.getRequest() != null) {
+            return generateLinks(options.getRequest());
+        } else {
+            return generateLinks(new TextLinks.Request.Builder(text)
+                    .setDefaultLocales(options.getDefaultLocales())
+                    .setEntityConfig(options.getEntityConfig())
+                    .build());
+        }
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index fc94b1f..10748ac 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1837,8 +1837,12 @@
      *
      * @param addr the string to search for addresses
      * @return the address, or if no address is found, {@code null}
+     * @deprecated this method is superseded by {@link TextClassifier#generateLinks(
+     * android.view.textclassifier.TextLinks.Request)}. Avoid using this method even when targeting
+     * API levels where no alternative is available.
      */
     @Nullable
+    @Deprecated
     public static String findAddress(String addr) {
         if (addr == null) {
             throw new NullPointerException("addr is null");
@@ -2455,14 +2459,6 @@
         return mWebViewThread;
     }
 
-    /**
-     * Returns the {@link Looper} corresponding to the thread on which WebView calls must be made.
-     */
-    @NonNull
-    public Looper getLooper() {
-        return mWebViewThread;
-    }
-
     //-------------------------------------------------------------------------
     // Interface for WebView providers
     //-------------------------------------------------------------------------
diff --git a/core/java/com/android/internal/app/SuspendedAppActivity.java b/core/java/com/android/internal/app/SuspendedAppActivity.java
new file mode 100644
index 0000000..322c876
--- /dev/null
+++ b/core/java/com/android/internal/app/SuspendedAppActivity.java
@@ -0,0 +1,82 @@
+/*
+ * 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.app;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.R;
+
+public class SuspendedAppActivity extends AlertActivity
+        implements DialogInterface.OnClickListener {
+    private static final String TAG = "SuspendedAppActivity";
+
+    public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
+    public static final String EXTRA_MORE_DETAILS_INTENT =
+            "SuspendedAppActivity.extra.MORE_DETAILS_INTENT";
+
+    private Intent mMoreDetailsIntent;
+    private int mUserId;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        Window window = getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+        super.onCreate(icicle);
+
+        final Intent intent = getIntent();
+        mMoreDetailsIntent = intent.getParcelableExtra(EXTRA_MORE_DETAILS_INTENT);
+        mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
+        if (mUserId < 0) {
+            Slog.wtf(TAG, "Invalid user: " + mUserId);
+            finish();
+            return;
+        }
+        String dialogMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
+        if (dialogMessage == null) {
+            dialogMessage = getString(R.string.app_suspended_default_message);
+        }
+
+        final AlertController.AlertParams ap = mAlertParams;
+        ap.mTitle = getString(R.string.app_suspended_title);
+        ap.mMessage = String.format(getResources().getConfiguration().getLocales().get(0),
+                dialogMessage, intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
+        ap.mPositiveButtonText = getString(android.R.string.ok);
+        if (mMoreDetailsIntent != null) {
+            ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
+        }
+        ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
+        setupAlert();
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        switch (which) {
+            case AlertDialog.BUTTON_NEUTRAL:
+                startActivityAsUser(mMoreDetailsIntent, UserHandle.of(mUserId));
+                Slog.i(TAG, "Started more details activity");
+                break;
+        }
+        finish();
+    }
+}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ad5743d..2790324 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 
@@ -141,7 +141,7 @@
     void showShutdownUi(boolean isReboot, String reason);
 
     // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricDialogReceiver receiver);
+    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
     // Used to hide the dialog when a finger is authenticated
     void onFingerprintAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 0c5efe2..24f2fbf 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -20,7 +20,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -88,7 +88,7 @@
     void showPinningEscapeToast();
 
     // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IBiometricDialogReceiver receiver);
+    void showFingerprintDialog(in Bundle bundle, IBiometricPromptReceiver receiver);
     // Used to hide the dialog when a finger is authenticated
     void onFingerprintAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/widget/MessagingGroup.java b/core/java/com/android/internal/widget/MessagingGroup.java
index 239beaa..f73b607 100644
--- a/core/java/com/android/internal/widget/MessagingGroup.java
+++ b/core/java/com/android/internal/widget/MessagingGroup.java
@@ -64,7 +64,7 @@
     private boolean mIsHidingAnimated;
     private boolean mNeedsGeneratedAvatar;
     private Notification.Person mSender;
-    private boolean mAvatarsAtEnd;
+    private boolean mImagesAtEnd;
     private ViewGroup mImageContainer;
     private MessagingImageMessage mIsolatedMessage;
     private boolean mTransformingImages;
@@ -342,7 +342,7 @@
                 mAddedMessages.add(message);
             }
             boolean isImage = message instanceof MessagingImageMessage;
-            if (mAvatarsAtEnd && isImage) {
+            if (mImagesAtEnd && isImage) {
                 isolatedMessage = (MessagingImageMessage) message;
             } else {
                 if (removeFromParentIfDifferent(message, mMessageContainer)) {
@@ -474,9 +474,9 @@
         mTransformingImages = transformingImages;
     }
 
-    public void setDisplayAvatarsAtEnd(boolean atEnd) {
-        if (mAvatarsAtEnd != atEnd) {
-            mAvatarsAtEnd = atEnd;
+    public void setDisplayImagesAtEnd(boolean atEnd) {
+        if (mImagesAtEnd != atEnd) {
+            mImagesAtEnd = atEnd;
             mImageContainer.setVisibility(atEnd ? View.VISIBLE : View.GONE);
         }
     }
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 5279636..292df60 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -81,7 +81,7 @@
     private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>();
     private Notification.Person mUser;
     private CharSequence mNameReplacement;
-    private boolean mIsCollapsed;
+    private boolean mDisplayImagesAtEnd;
 
     public MessagingLayout(@NonNull Context context) {
         super(context);
@@ -128,8 +128,8 @@
     }
 
     @RemotableViewMethod
-    public void setIsCollapsed(boolean isCollapsed) {
-        mIsCollapsed = isCollapsed;
+    public void setDisplayImagesAtEnd(boolean atEnd) {
+        mDisplayImagesAtEnd = atEnd;
     }
 
     @RemotableViewMethod
@@ -337,7 +337,7 @@
                 newGroup = MessagingGroup.createGroup(mMessagingLinearLayout);
                 mAddedGroups.add(newGroup);
             }
-            newGroup.setDisplayAvatarsAtEnd(mIsCollapsed);
+            newGroup.setDisplayImagesAtEnd(mDisplayImagesAtEnd);
             newGroup.setLayoutColor(mLayoutColor);
             Notification.Person sender = senders.get(groupIndex);
             CharSequence nameOverride = null;
diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java
index bf1c5ca..a2cc7cf 100644
--- a/core/java/com/android/internal/widget/MessagingMessage.java
+++ b/core/java/com/android/internal/widget/MessagingMessage.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.app.ActivityManager;
 import android.app.Notification;
 import android.view.View;
 
@@ -33,7 +34,7 @@
 
     static MessagingMessage createMessage(MessagingLayout layout,
             Notification.MessagingStyle.Message m) {
-        if (hasImage(m)) {
+        if (hasImage(m) && !ActivityManager.isLowRamDeviceStatic()) {
             return MessagingImageMessage.createMessage(layout, m);
         } else {
             return MessagingTextMessage.createMessage(layout, m);
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 825b7a0..3ea6049 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -116,9 +116,10 @@
     const auto& info = decoder->mCodec->getInfo();
     const int width = info.width();
     const int height = info.height();
+    const bool isNinePatch = decoder->mPeeker->mPatch != nullptr;
     return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
                           reinterpret_cast<jlong>(decoder.release()), width, height,
-                          animated);
+                          animated, isNinePatch);
 }
 
 static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
@@ -332,13 +333,6 @@
         }
     }
 
-    float scaleX = 1.0f;
-    float scaleY = 1.0f;
-    if (scale) {
-        scaleX = (float) desiredWidth  / decodeInfo.width();
-        scaleY = (float) desiredHeight / decodeInfo.height();
-    }
-
     jbyteArray ninePatchChunk = nullptr;
     jobject ninePatchInsets = nullptr;
 
@@ -346,9 +340,6 @@
     if (!jpostProcess) {
         // FIXME: Share more code with BitmapFactory.cpp.
         if (decoder->mPeeker->mPatch != nullptr) {
-            if (scale) {
-                decoder->mPeeker->scale(scaleX, scaleY, desiredWidth, desiredHeight);
-            }
             size_t ninePatchArraySize = decoder->mPeeker->mPatch->serializedSize();
             ninePatchChunk = env->NewByteArray(ninePatchArraySize);
             if (ninePatchChunk == nullptr) {
@@ -408,7 +399,12 @@
 
         SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
         canvas.translate(translateX, translateY);
-        canvas.scale(scaleX, scaleY);
+        if (scale) {
+            float scaleX = (float) desiredWidth  / decodeInfo.width();
+            float scaleY = (float) desiredHeight / decodeInfo.height();
+            canvas.scale(scaleX, scaleY);
+        }
+
         canvas.drawBitmap(bm, 0.0f, 0.0f, &paint);
 
         bm.swap(scaledBm);
@@ -532,7 +528,7 @@
 
 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_constructorMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "<init>", "(JIIZZ)V");
     gImageDecoder_postProcessMethodID = GetMethodIDOrDie(env, gImageDecoder_class, "postProcessAndRelease", "(Landroid/graphics/Canvas;)I");
 
     gSize_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/util/Size"));
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 451f278..7481f1c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -992,6 +992,10 @@
     Properties::isolatedProcess = isolated;
 }
 
+static void android_view_ThreadedRenderer_setContextPriority(JNIEnv*, jclass,
+        jint contextPriority) {
+    Properties::contextPriority = contextPriority;
+}
 
 // ----------------------------------------------------------------------------
 // FrameMetricsObserver
@@ -1103,6 +1107,7 @@
             (void*)android_view_ThreadedRenderer_hackySetRTAnimationsEnabled },
     { "nSetDebuggingEnabled", "(Z)V", (void*)android_view_ThreadedRenderer_setDebuggingEnabled },
     { "nSetIsolatedProcess", "(Z)V", (void*)android_view_ThreadedRenderer_setIsolatedProcess },
+    { "nSetContextPriority", "(I)V", (void*)android_view_ThreadedRenderer_setContextPriority },
 };
 
 static JavaVM* mJvm = nullptr;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 6467976..2a7c256 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -170,7 +170,7 @@
     ];
 
     optional BatteryTypeProto battery_type = 2006 [
-        (section).type = SECTION_FILE,
+        (section).type = SECTION_NONE, // disabled since the path is device specific!
         (section).args = "/sys/class/power_supply/bms/battery_type"
     ];
 
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index b5303c8..05686a0 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -451,6 +451,7 @@
         // If set to 1, {@link Secure#LOCATION_MODE} will be set to {@link
         // Secure#LOCATION_MODE_OFF} temporarily for all users.
         optional SettingProto global_kill_switch = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gnss_satellite_blacklist = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Location location = 69;
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 593747d..3de8c39 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -340,6 +340,11 @@
     }
     optional Rotation rotation = 46;
 
+    // User-selected RTT mode. When on, outgoing and incoming calls will be
+    // answered as RTT calls when supported by the device and carrier. Boolean
+    // value.
+    optional SettingProto rtt_calling_mode = 69 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
     message Screensaver {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
 
@@ -474,5 +479,5 @@
 
     // Please insert fields in alphabetical order and group them into messages
     // if possible (to avoid reaching the method limit).
-    // Next tag = 69;
+    // Next tag = 70;
 }
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index 6b6edd2..dfc4081 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -151,10 +151,7 @@
     }
     optional Rotation rotation = 20;
 
-    // User-selected RTT mode. When on, outgoing and incoming calls will be
-    // answered as RTT calls when supported by the device and carrier. Boolean
-    // value.
-    optional SettingProto rtt_calling_mode = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    reserved 21; // rtt_calling_mode moved to Settings.Secure
 
     message Screen {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto
index cccd2fe..bcd7f29 100644
--- a/core/proto/android/service/notification.proto
+++ b/core/proto/android/service/notification.proto
@@ -65,7 +65,7 @@
     optional bool can_vibrate = 7;
     optional bool can_show_light = 8;
     optional string group_key = 9 [ (.android.privacy).dest = DEST_EXPLICIT ];
-    optional int32 importance = 10;
+    optional sint32 importance = 10;
 }
 
 message ListenersDisablingEffectsProto {
@@ -122,11 +122,11 @@
         // Default value is UNKNOWN_UID = USER_NULL = -10000.
         optional int32 uid = 2;
         // Default is IMPORTANCE_UNSPECIFIED (-1000).
-        optional int32 importance = 3;
+        optional sint32 importance = 3;
         // Default is PRIORITY_DEFAULT (0).
         optional int32 priority = 4;
         // Default is VISIBILITY_NO_OVERRIDE (-1000).
-        optional int32 visibility = 5;
+        optional sint32 visibility = 5;
         // Default is true.
         optional bool show_badge = 6;
         repeated android.app.NotificationChannelProto channels = 7;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b7b5f23..da15506 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2030,6 +2030,15 @@
     <permission android:name="android.permission.START_ANY_ACTIVITY"
         android:protectionLevel="signature" />
 
+    <!-- @SystemApi Must be required by activities that handle the intent action
+         {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
+         hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"
+                android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS" />
+
     <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
         API is no longer supported. -->
     <permission android:name="android.permission.RESTART_PACKAGES"
@@ -4116,6 +4125,12 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="com.android.internal.app.SuspendedAppActivity"
+                  android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+                  android:excludeFromRecents="true"
+                  android:process=":ui">
+        </activity>
+
         <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
                 android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
                 android:excludeFromRecents="true"
diff --git a/core/res/res/anim/activity_translucent_close_exit.xml b/core/res/res/anim/activity_translucent_close_exit.xml
index 04c5dea..02df0ff 100644
--- a/core/res/res/anim/activity_translucent_close_exit.xml
+++ b/core/res/res/anim/activity_translucent_close_exit.xml
@@ -20,6 +20,6 @@
     <translate
         android:fromYDelta="0%"
         android:toYDelta="100%"
-        android:interpolator="@interpolator/fast_out_linear_in"
-        android:duration="300"/>
+        android:interpolator="@interpolator/accelerate_quad"
+        android:duration="250"/>
 </set>
diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml
index 265eaaf..351bd81 100644
--- a/core/res/res/values-land/dimens.xml
+++ b/core/res/res/values-land/dimens.xml
@@ -29,7 +29,10 @@
 
     <!-- Height of the status bar -->
     <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen>
-
+    <!-- Height of area above QQS where battery/time go -->
+    <dimen name="quick_qs_offset_height">@dimen/status_bar_height_landscape</dimen>
+    <!-- Total height of QQS in landscape, this is effectively status_bar_height_landscape + 128 -->
+    <dimen name="quick_qs_total_height">152dp</dimen>
     <!-- Default height of an action bar. -->
     <dimen name="action_bar_default_height">40dip</dimen>
     <!-- Vertical padding around action bar icons. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3cd80f2..12e5dfe 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2249,6 +2249,10 @@
          mirror the content of the default display. -->
     <bool name="config_localDisplaysMirrorContent">true</bool>
 
+    <!-- Indicates whether local non-default displays are private.
+         {@see android.view.Display#FLAG_PRIVATE} -->
+    <bool name="config_localDisplaysPrivate">false</bool>
+
     <!-- The default mode for the default display. One of the following values (See Display.java):
              0 - COLOR_MODE_DEFAULT
              7 - COLOR_MODE_SRGB
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 17b9d28..715be5b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4703,6 +4703,13 @@
     <!-- Menu item in the locale menu  [CHAR LIMIT=30] -->
     <string name="locale_search_menu">Search</string>
 
+    <!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=30] -->
+    <string name="app_suspended_title">Action not allowed</string>
+    <!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
+    <string name="app_suspended_default_message">The application <xliff:g id="app_name" example="GMail">%1$s</xliff:g> is currently disabled.</string>
+    <!-- Title of the button to show users more details about why the app has been suspended [CHAR LIMIT=50]-->
+    <string name="app_suspended_more_details">More details</string>
+
     <!-- Title of a dialog. The string is asking if the user wants to turn on their work profile, which contains work apps that are managed by their employer. "Work" is an adjective. [CHAR LIMIT=30] -->
     <string name="work_mode_off_title">Turn on work profile?</string>
     <!-- Text in a dialog. This string describes what will happen if a user decides to turn on their work profile. "Work profile" is used as an adjective. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 78a7286..5856648 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -352,6 +352,7 @@
   <java-symbol type="bool" name="config_noHomeScreen" />
   <java-symbol type="bool" name="config_guestUserEphemeral" />
   <java-symbol type="bool" name="config_localDisplaysMirrorContent" />
+  <java-symbol type="bool" name="config_localDisplaysPrivate" />
   <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" />
   <java-symbol type="bool" name="config_enableAppWidgetService" />
   <java-symbol type="string" name="config_defaultPictureInPictureScreenEdgeInsets" />
@@ -1670,6 +1671,7 @@
   <java-symbol type="dimen" name="navigation_bar_height_landscape_car_mode" />
   <java-symbol type="dimen" name="navigation_bar_width_car_mode" />
   <java-symbol type="dimen" name="status_bar_height" />
+  <java-symbol type="dimen" name="quick_qs_offset_height" />
   <java-symbol type="dimen" name="quick_qs_total_height" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_off" />
   <java-symbol type="drawable" name="ic_jog_dial_sound_on" />
@@ -2876,6 +2878,10 @@
 
   <java-symbol type="string" name="suspended_widget_accessibility" />
 
+  <java-symbol type="string" name="app_suspended_title" />
+  <java-symbol type="string" name="app_suspended_more_details" />
+  <java-symbol type="string" name="app_suspended_default_message" />
+
   <!-- Used internally for assistant to launch activity transitions -->
   <java-symbol type="id" name="cross_task_transition" />
 
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 002c9e2..b1936b9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -239,6 +239,7 @@
                     Settings.Global.GLOBAL_HTTP_PROXY_HOST,
                     Settings.Global.GLOBAL_HTTP_PROXY_PAC,
                     Settings.Global.GLOBAL_HTTP_PROXY_PORT,
+                    Settings.Global.GNSS_SATELLITE_BLACKLIST,
                     Settings.Global.GPRS_REGISTER_CHECK_PERIOD_MS,
                     Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED,
                     Settings.Global.HDMI_CONTROL_AUTO_WAKEUP_ENABLED,
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index 95aff9a..4e09c69 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -17,66 +17,28 @@
 
 <!--
 This XML file declares which system apps should be exempted from the hidden API blacklisting, i.e.
-which apps should be allowed to access the entire private API.
+which apps should be allowed to access the entire private API. Only apps NOT signed with the
+platform cert need to be included, as apps signed with the platform cert are exempted by default.
 -->
 
 <config>
-  <hidden-api-whitelisted-app package="android.car.cluster.loggingrenderer" />
-  <hidden-api-whitelisted-app package="android.car.input.service" />
-  <hidden-api-whitelisted-app package="android.car.usb.handler" />
   <hidden-api-whitelisted-app package="android.ext.services" />
   <hidden-api-whitelisted-app package="com.android.apps.tag" />
-  <hidden-api-whitelisted-app package="com.android.backupconfirm" />
   <hidden-api-whitelisted-app package="com.android.basicsmsreceiver" />
-  <hidden-api-whitelisted-app package="com.android.bluetooth" />
-  <hidden-api-whitelisted-app package="com.android.bluetoothdebug" />
-  <hidden-api-whitelisted-app package="com.android.bluetoothmidiservice" />
   <hidden-api-whitelisted-app package="com.android.bookmarkprovider" />
   <hidden-api-whitelisted-app package="com.android.calllogbackup" />
   <hidden-api-whitelisted-app package="com.android.camera" />
-  <hidden-api-whitelisted-app package="com.android.captiveportallogin" />
-  <hidden-api-whitelisted-app package="com.android.car" />
   <hidden-api-whitelisted-app package="com.android.car.dialer" />
-  <hidden-api-whitelisted-app package="com.android.car.hvac" />
-  <hidden-api-whitelisted-app package="com.android.car.mapsplaceholder" />
-  <hidden-api-whitelisted-app package="com.android.car.media" />
-  <hidden-api-whitelisted-app package="com.android.car.media.localmediaplayer" />
   <hidden-api-whitelisted-app package="com.android.car.messenger" />
   <hidden-api-whitelisted-app package="com.android.car.overview" />
-  <hidden-api-whitelisted-app package="com.android.car.radio" />
-  <hidden-api-whitelisted-app package="com.android.car.settings" />
   <hidden-api-whitelisted-app package="com.android.car.stream" />
-  <hidden-api-whitelisted-app package="com.android.car.systemupdater" />
-  <hidden-api-whitelisted-app package="com.android.car.trust" />
-  <hidden-api-whitelisted-app package="com.android.carrierconfig" />
-  <hidden-api-whitelisted-app package="com.android.carrierdefaultapp" />
-  <hidden-api-whitelisted-app package="com.android.cellbroadcastreceiver" />
-  <hidden-api-whitelisted-app package="com.android.certinstaller" />
   <hidden-api-whitelisted-app package="com.android.companiondevicemanager" />
-  <hidden-api-whitelisted-app package="com.android.customlocale2" />
-  <hidden-api-whitelisted-app package="com.android.defcontainer" />
-  <hidden-api-whitelisted-app package="com.android.development" />
-  <hidden-api-whitelisted-app package="com.android.documentsui" />
   <hidden-api-whitelisted-app package="com.android.dreams.basic" />
-  <hidden-api-whitelisted-app package="com.android.egg" />
-  <hidden-api-whitelisted-app package="com.android.emergency" />
-  <hidden-api-whitelisted-app package="com.android.externalstorage" />
-  <hidden-api-whitelisted-app package="com.android.fakeoemfeatures" />
   <hidden-api-whitelisted-app package="com.android.gallery" />
-  <hidden-api-whitelisted-app package="com.android.hotspot2" />
-  <hidden-api-whitelisted-app package="com.android.keychain" />
   <hidden-api-whitelisted-app package="com.android.launcher3" />
-  <hidden-api-whitelisted-app package="com.android.location.fused" />
-  <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
-  <hidden-api-whitelisted-app package="com.android.mms.service" />
   <hidden-api-whitelisted-app package="com.android.mtp" />
   <hidden-api-whitelisted-app package="com.android.musicfx" />
-  <hidden-api-whitelisted-app package="com.android.nfc" />
-  <hidden-api-whitelisted-app package="com.android.osu" />
   <hidden-api-whitelisted-app package="com.android.packageinstaller" />
-  <hidden-api-whitelisted-app package="com.android.pacprocessor" />
-  <hidden-api-whitelisted-app package="com.android.phone" />
-  <hidden-api-whitelisted-app package="com.android.pmc" />
   <hidden-api-whitelisted-app package="com.android.printservice.recommendation" />
   <hidden-api-whitelisted-app package="com.android.printspooler" />
   <hidden-api-whitelisted-app package="com.android.providers.blockednumber" />
@@ -85,36 +47,13 @@
   <hidden-api-whitelisted-app package="com.android.providers.downloads" />
   <hidden-api-whitelisted-app package="com.android.providers.downloads.ui" />
   <hidden-api-whitelisted-app package="com.android.providers.media" />
-  <hidden-api-whitelisted-app package="com.android.providers.settings" />
-  <hidden-api-whitelisted-app package="com.android.providers.telephony" />
   <hidden-api-whitelisted-app package="com.android.providers.tv" />
   <hidden-api-whitelisted-app package="com.android.providers.userdictionary" />
-  <hidden-api-whitelisted-app package="com.android.provision" />
-  <hidden-api-whitelisted-app package="com.android.proxyhandler" />
-  <hidden-api-whitelisted-app package="com.android.sdksetup" />
-  <hidden-api-whitelisted-app package="com.android.se" />
-  <hidden-api-whitelisted-app package="com.android.server.telecom" />
-  <hidden-api-whitelisted-app package="com.android.service.ims" />
-  <hidden-api-whitelisted-app package="com.android.service.ims.presence" />
-  <hidden-api-whitelisted-app package="com.android.settings" />
-  <hidden-api-whitelisted-app package="com.android.sharedstoragebackup" />
-  <hidden-api-whitelisted-app package="com.android.shell" />
   <hidden-api-whitelisted-app package="com.android.smspush" />
   <hidden-api-whitelisted-app package="com.android.spare_parts" />
   <hidden-api-whitelisted-app package="com.android.statementservice" />
-  <hidden-api-whitelisted-app package="com.android.stk" />
   <hidden-api-whitelisted-app package="com.android.storagemanager" />
-  <hidden-api-whitelisted-app package="com.android.support.car.lenspicker" />
-  <hidden-api-whitelisted-app package="com.android.systemui" />
   <hidden-api-whitelisted-app package="com.android.systemui.plugins" />
   <hidden-api-whitelisted-app package="com.android.terminal" />
-  <hidden-api-whitelisted-app package="com.android.timezone.updater" />
-  <hidden-api-whitelisted-app package="com.android.traceur" />
-  <hidden-api-whitelisted-app package="com.android.tv.settings" />
-  <hidden-api-whitelisted-app package="com.android.vpndialogs" />
-  <hidden-api-whitelisted-app package="com.android.wallpaper.livepicker" />
-  <hidden-api-whitelisted-app package="com.android.wallpaperbackup" />
-  <hidden-api-whitelisted-app package="com.android.wallpapercropper" />
-  <hidden-api-whitelisted-app package="com.googlecode.android_scripting" />
   <hidden-api-whitelisted-app package="jp.co.omronsoft.openwnn" />
 </config>
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 31abf92..098f100 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -23,8 +23,10 @@
 
 import android.annotation.AnyThread;
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.Px;
 import android.annotation.TestApi;
 import android.annotation.WorkerThread;
 import android.content.ContentResolver;
@@ -579,7 +581,7 @@
     /** @removed
      * @deprecated Subsumed by {@link #DecodeException}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public static class IncompleteException extends IOException {};
 
     /**
@@ -611,19 +613,19 @@
     /** @removed
      * @deprecated Replaced by {@link #DecodeException#SOURCE_EXCEPTION}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public static final int ERROR_SOURCE_EXCEPTION  = 1;
 
     /** @removed
      * @deprecated Replaced by {@link #DecodeException#SOURCE_INCOMPLETE}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public static final int ERROR_SOURCE_INCOMPLETE = 2;
 
     /** @removed
      * @deprecated Replaced by {@link #DecodeException#SOURCE_MALFORMED_DATA}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public static final int ERROR_SOURCE_ERROR      = 3;
 
     /**
@@ -746,6 +748,7 @@
     private final int     mWidth;
     private final int     mHeight;
     private final boolean mAnimated;
+    private final boolean mIsNinePatch;
 
     private int        mDesiredWidth;
     private int        mDesiredHeight;
@@ -776,13 +779,14 @@
      */
     @SuppressWarnings("unused")
     private ImageDecoder(long nativePtr, int width, int height,
-            boolean animated) {
+            boolean animated, boolean isNinePatch) {
         mNativePtr = nativePtr;
         mWidth = width;
         mHeight = height;
         mDesiredWidth = width;
         mDesiredHeight = height;
         mAnimated = animated;
+        mIsNinePatch = isNinePatch;
         mCloseGuard.open("close");
     }
 
@@ -998,7 +1002,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setTargetSize}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setResize(int width, int height) {
         this.setTargetSize(width, height);
         return this;
@@ -1020,10 +1024,11 @@
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      *
-     *  @param width must be greater than 0.
-     *  @param height must be greater than 0.
+     *  @param width width in pixels of the output, must be greater than 0
+     *  @param height height in pixels of the output, must be greater than 0
      */
-    public void setTargetSize(int width, int height) {
+    public void setTargetSize(@Px @IntRange(from = 1) int width,
+                              @Px @IntRange(from = 1) int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -1036,7 +1041,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setTargetSampleSize}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setResize(int sampleSize) {
         this.setTargetSampleSize(sampleSize);
         return this;
@@ -1083,14 +1088,20 @@
      *
      *  <p>Must be greater than or equal to 1.</p>
      *
+     *  <p>This has the same effect as calling {@link #setTargetSize} with
+     *  dimensions based on the {@code sampleSize}. Unlike dividing the original
+     *  width and height by the {@code sampleSize} manually, calling this method
+     *  allows {@code ImageDecoder} to round in the direction that it can do most
+     *  efficiently.</p>
+     *
      *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      *
-     *  @param sampleSize Sampling rate of the encoded image.
+     *  @param sampleSize sampling rate of the encoded image.
      */
-    public void setTargetSampleSize(int sampleSize) {
+    public void setTargetSampleSize(@IntRange(from = 1) int sampleSize) {
         Size size = this.getSampledSize(sampleSize);
         int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
         int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
@@ -1116,7 +1127,7 @@
     /**
      *  Use a software allocation for the pixel memory.
      *
-     *  Useful for drawing to a software {@link Canvas} or for
+     *  <p>Useful for drawing to a software {@link Canvas} or for
      *  accessing the pixels on the final output.
      */
     public static final int ALLOCATOR_SOFTWARE = 1;
@@ -1124,14 +1135,14 @@
     /**
      *  Use shared memory for the pixel memory.
      *
-     *  Useful for sharing across processes.
+     *  <p>Useful for sharing across processes.
      */
     public static final int ALLOCATOR_SHARED_MEMORY = 2;
 
     /**
      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
      *
-     *  When this is combined with incompatible options, like
+     *  <p>When this is combined with incompatible options, like
      *  {@link #setMutableRequired setMutableRequired(true)} or
      *  {@link #setDecodeAsAlphaMaskEnabled setDecodeAsAlphaMaskEnabled(true)},
      *  {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap decodeBitmap}
@@ -1194,7 +1205,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
         this.setUnpremultipliedRequired(unpremultipliedRequired);
         return this;
@@ -1210,7 +1221,7 @@
     /** @removed
      * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public boolean getRequireUnpremultiplied() {
         return this.isUnpremultipliedRequired();
     }
@@ -1339,7 +1350,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setMutableRequired}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setMutable(boolean mutable) {
         this.setMutableRequired(mutable);
         return this;
@@ -1355,37 +1366,68 @@
     /** @removed
      * @deprecated Renamed to {@link #isMutableRequired}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public boolean getMutable() {
         return this.isMutableRequired();
     }
 
     /**
-     *  Specify whether to potentially save RAM at the expense of quality.
+     * Save memory if possible by using a denser {@link Bitmap.Config} at the
+     * cost of some image quality.
      *
-     *  <p>Setting this to {@code true} may result in a {@link Bitmap} with a
-     *  denser {@link Bitmap.Config}, depending on the image. For example, an
-     *  opaque {@link Bitmap} with 8 bits or precision for each of its red,
-     *  green and blue components would decode to
-     *  {@link Bitmap.Config#ARGB_8888} by default, but setting this to
-     *  {@code true} will result in decoding to {@link Bitmap.Config#RGB_565}.
-     *  This necessarily lowers the quality of the output, but saves half
-     *  the memory used.</p>
+     * <p>For example an opaque 8-bit image may be compressed into an
+     * {@link Bitmap.Config#RGB_565} configuration, sacrificing image
+     * quality to save memory.
+     */
+    public static final int MEMORY_POLICY_LOW_RAM = 0;
+
+    /**
+     * Use the most natural {@link Bitmap.Config} for the internal {@link Bitmap}.
+     *
+     * <p>This is the recommended default for most applications and usages. This
+     * will use the closest {@link Bitmap.Config} for the encoded source. If the
+     * encoded source does not exactly match any {@link Bitmap.Config}, the next
+     * highest quality {@link Bitmap.Config} will be used avoiding any loss in
+     * image quality.
+     */
+    public static final int MEMORY_POLICY_DEFAULT  = 1;
+
+    /** @hide **/
+    @Retention(SOURCE)
+    @IntDef(value = { MEMORY_POLICY_DEFAULT, MEMORY_POLICY_LOW_RAM },
+              prefix = {"MEMORY_POLICY_"})
+    public @interface MemoryPolicy {};
+
+    /**
+     *  Specify the memory policy for the decoded {@link Bitmap}.
      *
      *  <p>Like all setters on ImageDecoder, this must be called inside
      *  {@link OnHeaderDecodedListener#onHeaderDecoded onHeaderDecoded}.</p>
      */
+    public void setMemorySizePolicy(@MemoryPolicy int policy) {
+        mConserveMemory = (policy == MEMORY_POLICY_LOW_RAM);
+    }
+
+    /**
+     *  Retrieve the memory policy for the decoded {@link Bitmap}.
+     */
+    @MemoryPolicy
+    public int getMemorySizePolicy() {
+        return mConserveMemory ? MEMORY_POLICY_LOW_RAM : MEMORY_POLICY_DEFAULT;
+    }
+
+    /** @removed
+     * @deprecated Replaced by {@link #setMemorySizePolicy}.
+     */
+    @Deprecated
     public void setConserveMemory(boolean conserveMemory) {
         mConserveMemory = conserveMemory;
     }
 
-    /**
-     *  Return whether this object will try to save RAM at the expense of quality.
-     *
-     *  <p>This returns whether {@link #setConserveMemory} was set to {@code true}.
-     *  It may still return {@code true} even if the {@code ImageDecoder} does not
-     *  have a way to save RAM at the expense of quality for this image.</p>
+    /** @removed
+     * @deprecated Replaced by {@link #getMemorySizePolicy}.
      */
+    @Deprecated
     public boolean getConserveMemory() {
         return mConserveMemory;
     }
@@ -1412,7 +1454,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
         this.setDecodeAsAlphaMaskEnabled(enabled);
         return this;
@@ -1421,7 +1463,7 @@
     /** @removed
      * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
         this.setDecodeAsAlphaMask(asAlphaMask);
         return this;
@@ -1442,7 +1484,7 @@
     /** @removed
      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public boolean getDecodeAsAlphaMask() {
         return mDecodeAsAlphaMask;
     }
@@ -1450,7 +1492,7 @@
     /** @removed
      * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
      */
-    @java.lang.Deprecated
+    @Deprecated
     public boolean getAsAlphaMask() {
         return this.getDecodeAsAlphaMask();
     }
@@ -1625,7 +1667,7 @@
 
             // this call potentially manipulates the decoder so it must be performed prior to
             // decoding the bitmap and after decode set the density on the resulting bitmap
-            final int srcDensity = computeDensity(src, decoder);
+            final int srcDensity = decoder.computeDensity(src);
             if (decoder.mAnimated) {
                 // AnimatedImageDrawable calls postProcessAndRelease only if
                 // mPostProcessor exists.
@@ -1715,7 +1757,7 @@
 
             // this call potentially manipulates the decoder so it must be performed prior to
             // decoding the bitmap
-            final int srcDensity = computeDensity(src, decoder);
+            final int srcDensity = decoder.computeDensity(src);
             Bitmap bm = decoder.decodeBitmapInternal();
             bm.setDensity(srcDensity);
 
@@ -1732,12 +1774,26 @@
     }
 
     // This method may modify the decoder so it must be called prior to performing the decode
-    private static int computeDensity(@NonNull Source src, @NonNull ImageDecoder decoder) {
+    private int computeDensity(@NonNull Source src) {
         // if the caller changed the size then we treat the density as unknown
-        if (decoder.requestedResize()) {
+        if (this.requestedResize()) {
             return Bitmap.DENSITY_NONE;
         }
 
+        final int srcDensity = src.getDensity();
+        if (srcDensity == Bitmap.DENSITY_NONE) {
+            return srcDensity;
+        }
+
+        // Scaling up nine-patch divs is imprecise and is better handled
+        // at draw time. An app won't be relying on the internal Bitmap's
+        // size, so it is safe to let NinePatchDrawable handle scaling.
+        // mPostProcessor disables nine-patching, so behave normally if
+        // it is present.
+        if (mIsNinePatch && mPostProcessor == null) {
+            return srcDensity;
+        }
+
         // Special stuff for compatibility mode: if the target density is not
         // the same as the display density, but the resource -is- the same as
         // the display density, then don't scale it down to the target density.
@@ -1746,23 +1802,25 @@
         // to the compatibility density only to have them scaled back up when
         // drawn to the screen.
         Resources res = src.getResources();
-        final int srcDensity = src.getDensity();
         if (res != null && res.getDisplayMetrics().noncompatDensityDpi == srcDensity) {
             return srcDensity;
         }
 
+        final int dstDensity = src.computeDstDensity();
+        if (srcDensity == dstDensity) {
+            return srcDensity;
+        }
+
         // For P and above, only resize if it would be a downscale. Scale up prior
         // to P in case the app relies on the Bitmap's size without considering density.
-        final int dstDensity = src.computeDstDensity();
-        if (srcDensity == Bitmap.DENSITY_NONE || srcDensity == dstDensity
-                || (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P)) {
+        if (srcDensity < dstDensity && sApiLevel >= Build.VERSION_CODES.P) {
             return srcDensity;
         }
 
         float scale = (float) dstDensity / srcDensity;
-        int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
-        int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-        decoder.setTargetSize(scaledWidth, scaledHeight);
+        int scaledWidth = (int) (mWidth * scale + 0.5f);
+        int scaledHeight = (int) (mHeight * scale + 0.5f);
+        this.setTargetSize(scaledWidth, scaledHeight);
         return dstDensity;
     }
 
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 1602b4b..0a6c45b 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -65,6 +65,8 @@
 bool Properties::debuggingEnabled = false;
 bool Properties::isolatedProcess = false;
 
+int Properties::contextPriority = 0;
+
 static int property_get_int(const char* key, int defaultValue) {
     char buf[PROPERTY_VALUE_MAX] = {
             '\0',
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 81a3657..764c502 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -271,6 +271,8 @@
     ANDROID_API static bool debuggingEnabled;
     ANDROID_API static bool isolatedProcess;
 
+    ANDROID_API static int contextPriority;
+
 private:
     static ProfileType sProfileType;
     static bool sDisableProfileBars;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 5b87e10..6e239e3 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -82,6 +82,7 @@
     bool pixelFormatFloat = false;
     bool glColorSpace = false;
     bool scRGB = false;
+    bool contextPriority = false;
 } EglExtensions;
 
 EglManager::EglManager(RenderThread& thread)
@@ -168,6 +169,7 @@
 #else
     EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb");
 #endif
+    EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
 }
 
 bool EglManager::hasEglContext() {
@@ -247,10 +249,18 @@
 }
 
 void EglManager::createContext() {
-    EGLint attribs[] = {EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE};
+    std::vector<EGLint> contextAttributes;
+    contextAttributes.reserve(5);
+    contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+    contextAttributes.push_back(GLES_VERSION);
+    if (Properties::contextPriority != 0 && EglExtensions.contextPriority) {
+        contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+        contextAttributes.push_back(Properties::contextPriority);
+    }
+    contextAttributes.push_back(EGL_NONE);
     mEglContext = eglCreateContext(
             mEglDisplay, EglExtensions.noConfigContext ? ((EGLConfig) nullptr) : mEglConfig,
-            EGL_NO_CONTEXT, attribs);
+            EGL_NO_CONTEXT, contextAttributes.data());
     LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, "Failed to create context, error = %s",
                         eglErrorString());
 }
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index 4f1d2d5..7ad83ca 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -37,3 +37,20 @@
         "liblog",
     ],
 }
+
+cc_test {
+    name: "libprotoutil_test",
+
+    srcs: [
+        "tests/EncodedBuffer_test.cpp",
+    ],
+
+    shared_libs: [
+        "libcutils",
+        "libprotoutil",
+    ],
+
+    static_libs: [
+        "libgmock",
+    ],
+}
diff --git a/libs/protoutil/AndroidTest.xml b/libs/protoutil/AndroidTest.xml
new file mode 100644
index 0000000..46d418e
--- /dev/null
+++ b/libs/protoutil/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<configuration description="Config for libprotoutil_test">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="libprotoutil_test->/data/nativetest/libprotoutil_test" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/nativetest" />
+        <option name="module-name" value="libprotoutil_test" />
+    </test>
+</configuration>
diff --git a/libs/protoutil/src/EncodedBuffer.cpp b/libs/protoutil/src/EncodedBuffer.cpp
index 3a5e2e9..c017851 100644
--- a/libs/protoutil/src/EncodedBuffer.cpp
+++ b/libs/protoutil/src/EncodedBuffer.cpp
@@ -13,11 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#define LOG_TAG "libprotoutil"
+
+#include <stdlib.h>
 
 #include <android/util/EncodedBuffer.h>
 #include <android/util/protobuf.h>
-
-#include <stdlib.h>
+#include <cutils/log.h>
 
 namespace android {
 namespace util {
@@ -228,7 +230,7 @@
     size_t start = mEp.pos();
     while (true) {
         uint8_t byte = readRawByte();
-        val += (byte & 0x7F) << shift;
+        val |= (UINT64_C(0x7F) & byte) << shift;
         if ((byte & 0x80) == 0) break;
         shift += 7;
     }
@@ -345,7 +347,7 @@
     uint64_t val = 0, shift = 0;
     while (true) {
         uint8_t byte = next();
-        val += (byte & 0x7F) << shift;
+        val |= (INT64_C(0x7F) & byte) << shift;
         if ((byte & 0x80) == 0) break;
         shift += 7;
     }
diff --git a/libs/protoutil/tests/EncodedBuffer_test.cpp b/libs/protoutil/tests/EncodedBuffer_test.cpp
new file mode 100644
index 0000000..615ab4a
--- /dev/null
+++ b/libs/protoutil/tests/EncodedBuffer_test.cpp
@@ -0,0 +1,25 @@
+// 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.
+#include <android/util/EncodedBuffer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using namespace android::util;
+
+TEST(EncodedBufferTest, ReadVarint) {
+    EncodedBuffer buffer;
+    uint64_t val = UINT64_C(1522865904593);
+    buffer.writeRawVarint64(val);
+    EXPECT_EQ(val, buffer.begin().readRawVarint());
+}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index bc0e43b..7888436 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -66,7 +66,7 @@
 /**
  * This is a class for reading and writing Exif tags in a JPEG file or a RAW image file.
  * <p>
- * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW and RAF.
+ * Supported formats are: JPEG, DNG, CR2, NEF, NRW, ARW, RW2, ORF, PEF, SRW, RAF and HEIF.
  * <p>
  * Attribute mutation is supported for JPEG image files.
  */
@@ -2524,46 +2524,46 @@
     private void getHeifAttributes(ByteOrderedDataInputStream in) throws IOException {
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
-            if (mSeekableFileDescriptor != null) {
-                retriever.setDataSource(mSeekableFileDescriptor);
-            } else {
-                retriever.setDataSource(new MediaDataSource() {
-                    long mPosition;
+            retriever.setDataSource(new MediaDataSource() {
+                long mPosition;
 
-                    @Override
-                    public void close() throws IOException {}
+                @Override
+                public void close() throws IOException {}
 
-                    @Override
-                    public int readAt(long position, byte[] buffer, int offset, int size)
-                            throws IOException {
-                        if (size == 0) {
-                            return 0;
-                        }
-                        if (position < 0) {
-                            return -1;
-                        }
-                        if (mPosition != position) {
-                            in.seek(position);
-                            mPosition = position;
-                        }
-
-                        int bytesRead = in.read(buffer, offset, size);
-                        if (bytesRead < 0) {
-                            mPosition = -1; // need to seek on next read
-                            return -1;
-                        }
-
-                        mPosition += bytesRead;
-                        return bytesRead;
+                @Override
+                public int readAt(long position, byte[] buffer, int offset, int size)
+                        throws IOException {
+                    if (size == 0) {
+                        return 0;
                     }
-
-                    @Override
-                    public long getSize() throws IOException {
+                    if (position < 0) {
                         return -1;
                     }
-                });
-            }
+                    if (mPosition != position) {
+                        in.seek(position);
+                        mPosition = position;
+                    }
 
+                    int bytesRead = in.read(buffer, offset, size);
+                    if (bytesRead < 0) {
+                        mPosition = -1; // need to seek on next read
+                        return -1;
+                    }
+
+                    mPosition += bytesRead;
+                    return bytesRead;
+                }
+
+                @Override
+                public long getSize() throws IOException {
+                    return -1;
+                }
+            });
+
+            String exifOffsetStr = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_EXIF_OFFSET);
+            String exifLengthStr = retriever.extractMetadata(
+                    MediaMetadataRetriever.METADATA_KEY_EXIF_LENGTH);
             String hasImage = retriever.extractMetadata(
                     MediaMetadataRetriever.METADATA_KEY_HAS_IMAGE);
             String hasVideo = retriever.extractMetadata(
@@ -2622,6 +2622,30 @@
                         ExifAttribute.createUShort(orientation, mExifByteOrder));
             }
 
+            if (exifOffsetStr != null && exifLengthStr != null) {
+                int offset = Integer.parseInt(exifOffsetStr);
+                int length = Integer.parseInt(exifLengthStr);
+                if (length <= 6) {
+                    throw new IOException("Invalid exif length");
+                }
+                in.seek(offset);
+                byte[] identifier = new byte[6];
+                if (in.read(identifier) != 6) {
+                    throw new IOException("Can't read identifier");
+                }
+                offset += 6;
+                length -= 6;
+                if (!Arrays.equals(identifier, IDENTIFIER_EXIF_APP1)) {
+                    throw new IOException("Invalid identifier");
+                }
+
+                byte[] bytes = new byte[length];
+                if (in.read(bytes) != length) {
+                    throw new IOException("Can't read exif");
+                }
+                readExifSegment(bytes, IFD_TYPE_PRIMARY);
+            }
+
             if (DEBUG) {
                 Log.d(TAG, "Heif meta: " + width + "x" + height + ", rotation " + rotation);
             }
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 0955dd6..8ab5ec4 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -890,5 +890,14 @@
      */
     public static final int METADATA_KEY_VIDEO_FRAME_COUNT = 32;
 
+    /**
+     * @hide
+     */
+    public static final int METADATA_KEY_EXIF_OFFSET = 33;
+
+    /**
+     * @hide
+     */
+    public static final int METADATA_KEY_EXIF_LENGTH = 34;
     // Add more here...
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index aef31b1..f9a1f8b 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2128,7 +2128,13 @@
             mTimeProvider.close();
             mTimeProvider = null;
         }
-        mOnSubtitleDataListener = null;
+        synchronized(this) {
+            mSubtitleDataListenerDisabled = false;
+            mExtSubtitleDataListener = null;
+            mExtSubtitleDataHandler = null;
+            mOnMediaTimeDiscontinuityListener = null;
+            mOnMediaTimeDiscontinuityHandler = null;
+        }
 
         // Modular DRM clean up
         mOnDrmConfigHelper = null;
@@ -2699,7 +2705,7 @@
     private int mSelectedSubtitleTrackIndex = -1;
     private Vector<InputStream> mOpenSubtitleSources;
 
-    private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
+    private final OnSubtitleDataListener mIntSubtitleDataListener = new OnSubtitleDataListener() {
         @Override
         public void onSubtitleData(MediaPlayer mp, SubtitleData data) {
             int index = data.getTrackIndex();
@@ -2725,7 +2731,9 @@
             }
             mSelectedSubtitleTrackIndex = -1;
         }
-        setOnSubtitleDataListener(null);
+        synchronized (this) {
+            mSubtitleDataListenerDisabled = true;
+        }
         if (track == null) {
             return;
         }
@@ -2745,7 +2753,9 @@
                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
             } catch (IllegalStateException e) {
             }
-            setOnSubtitleDataListener(mSubtitleDataListener);
+            synchronized (this) {
+                mSubtitleDataListenerDisabled = false;
+            }
         }
         // no need to select out-of-band tracks
     }
@@ -3304,6 +3314,7 @@
     private static final int MEDIA_SUBTITLE_DATA = 201;
     private static final int MEDIA_META_DATA = 202;
     private static final int MEDIA_DRM_INFO = 210;
+    private static final int MEDIA_TIME_DISCONTINUITY = 211;
     private static final int MEDIA_AUDIO_ROUTING_CHANGED = 10000;
 
     private TimeProvider mTimeProvider;
@@ -3514,15 +3525,34 @@
                 return;
 
             case MEDIA_SUBTITLE_DATA:
-                OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
-                if (onSubtitleDataListener == null) {
-                    return;
+                final OnSubtitleDataListener extSubtitleListener;
+                final Handler extSubtitleHandler;
+                synchronized(this) {
+                    if (mSubtitleDataListenerDisabled) {
+                        return;
+                    }
+                    extSubtitleListener = mExtSubtitleDataListener;
+                    extSubtitleHandler = mExtSubtitleDataHandler;
                 }
                 if (msg.obj instanceof Parcel) {
                     Parcel parcel = (Parcel) msg.obj;
-                    SubtitleData data = new SubtitleData(parcel);
+                    final SubtitleData data = new SubtitleData(parcel);
                     parcel.recycle();
-                    onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+
+                    mIntSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
+
+                    if (extSubtitleListener != null) {
+                        if (extSubtitleHandler == null) {
+                            extSubtitleListener.onSubtitleData(mMediaPlayer, data);
+                        } else {
+                            extSubtitleHandler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    extSubtitleListener.onSubtitleData(mMediaPlayer, data);
+                                }
+                            });
+                        }
+                    }
                 }
                 return;
 
@@ -3553,6 +3583,43 @@
                 }
                 return;
 
+            case MEDIA_TIME_DISCONTINUITY:
+                final OnMediaTimeDiscontinuityListener mediaTimeListener;
+                final Handler mediaTimeHandler;
+                synchronized(this) {
+                    mediaTimeListener = mOnMediaTimeDiscontinuityListener;
+                    mediaTimeHandler = mOnMediaTimeDiscontinuityHandler;
+                }
+                if (mediaTimeListener == null) {
+                    return;
+                }
+                if (msg.obj instanceof Parcel) {
+                    Parcel parcel = (Parcel) msg.obj;
+                    parcel.setDataPosition(0);
+                    long anchorMediaUs = parcel.readLong();
+                    long anchorRealUs = parcel.readLong();
+                    float playbackRate = parcel.readFloat();
+                    parcel.recycle();
+                    final MediaTimestamp timestamp;
+                    if (anchorMediaUs != -1 && anchorRealUs != -1) {
+                        timestamp = new MediaTimestamp(
+                                anchorMediaUs /*Us*/, anchorRealUs * 1000 /*Ns*/, playbackRate);
+                    } else {
+                        timestamp = MediaTimestamp.TIMESTAMP_UNKNOWN;
+                    }
+                    if (mediaTimeHandler == null) {
+                        mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
+                    } else {
+                        mediaTimeHandler.post(new Runnable() {
+                            @Override
+                            public void run() {
+                                mediaTimeListener.onMediaTimeDiscontinuity(mMediaPlayer, timestamp);
+                            }
+                        });
+                    }
+                }
+                return;
+
             default:
                 Log.e(TAG, "Unknown message type " + msg.what);
                 return;
@@ -3877,13 +3944,15 @@
     private void setOnSubtitleDataListenerInt(
             @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) {
         synchronized (this) {
-            mOnSubtitleDataListener = listener;
-            mOnSubtitleDataHandler = handler;
+            mExtSubtitleDataListener = listener;
+            mExtSubtitleDataHandler = handler;
         }
     }
 
-    private OnSubtitleDataListener mOnSubtitleDataListener;
-    private Handler mOnSubtitleDataHandler;
+    private boolean mSubtitleDataListenerDisabled;
+    /** External OnSubtitleDataListener, the one set by {@link #setOnSubtitleDataListener}. */
+    private OnSubtitleDataListener mExtSubtitleDataListener;
+    private Handler mExtSubtitleDataHandler;
 
     /**
      * Interface definition of a callback to be invoked when discontinuity in the normal progression
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index 938dd14..dd43b4e 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -98,4 +98,13 @@
                 && (this.nanoTime == that.nanoTime)
                 && (this.clockRate == that.clockRate);
     }
+
+    @Override
+    public String toString() {
+        return getClass().getName()
+                + "{AnchorMediaTimeUs=" + mediaTimeUs
+                + " AnchorSystemNanoTime=" + nanoTime
+                + " clockRate=" + clockRate
+                + "}";
+    }
 }
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 21d6873..1cb4d67 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.os.Handler;
 import android.os.Looper;
@@ -133,9 +134,10 @@
               .fromString("7261676f-6d75-7369-6364-28e2fd3ac39e");
 
     /**
-     * Null effect UUID. Used when the UUID for effect type of
+     * Null effect UUID. See {@link AudioEffect(UUID, UUID, int, int)} for use.
      * @hide
      */
+    @TestApi
     public static final UUID EFFECT_TYPE_NULL = UUID
             .fromString("ec7178ec-e5e1-4432-a3f4-4657e6795210");
 
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index f16804c..84f85e7 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -531,7 +531,7 @@
          *
          * @param state The new playback state of the session
          */
-        public void onPlaybackStateChanged(@NonNull PlaybackState state) {
+        public void onPlaybackStateChanged(@Nullable PlaybackState state) {
         }
 
         /**
diff --git a/packages/InputDevices/res/raw/keyboard_layout_polish.kcm b/packages/InputDevices/res/raw/keyboard_layout_polish.kcm
new file mode 100644
index 0000000..559ec07
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_polish.kcm
@@ -0,0 +1,327 @@
+# 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.
+
+#
+# Polish (qwerty) keyboard layout.
+#
+
+type OVERLAY
+
+### ROW 1
+
+key GRAVE {
+    label:                              '`'
+    base:                               '`'
+    shift:                              '~'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base:                               'q'
+    shift, capslock:                    'Q'
+}
+
+key W {
+    label:                              'W'
+    base:                               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base:                               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u0119'
+    ralt+shift, ralt+capslock:          '\u0118'
+}
+
+key R {
+    label:                              'R'
+    base:                               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base:                               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base:                               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base:                               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base:                               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base:                               'o'
+    shift, capslock:                    'O'
+    ralt:                               '\u00F3'
+    ralt+shift, ralt+capslock:          '\u00D3'
+}
+
+key P {
+    label:                              'P'
+    base:                               'p'
+    shift, capslock:                    'P'
+}
+
+key LEFT_BRACKET {
+    label:                              '['
+    base:                               '['
+    shift:                              '{'
+}
+
+key RIGHT_BRACKET {
+    label:                              ']'
+    base:                               ']'
+    shift:                              '}'
+}
+
+key BACKSLASH {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '|'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base:                               'a'
+    shift, capslock:                    'A'
+    ralt:                               '\u0105'
+    ralt+shift, ralt+capslock:          '\u0104'
+}
+
+key S {
+    label:                              'S'
+    base:                               's'
+    shift, capslock:                    'S'
+    ralt:                               '\u015b'
+    ralt+shift, ralt+capslock:          '\u015a'
+}
+
+key D {
+    label:                              'D'
+    base:                               'd'
+    shift, capslock:                    'D'
+}
+
+key F {
+    label:                              'F'
+    base:                               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base:                               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base:                               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base:                               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base:                               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base:                               'l'
+    shift, capslock:                    'L'
+    ralt:                               '\u0142'
+    ralt+shift, ralt+capslock:          '\u0141'
+}
+
+key SEMICOLON {
+    label:                              ';'
+    base:                               ';'
+    shift:                              ':'
+}
+
+key APOSTROPHE {
+    label:                              '\''
+    base:                               '\''
+    shift:                              '"'
+}
+
+### ROW 4
+
+key Z {
+    label:                              'Z'
+    base:                               'z'
+    shift, capslock:                    'Z'
+    ralt:                               '\u017c'
+    ralt+shift, ralt+capslock:          '\u017b'
+}
+
+key X {
+    label:                              'X'
+    base:                               'x'
+    shift, capslock:                    'X'
+    ralt:                               '\u017a'
+    ralt+shift, ralt+capslock:          '\u0179'
+}
+
+key C {
+    label:                              'C'
+    base:                               'c'
+    shift, capslock:                    'C'
+    ralt:                               '\u0107'
+    ralt+shift, ralt+capslock:          '\u0106'
+}
+
+key V {
+    label:                              'V'
+    base:                               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base:                               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base:                               'n'
+    shift, capslock:                    'N'
+    ralt:                               '\u0144'
+    ralt+shift, ralt+capslock:          '\u0143'
+}
+
+key M {
+    label:                              'M'
+    base:                               'm'
+    shift, capslock:                    'M'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              '<'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              '>'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 61d3234..5fdc4a6 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -125,4 +125,7 @@
 
     <!-- Azerbaijani keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_azerbaijani">Azerbaijani</string>
+
+    <!-- Polish keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_polish">Polish</string>
 </resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index c6bfc1f..1807aea 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -159,4 +159,8 @@
     <keyboard-layout android:name="keyboard_layout_azerbaijani"
             android:label="@string/keyboard_layout_azerbaijani"
             android:keyboardLayout="@raw/keyboard_layout_azerbaijani" />
+
+    <keyboard-layout android:name="keyboard_layout_polish"
+            android:label="@string/keyboard_layout_polish"
+            android:keyboardLayout="@raw/keyboard_layout_polish" />
 </keyboard-layouts>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 34a099c..00ee575 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -195,8 +195,10 @@
                 if (DEBUG) Log.d(TAG, "Adding local HEADSET profile");
                 mHeadsetProfile = new HeadsetProfile(mContext, mLocalAdapter,
                         mDeviceManager, this);
-                addProfile(mHeadsetProfile, HeadsetProfile.NAME,
-                        BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+                addHeadsetProfile(mHeadsetProfile, HeadsetProfile.NAME,
+                        BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
+                        BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
+                        BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
             }
         } else if (mHeadsetProfile != null) {
             Log.w(TAG, "Warning: HEADSET profile was previously added but the UUID is now missing.");
@@ -208,8 +210,10 @@
                 if(DEBUG) Log.d(TAG, "Adding local HfpClient profile");
                 mHfpClientProfile =
                     new HfpClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
-                addProfile(mHfpClientProfile, HfpClientProfile.NAME,
-                        BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
+                addHeadsetProfile(mHfpClientProfile, HfpClientProfile.NAME,
+                        BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED,
+                        BluetoothHeadsetClient.ACTION_AUDIO_STATE_CHANGED,
+                        BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED);
             }
         } else if (mHfpClientProfile != null) {
             Log.w(TAG,
@@ -277,6 +281,15 @@
         // There is no local SDP record for HID and Settings app doesn't control PBAP Server.
     }
 
+    private void addHeadsetProfile(LocalBluetoothProfile profile, String profileName,
+            String stateChangedAction, String audioStateChangedAction, int audioDisconnectedState) {
+        BluetoothEventManager.Handler handler = new HeadsetStateChangeHandler(
+                profile, audioStateChangedAction, audioDisconnectedState);
+        mEventManager.addProfileHandler(stateChangedAction, handler);
+        mEventManager.addProfileHandler(audioStateChangedAction, handler);
+        mProfileNameMap.put(profileName, profile);
+    }
+
     private final Collection<ServiceListener> mServiceListeners =
             new ArrayList<ServiceListener>();
 
@@ -323,18 +336,47 @@
                 cachedDevice = mDeviceManager.addDevice(mLocalAdapter,
                         LocalBluetoothProfileManager.this, device);
             }
+            onReceiveInternal(intent, cachedDevice);
+        }
+
+        protected void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
             int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
             int oldState = intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, 0);
             if (newState == BluetoothProfile.STATE_DISCONNECTED &&
                     oldState == BluetoothProfile.STATE_CONNECTING) {
                 Log.i(TAG, "Failed to connect " + mProfile + " device");
             }
-
             cachedDevice.onProfileStateChanged(mProfile, newState);
             cachedDevice.refresh();
         }
     }
 
+    /** Connectivity and audio state change handler for headset profiles. */
+    private class HeadsetStateChangeHandler extends StateChangedHandler {
+        private final String mAudioChangeAction;
+        private final int mAudioDisconnectedState;
+
+        HeadsetStateChangeHandler(LocalBluetoothProfile profile, String audioChangeAction,
+                int audioDisconnectedState) {
+            super(profile);
+            mAudioChangeAction = audioChangeAction;
+            mAudioDisconnectedState = audioDisconnectedState;
+        }
+
+        @Override
+        public void onReceiveInternal(Intent intent, CachedBluetoothDevice cachedDevice) {
+            if (mAudioChangeAction.equals(intent.getAction())) {
+                int newState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 0);
+                if (newState != mAudioDisconnectedState) {
+                    cachedDevice.onProfileStateChanged(mProfile, BluetoothProfile.STATE_CONNECTED);
+                }
+                cachedDevice.refresh();
+            } else {
+                super.onReceiveInternal(intent, cachedDevice);
+            }
+        }
+    }
+
     /** State change handler for NAP and PANU profiles. */
     private class PanStateChangedHandler extends StateChangedHandler {
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index f43e719..67053d8 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -742,6 +742,9 @@
         dumpSetting(s, p,
                 Settings.Global.LOCATION_GLOBAL_KILL_SWITCH,
                 GlobalSettingsProto.Location.GLOBAL_KILL_SWITCH);
+        dumpSetting(s, p,
+                Settings.Global.GNSS_SATELLITE_BLACKLIST,
+                GlobalSettingsProto.Location.GNSS_SATELLITE_BLACKLIST);
         p.end(locationToken);
 
         final long lpmToken = p.start(GlobalSettingsProto.LOW_POWER_MODE);
@@ -2019,6 +2022,10 @@
                 SecureSettingsProto.Rotation.NUM_ROTATION_SUGGESTIONS_ACCEPTED);
         p.end(rotationToken);
 
+        dumpSetting(s, p,
+                Settings.Secure.RTT_CALLING_MODE,
+                SecureSettingsProto.RTT_CALLING_MODE);
+
         final long screensaverToken = p.start(SecureSettingsProto.SCREENSAVER);
         dumpSetting(s, p,
                 Settings.Secure.SCREENSAVER_ENABLED,
@@ -2402,10 +2409,6 @@
                 SystemSettingsProto.Rotation.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
         p.end(rotationToken);
 
-        dumpSetting(s, p,
-                Settings.System.RTT_CALLING_MODE,
-                SystemSettingsProto.RTT_CALLING_MODE);
-
         final long screenToken = p.start(SystemSettingsProto.SCREEN);
         dumpSetting(s, p,
                 Settings.System.SCREEN_OFF_TIMEOUT,
diff --git a/packages/SystemUI/res/drawable/stat_sys_ethernet.xml b/packages/SystemUI/res/drawable/stat_sys_ethernet.xml
index e765320..babaa9f 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ethernet.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ethernet.xml
@@ -20,11 +20,11 @@
         android:viewportWidth="53.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="?attr/singleToneColor"
+        android:fillColor="#FFF"
         android:pathData="M15.54 13.52l-3.08-2.55L1.64 24l10.82 13.04 3.08-2.55L6.84 24l8.7-10.48zM14 26h4v-4h-4v4zm20-4h-4v4h4v-4zm-12 4h4v-4h-4v4zm13.54-15.04l-3.08 2.55L41.16 24l-8.7 10.48 3.08 2.55L46.36 24 35.54 10.96z"/>
     <path
-        android:fillColor="?attr/singleToneColor"
+        android:fillColor="#FFF"
         android:pathData="M49.000000,40.000000l4.000000,0.000000l0.000000,4.000000l-4.000000,0.000000z"/>
     <path
-        android:fillColor="?attr/singleToneColor"
+        android:fillColor="#FFF"
         android:pathData="M49.000000,20.000000l4.000000,0.000000l0.000000,16.100000l-4.000000,0.000000z"/></vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_ethernet_fully.xml b/packages/SystemUI/res/drawable/stat_sys_ethernet_fully.xml
index b7b154c..08a9993 100644
--- a/packages/SystemUI/res/drawable/stat_sys_ethernet_fully.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_ethernet_fully.xml
@@ -19,6 +19,6 @@
         android:viewportWidth="53.0"
         android:viewportHeight="48.0">
     <path
-        android:fillColor="?attr/singleToneColor"
+        android:fillColor="#FFF"
         android:pathData="M15.54 13.52l-3.08-2.55L1.64 24l10.82 13.04 3.08-2.55L6.84 24l8.7-10.48zM14 26h4v-4h-4v4zm20-4h-4v4h4v-4zm-12 4h4v-4h-4v4zm13.54-15.04l-3.08 2.55L41.16 24l-8.7 10.48 3.08 2.55L46.36 24 35.54 10.96z"/>
 </vector>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index f38129f..388b633 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -31,25 +31,23 @@
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
-        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:singleLine="true"
+        android:gravity="center_vertical|start"
         android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
         android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
-        android:gravity="center_vertical|start"
-        systemui:showDark="false"
-    />
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+        systemui:showDark="false" />
 
     <com.android.systemui.statusbar.policy.DateView
         android:id="@+id/date"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:padding="4dp"
+        android:gravity="center_vertical"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
         android:textSize="@dimen/qs_time_collapsed_size"
-        android:gravity="center_vertical"
         systemui:datePattern="@string/abbrev_wday_month_day_no_year_alarm" />
 
     <android.widget.Space
@@ -57,12 +55,11 @@
         android:layout_width="0dp"
         android:layout_height="match_parent"
         android:layout_weight="1"
-        android:gravity="center_vertical|center_horizontal"
-    />
+        android:gravity="center_vertical|center_horizontal" />
 
-    <com.android.systemui.BatteryMeterView android:id="@+id/battery"
+    <com.android.systemui.BatteryMeterView
+        android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
-        android:gravity="center_vertical|end"
-        />
+        android:gravity="center_vertical|end" />
 </LinearLayout>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 107249b..ca5b034 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -71,6 +71,9 @@
 
     private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
 
+    // Should match the values in PhoneWindowManager
+    public static final String CLOSE_SYSTEM_WINDOWS_REASON_RECENTS = "recentapps";
+
     private final PackageManager mPackageManager;
     private final BackgroundExecutor mBackgroundExecutor;
     private final TaskStackChangeListeners mTaskStackChangeListeners;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java
new file mode 100644
index 0000000..bf88a29
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ThreadedRendererCompat.java
@@ -0,0 +1,33 @@
+/*
+ * 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.systemui.shared.system;
+
+import android.view.ThreadedRenderer;
+
+/**
+ * @see ThreadedRenderer
+ */
+public class ThreadedRendererCompat {
+
+    public static int EGL_CONTEXT_PRIORITY_HIGH_IMG = 0x3101;
+    public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102;
+    public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+
+    public static void setContextPriority(int priority) {
+        ThreadedRenderer.setContextPriority(priority);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 1ae06d7..0683514 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -81,6 +81,14 @@
     private float mDarkIntensity;
     private int mUser;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings.
+     */
+    private boolean mUseWallpaperTextColors;
+
+    private int mNonAdaptedForegroundColor;
+    private int mNonAdaptedBackgroundColor;
+
     public BatteryMeterView(Context context) {
         this(context, null, 0);
     }
@@ -140,6 +148,29 @@
         updateShowPercent();
     }
 
+    /**
+     * Sets whether the battery meter view uses the wallpaperTextColor. If we're not using it, we'll
+     * revert back to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for all
+     *                                    components
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColors) {
+            return;
+        }
+
+        mUseWallpaperTextColors = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColors) {
+            updateColors(
+                    Utils.getColorAttr(mContext, R.attr.wallpaperTextColor),
+                    Utils.getColorAttr(mContext, R.attr.wallpaperTextColorSecondary));
+        } else {
+            updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+        }
+    }
+
     public void setColorsFromContext(Context context) {
         if (context == null) {
             return;
@@ -179,7 +210,8 @@
         getContext().getContentResolver().registerContentObserver(
                 Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver, mUser);
         updateShowPercent();
-        Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+        Dependency.get(TunerService.class)
+                .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
         Dependency.get(ConfigurationController.class).addCallback(this);
         mUserTracker.startTracking();
     }
@@ -273,19 +305,23 @@
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         mDarkIntensity = darkIntensity;
+
         float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
-        int foreground = getColorForDarkIntensity(intensity, mLightModeFillColor,
-                mDarkModeFillColor);
-        int background = getColorForDarkIntensity(intensity, mLightModeBackgroundColor,
-                mDarkModeBackgroundColor);
-        mDrawable.setColors(foreground, background);
-        setTextColor(foreground);
+        mNonAdaptedForegroundColor = getColorForDarkIntensity(
+                intensity, mLightModeFillColor, mDarkModeFillColor);
+        mNonAdaptedBackgroundColor = getColorForDarkIntensity(
+                intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor);
+
+        if (!mUseWallpaperTextColors) {
+            updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor);
+        }
     }
 
-    public void setTextColor(int color) {
-        mTextColor = color;
+    private void updateColors(int foregroundColor, int backgroundColor) {
+        mDrawable.setColors(foregroundColor, backgroundColor);
+        mTextColor = foregroundColor;
         if (mBatteryPercentView != null) {
-            mBatteryPercentView.setTextColor(color);
+            mBatteryPercentView.setTextColor(foregroundColor);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 039e7b5..0ba26e9 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockIcon;
 import com.android.systemui.statusbar.phone.LockscreenWallpaper;
@@ -142,5 +143,6 @@
         providers.put(NotificationViewHierarchyManager.class,
                 () -> new NotificationViewHierarchyManager(context));
         providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
+        providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
index 3577c0f..c238e54 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.hardware.biometrics.BiometricDialog;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -48,7 +48,7 @@
 
     private FingerprintDialogView mDialogView;
     private WindowManager mWindowManager;
-    private IBiometricDialogReceiver mReceiver;
+    private IBiometricPromptReceiver mReceiver;
     private boolean mDialogShowing;
 
     private Handler mHandler = new Handler() {
@@ -97,7 +97,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         if (DEBUG) Log.d(TAG, "showFingerprintDialog");
         // Remove these messages as they are part of the previous client
         mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
@@ -139,7 +139,7 @@
             Log.w(TAG, "Dialog already showing");
             return;
         }
-        mReceiver = (IBiometricDialogReceiver) args.arg2;
+        mReceiver = (IBiometricPromptReceiver) args.arg2;
         mDialogView.setBundle((Bundle)args.arg1);
         mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
         mDialogShowing = true;
@@ -177,7 +177,7 @@
         }
         if (userCanceled) {
             try {
-                mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_USER_CANCEL);
+                mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException when hiding dialog", e);
             }
@@ -193,7 +193,7 @@
             return;
         }
         try {
-            mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_NEGATIVE);
+            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling negative button", e);
         }
@@ -206,7 +206,7 @@
             return;
         }
         try {
-            mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_POSITIVE);
+            mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling positive button", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index 3e1ac02..d1d6609 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -22,11 +22,12 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
-import android.hardware.biometrics.BiometricDialog;
+import android.hardware.biometrics.BiometricPrompt;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.text.TextUtils;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -161,29 +162,29 @@
         mLastState = STATE_NONE;
         updateFingerprintIcon(STATE_FINGERPRINT);
 
-        title.setText(mBundle.getCharSequence(BiometricDialog.KEY_TITLE));
+        title.setText(mBundle.getCharSequence(BiometricPrompt.KEY_TITLE));
         title.setSelected(true);
 
-        final CharSequence subtitleText = mBundle.getCharSequence(BiometricDialog.KEY_SUBTITLE);
-        if (subtitleText == null) {
+        final CharSequence subtitleText = mBundle.getCharSequence(BiometricPrompt.KEY_SUBTITLE);
+        if (TextUtils.isEmpty(subtitleText)) {
             subtitle.setVisibility(View.GONE);
         } else {
             subtitle.setVisibility(View.VISIBLE);
             subtitle.setText(subtitleText);
         }
 
-        final CharSequence descriptionText = mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION);
-        if (descriptionText == null) {
-            subtitle.setVisibility(View.VISIBLE);
+        final CharSequence descriptionText = mBundle.getCharSequence(BiometricPrompt.KEY_DESCRIPTION);
+        if (TextUtils.isEmpty(descriptionText)) {
             description.setVisibility(View.GONE);
         } else {
-            description.setText(mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION));
+            description.setVisibility(View.VISIBLE);
+            description.setText(descriptionText);
         }
 
-        negative.setText(mBundle.getCharSequence(BiometricDialog.KEY_NEGATIVE_TEXT));
+        negative.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
         final CharSequence positiveText =
-                mBundle.getCharSequence(BiometricDialog.KEY_POSITIVE_TEXT);
+                mBundle.getCharSequence(BiometricPrompt.KEY_POSITIVE_TEXT);
         positive.setText(positiveText); // needs to be set for marquee to work
         if (positiveText != null) {
             positive.setVisibility(View.VISIBLE);
@@ -269,7 +270,7 @@
         mErrorText.setTextColor(mErrorColor);
         mErrorText.setContentDescription(message);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
-                BiometricDialog.HIDE_DIALOG_DELAY);
+                BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
     public void showHelpMessage(String message) {
@@ -279,7 +280,7 @@
     public void showErrorMessage(String error) {
         showTemporaryMessage(error);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
-                false /* userCanceled */), BiometricDialog.HIDE_DIALOG_DELAY);
+                false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
     private void updateFingerprintIcon(int newState) {
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 a0bdcd0..1805f96 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -295,6 +295,26 @@
                 final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
                         ? expandedAdjustedBounds
                         : normalAdjustedBounds;
+                final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
+                        ? expandedMovementBounds
+                        : normalMovementBounds;
+
+                // If the PIP window needs to shift to right above shelf/IME and it's already above
+                // that, don't move the PIP window.
+                if (toAdjustedBounds.bottom < mMovementBounds.bottom
+                        && animatingBounds.top < toAdjustedBounds.bottom) {
+                    return;
+                }
+
+                // If the PIP window needs to shift down due to dismissal of shelf/IME but it's way
+                // above the position as if shelf/IME shows, don't move the PIP window.
+                int movementBoundsAdjustment = toMovementBounds.bottom - mMovementBounds.bottom;
+                int offsetAdjustment = fromImeAdjustment ? mImeOffset : mShelfHeight;
+                if (toAdjustedBounds.bottom >= mMovementBounds.bottom
+                        && animatingBounds.top
+                        < toAdjustedBounds.bottom - movementBoundsAdjustment - offsetAdjustment) {
+                    return;
+                }
 
                 animateToOffset(animatingBounds, toAdjustedBounds);
             }
@@ -320,10 +340,6 @@
 
     private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
         final Rect bounds = new Rect(animatingBounds);
-        if (toAdjustedBounds.bottom < mMovementBounds.bottom
-                && bounds.top < toAdjustedBounds.bottom) {
-            return;
-        }
         bounds.offset(0, toAdjustedBounds.bottom - bounds.top);
         // In landscape mode, PIP window can go offset while launching IME. We want to align the
         // the top of the PIP window with the top of the movement bounds in that case.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index bfbfbf6..7161463 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,17 +18,15 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Canvas;
-import android.graphics.Path;
 import android.graphics.Point;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.widget.FrameLayout;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.R;
 import com.android.systemui.qs.customize.QSCustomizer;
-import com.android.systemui.statusbar.ExpandableOutlineView;
 
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -44,7 +42,11 @@
     protected float mQsExpansion;
     private QSCustomizer mQSCustomizer;
     private View mQSFooter;
+
     private View mBackground;
+    private View mBackgroundGradient;
+    private View mStatusBarBackground;
+
     private int mSideMargins;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
@@ -60,6 +62,8 @@
         mQSCustomizer = findViewById(R.id.qs_customize);
         mQSFooter = findViewById(R.id.qs_footer);
         mBackground = findViewById(R.id.quick_settings_background);
+        mStatusBarBackground = findViewById(R.id.quick_settings_status_bar_background);
+        mBackgroundGradient = findViewById(R.id.quick_settings_gradient_view);
         mSideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
 
         setClickable(true);
@@ -68,6 +72,22 @@
     }
 
     @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Hide the backgrounds when in landscape mode.
+        if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            mBackgroundGradient.setVisibility(View.INVISIBLE);
+            mStatusBarBackground.setVisibility(View.INVISIBLE);
+        } else {
+            mBackgroundGradient.setVisibility(View.VISIBLE);
+            mStatusBarBackground.setVisibility(View.VISIBLE);
+        }
+
+        updateResources();
+    }
+
+    @Override
     public boolean performClick() {
         // Want to receive clicks so missing QQS tiles doesn't cause collapse, but
         // don't want to do anything with them.
@@ -101,6 +121,14 @@
         updateExpansion();
     }
 
+    private void updateResources() {
+        LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
+        layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
+                com.android.internal.R.dimen.quick_qs_offset_height);
+
+        mQSPanel.setLayoutParams(layoutParams);
+    }
+
     /**
      * Overrides the height of this view (post-layout), so that the content is clipped to that
      * height and the background is set to that height.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 224c367..70bfad1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Rect;
 import android.media.AudioManager;
@@ -54,8 +55,10 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
+import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.NextAlarmController;
 
 import java.util.Locale;
@@ -90,6 +93,7 @@
     private TouchAnimator mStatusIconsAlphaAnimator;
     private TouchAnimator mHeaderTextContainerAlphaAnimator;
 
+    private View mSystemIconsView;
     private View mQuickQsStatusIcons;
     private View mDate;
     private View mHeaderTextContainerView;
@@ -107,6 +111,9 @@
     private View mStatusSeparator;
     private ImageView mRingerModeIcon;
     private TextView mRingerModeTextView;
+    private BatteryMeterView mBatteryMeterView;
+    private Clock mClockView;
+    private DateView mDateView;
 
     private NextAlarmController mAlarmController;
     /** Counts how many times the long press tooltip has been shown to the user. */
@@ -138,6 +145,7 @@
         mHeaderQsPanel = findViewById(R.id.quick_qs_panel);
         mDate = findViewById(R.id.date);
         mDate.setOnClickListener(this);
+        mSystemIconsView = findViewById(R.id.quick_status_bar_system_icons);
         mQuickQsStatusIcons = findViewById(R.id.quick_qs_status_icons);
         mIconManager = new TintedIconManager(findViewById(R.id.statusIcons));
 
@@ -164,8 +172,10 @@
         // Set the correct tint for the status icons so they contrast
         mIconManager.setTint(fillColor);
 
-        BatteryMeterView battery = findViewById(R.id.battery);
-        battery.setForceShowPercent(true);
+        mBatteryMeterView = findViewById(R.id.battery);
+        mBatteryMeterView.setForceShowPercent(true);
+        mClockView = findViewById(R.id.clock);
+        mDateView = findViewById(R.id.date);
     }
 
     private void updateStatusText() {
@@ -212,6 +222,13 @@
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         updateResources();
+
+        // Update color schemes in landscape to use wallpaperTextColor
+        boolean shouldUseWallpaperTextColor =
+                newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
+        mBatteryMeterView.useWallpaperTextColor(shouldUseWallpaperTextColor);
+        mClockView.useWallpaperTextColor(shouldUseWallpaperTextColor);
+        mDateView.useWallpaperTextColor(shouldUseWallpaperTextColor);
     }
 
     @Override
@@ -221,11 +238,21 @@
     }
 
     private void updateResources() {
-        // Update height, especially due to landscape mode restricting space.
+        Resources resources = mContext.getResources();
+
+        // Update height for a few views, especially due to landscape mode restricting space.
         mHeaderTextContainerView.getLayoutParams().height =
-                mContext.getResources().getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
+                resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
         mHeaderTextContainerView.setLayoutParams(mHeaderTextContainerView.getLayoutParams());
 
+        mSystemIconsView.getLayoutParams().height = resources.getDimensionPixelSize(
+                com.android.internal.R.dimen.quick_qs_offset_height);
+        mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams());
+
+        getLayoutParams().height =
+                resources.getDimensionPixelSize(com.android.internal.R.dimen.quick_qs_total_height);
+        setLayoutParams(getLayoutParams());
+
         updateStatusIconAlphaAnimator();
         updateHeaderTextContainerAlphaAnimator();
     }
@@ -497,9 +524,8 @@
         mHeaderQsPanel.setHost(host, null /* No customization in header */);
 
         // Use SystemUI context to get battery meter colors, and let it use the default tint (white)
-        BatteryMeterView battery = findViewById(R.id.battery);
-        battery.setColorsFromContext(mHost.getContext());
-        battery.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
+        mBatteryMeterView.setColorsFromContext(mHost.getContext());
+        mBatteryMeterView.onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
     }
 
     public void setCallback(Callback qsPanelCallback) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 0f85c5b..8bb3c02 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -22,6 +22,7 @@
 import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_RECENT_APPS;
 
 import android.app.ActivityManager;
+import android.app.trust.TrustManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -51,6 +52,7 @@
 import com.android.systemui.OverviewProxyService;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
+import com.android.systemui.SystemUIApplication;
 import com.android.systemui.shared.recents.IOverviewProxy;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.events.EventBus;
@@ -70,6 +72,7 @@
 import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 
+import com.android.systemui.statusbar.phone.StatusBar;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -107,6 +110,7 @@
 
     private Handler mHandler;
     private RecentsImpl mImpl;
+    private TrustManager mTrustManager;
     private int mDraggingInRecentsCurrentUser;
 
     // Only For system user, this is the callbacks instance we return to each secondary user
@@ -235,6 +239,8 @@
             registerWithSystemUser();
         }
         putComponent(Recents.class, this);
+
+        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
     }
 
     @Override
@@ -342,12 +348,28 @@
         // If connected to launcher service, let it handle the toggle logic
         IOverviewProxy overviewProxy = mOverviewProxyService.getProxy();
         if (overviewProxy != null) {
-            try {
-                overviewProxy.onOverviewToggle();
-                return;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
+            final Runnable toggleRecents = () -> {
+                try {
+                    if (mOverviewProxyService.getProxy() != null) {
+                        mOverviewProxyService.getProxy().onOverviewToggle();
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Cannot send toggle recents through proxy service.", e);
+                }
+            };
+            // Preload only if device for current user is unlocked
+            final StatusBar statusBar = getComponent(StatusBar.class);
+            if (statusBar != null && statusBar.isKeyguardShowing()) {
+                statusBar.executeRunnableDismissingKeyguard(() -> {
+                        // Flush trustmanager before checking device locked per user
+                        mTrustManager.reportKeyguardShowingChanged();
+                        mHandler.post(toggleRecents);
+                    }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
+                    true /* deferred */);
+            } else {
+                toggleRecents.run();
             }
+            return;
         }
 
         int growTarget = getComponent(Divider.class).getView().growsRecents();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 3dd6e35..bfbba7c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -107,7 +107,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSLUCENT);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 65037f9..6fd0aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -160,7 +160,7 @@
 
         default void onRotationProposal(int rotation, boolean isValid) { }
 
-        default void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) { }
+        default void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) { }
         default void onFingerprintAuthenticated() { }
         default void onFingerprintHelp(String message) { }
         default void onFingerprintError(String error) { }
@@ -513,7 +513,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
@@ -759,7 +759,7 @@
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).showFingerprintDialog(
                                 (Bundle)((SomeArgs)msg.obj).arg1,
-                                (IBiometricDialogReceiver)((SomeArgs)msg.obj).arg2);
+                                (IBiometricPromptReceiver)((SomeArgs)msg.obj).arg2);
                     }
                     break;
                 case MSG_FINGERPRINT_AUTHENTICATED:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index ccabb79..e24bf67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -47,7 +47,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.List;
 
 /**
  * Handles keeping track of the current user, profiles, and various things related to hiding
@@ -352,7 +351,8 @@
             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
                     mContext.getContentResolver(),
                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
-            final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
+            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+                    DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
             final boolean allowed = allowedByUser && allowedByDpm;
             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
             return allowed;
@@ -361,13 +361,13 @@
         return mUsersAllowingPrivateNotifications.get(userHandle);
     }
 
-    private boolean adminAllowsUnredactedNotifications(int userHandle) {
+    private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
         if (userHandle == UserHandle.USER_ALL) {
             return true;
         }
-        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
-                userHandle);
-        return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+        final int dpmFlags =
+                mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle);
+        return (dpmFlags & feature) == 0;
     }
 
     /**
@@ -389,14 +389,17 @@
      * "public" (secure & locked) mode?
      */
     private boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (isCurrentProfile(userHandle)) {
+        if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
             return true;
         }
 
         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowed = 0 != Settings.Secure.getIntForUser(
+            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
                     mContext.getContentResolver(),
                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
+            final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
+                    DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
+            final boolean allowed = allowedByUser && allowedByDpm;
             mUsersAllowingNotifications.append(userHandle, allowed);
             return allowed;
         }
@@ -428,7 +431,6 @@
                 Notification.VISIBILITY_PRIVATE;
     }
 
-
     private void updateCurrentProfilesCache() {
         synchronized (mCurrentProfiles) {
             mCurrentProfiles.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index fd3a9d5e..1637849 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -323,8 +323,7 @@
             boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
                     .notification);
             if (suppressedSummary
-                    || (mLockscreenUserManager.isLockscreenPublicMode(userId)
-                    && !mLockscreenUserManager.shouldShowLockscreenNotifications())
+                    || mLockscreenUserManager.shouldHideNotifications(userId)
                     || (isLocked && !showOnKeyguard)) {
                 entry.row.setVisibility(View.GONE);
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index f81671b..51b4239 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -142,12 +142,14 @@
         if (mState.strengthId != state.strengthId) {
             mMobileDrawable.setLevel(state.strengthId);
         }
-        if (mState.typeId != state.typeId && state.typeId != 0) {
-            mMobileType.setContentDescription(state.typeContentDescription);
-            mMobileType.setImageResource(state.typeId);
-            mMobileType.setVisibility(View.VISIBLE);
-        } else {
-            mMobileType.setVisibility(View.GONE);
+        if (mState.typeId != state.typeId) {
+            if (state.typeId != 0) {
+                mMobileType.setContentDescription(state.typeContentDescription);
+                mMobileType.setImageResource(state.typeId);
+                mMobileType.setVisibility(View.VISIBLE);
+            } else {
+                mMobileType.setVisibility(View.GONE);
+            }
         }
 
         mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index df2b817..60a3474 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -159,6 +159,11 @@
      */
     public void onFullyShown() {
         mFalsingManager.onBouncerShown();
+        if (mKeyguardView == null) {
+            Log.wtf(TAG, "onFullyShown when view was null");
+        } else {
+            mKeyguardView.onResume();
+        }
     }
 
     /**
@@ -180,7 +185,6 @@
         @Override
         public void run() {
             mRoot.setVisibility(View.VISIBLE);
-            mKeyguardView.onResume();
             showPromptReason(mBouncerPromptReason);
             final CharSequence customMessage = mCallback.consumeCustomMessage();
             if (customMessage != null) {
@@ -296,7 +300,7 @@
 
     public boolean isShowing() {
         return (mShowingSoon || (mRoot != null && mRoot.getVisibility() == View.VISIBLE))
-                && mExpansion == 0;
+                && mExpansion == 0 && !isAnimatingAway();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
new file mode 100644
index 0000000..759a0d1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+
+/** Executes actions that require the screen to be unlocked. */
+public interface KeyguardDismissHandler {
+    /** Executes an action that requres the screen to be unlocked. */
+    void dismissKeyguardThenExecute(
+            OnDismissAction action, @Nullable Runnable cancelAction, boolean afterKeyguardGone);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
new file mode 100644
index 0000000..c38b0b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardDismissUtil.java
@@ -0,0 +1,53 @@
+/*
+ * 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.systemui.statusbar.phone;
+
+import android.util.Log;
+
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
+
+/**
+ * Executes actions that require the screen to be unlocked. Delegates the actual handling to an
+ * implementation passed via {@link #setDismissHandler}.
+ */
+public class KeyguardDismissUtil implements KeyguardDismissHandler {
+    private static final String TAG = "KeyguardDismissUtil";
+
+    private volatile KeyguardDismissHandler mDismissHandler;
+
+    /** Sets the actual {@link DismissHandler} implementation. */
+    public void setDismissHandler(KeyguardDismissHandler dismissHandler) {
+        mDismissHandler = dismissHandler;
+    }
+
+    /**
+     * Executes an action that requres the screen to be unlocked.
+     *
+     * <p>Must be called after {@link #setDismissHandler}.
+     */
+    @Override
+    public void dismissKeyguardThenExecute(
+            OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) {
+        KeyguardDismissHandler dismissHandler = mDismissHandler;
+        if (dismissHandler == null) {
+            Log.wtf(TAG, "KeyguardDismissHandler not set.");
+            action.onDismiss();
+            return;
+        }
+        dismissHandler.dismissKeyguardThenExecute(action, cancelAction, afterKeyguardGone);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 4c4eb60..db2139d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -39,7 +39,6 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemProperties;
-import android.os.VibrationEffect;
 import android.support.annotation.ColorInt;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -69,7 +68,6 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.NavigationBarCompat;
 import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.VibratorHelper;
 import com.android.systemui.statusbar.policy.DeadZone;
 import com.android.systemui.statusbar.policy.KeyButtonDrawable;
 import com.android.systemui.statusbar.policy.TintedKeyButtonDrawable;
@@ -150,7 +148,6 @@
     private Divider mDivider;
     private RecentsOnboarding mRecentsOnboarding;
     private NotificationPanelView mPanelView;
-    private final VibratorHelper mVibratorHelper;
 
     private int mRotateBtnStyle = R.style.RotateButtonCCWStart90;
 
@@ -246,7 +243,6 @@
 
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
         mRecentsOnboarding = new RecentsOnboarding(context, mOverviewProxyService);
-        mVibratorHelper = Dependency.get(VibratorHelper.class);
 
         mConfiguration = new Configuration();
         mConfiguration.updateFrom(context.getResources().getConfiguration());
@@ -314,9 +310,6 @@
                 } else if (mRecentsButtonBounds.contains(x, y)) {
                     mDownHitTarget = HIT_TARGET_OVERVIEW;
                 }
-
-                // Vibrate tick whenever down occurs on navigation bar
-                mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
                 break;
         }
         return mGestureHelper.onInterceptTouchEvent(event);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 9adf923..8c257fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -370,11 +370,15 @@
         float layoutEnd = getLayoutEnd();
         float overflowStart = getMaxOverflowStart();
         mVisualOverflowStart = 0;
+        mFirstVisibleIconState = null;
         boolean hasAmbient = mSpeedBumpIndex != -1 && mSpeedBumpIndex < getChildCount();
         for (int i = 0; i < childCount; i++) {
             View view = getChildAt(i);
             IconState iconState = mIconStates.get(view);
             iconState.xTranslation = translationX;
+            if (mFirstVisibleIconState == null) {
+                mFirstVisibleIconState = iconState;
+            }
             boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex
                     && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons;
             boolean noOverflowAfter = i == childCount - 1;
@@ -401,7 +405,6 @@
         mNumDots = 0;
         if (firstOverflowIndex != -1) {
             translationX = mVisualOverflowStart;
-            mFirstVisibleIconState = null;
             for (int i = firstOverflowIndex; i < childCount; i++) {
                 View view = getChildAt(i);
                 IconState iconState = mIconStates.get(view);
@@ -416,9 +419,6 @@
                     }
                     translationX += (mNumDots == MAX_DOTS ? MAX_DOTS * dotWidth : dotWidth)
                             * iconState.iconAppearAmount;
-                    if (mFirstVisibleIconState == null) {
-                        mFirstVisibleIconState = iconState;
-                    }
                     mLastVisibleIconState = iconState;
                 } else {
                     iconState.visibleState = StatusBarIconView.STATE_HIDDEN;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 3a34dcc..3dbac51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -91,7 +91,7 @@
     /**
      * Default alpha value for most scrims.
      */
-    public static final float GRADIENT_SCRIM_ALPHA = 0.45f;
+    public static final float GRADIENT_SCRIM_ALPHA = 0.70f;
     /**
      * A scrim varies its opacity based on a busyness factor, for example
      * how many notifications are currently visible.
@@ -395,7 +395,7 @@
             // Darken scrim as you pull down the shade when unlocked
             float behindFraction = getInterpolatedFraction();
             behindFraction = (float) Math.pow(behindFraction, 0.8f);
-            mCurrentBehindAlpha = behindFraction * mScrimBehindAlphaKeyguard;
+            mCurrentBehindAlpha = behindFraction * GRADIENT_SCRIM_ALPHA_BUSY;
             mCurrentInFrontAlpha = 0;
         } else if (mState == ScrimState.KEYGUARD) {
             // Either darken of make the scrim transparent when you
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 340b462..7987bfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -213,6 +213,7 @@
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.UnlockMethodCache.OnUnlockMethodChangedListener;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -1300,6 +1301,8 @@
 
         mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
         mLightBarController.setFingerprintUnlockController(mFingerprintUnlockController);
+        Dependency.get(KeyguardDismissUtil.class).setDismissHandler(
+                this::dismissKeyguardThenExecute);
         Trace.endSection();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 510af03..b4e7575 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -306,17 +306,6 @@
         mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
     }
 
-    /**
-     * For mobile essentially (an array of holders in one slot)
-     */
-    private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) {
-        for (StatusBarIconHolder holder : holders) {
-            int viewIndex = getViewIndex(slotIndex, holder.getTag());
-            mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible());
-            mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
-        }
-    }
-
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println(TAG + " state:");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 6b6ea10..2727b30 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -89,7 +89,6 @@
     protected boolean mFirstUpdate = true;
     protected boolean mLastShowing;
     protected boolean mLastOccluded;
-    private boolean mLastTracking;
     private boolean mLastBouncerShowing;
     private boolean mLastBouncerDismissible;
     protected boolean mLastRemoteInputActive;
@@ -152,28 +151,19 @@
         // • The user quickly taps on the display and we show "swipe up to unlock."
         // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
         // • Full-screen user switcher is displayed.
-        final boolean noLongerTracking = mLastTracking != tracking && !tracking;
         if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
                 || mBouncer.willDismissWithAction()
                 || mStatusBar.isFullScreenUserSwitcherState()) {
             mBouncer.setExpansion(0);
         } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
             mBouncer.setExpansion(expansion);
-            if (expansion == 1) {
-                mBouncer.onFullyHidden();
-            } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
+            if (expansion != 1 && tracking && !mBouncer.isShowing()
+                    && !mBouncer.isAnimatingAway()) {
                 mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
-            } else if (noLongerTracking) {
-                // Notify that falsing manager should stop its session when user stops touching,
-                // even before the animation ends, to guarantee that we're not recording sensitive
-                // data.
-                mBouncer.onFullyShown();
-            }
-            if (expansion == 0 || expansion == 1) {
+            } else if (expansion == 0 || expansion == 1) {
                 updateStates();
             }
         }
-        mLastTracking = tracking;
     }
 
     /**
@@ -595,6 +585,11 @@
         if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
             mStatusBarWindowManager.setBouncerShowing(bouncerShowing);
             mStatusBar.setBouncerShowing(bouncerShowing);
+            if (bouncerShowing) {
+                mBouncer.onFullyShown();
+            } else {
+                mBouncer.onFullyHidden();
+            }
         }
 
         KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index c5a3a0d..94ac4f62 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -414,7 +414,7 @@
 
         @Override public String toString() {
             return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
-                    + roaming + ", visible=" + visible + ")";
+                    + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 4c92d01..baeaaad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -40,6 +40,7 @@
 import android.view.View;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.FontSizeUtils;
@@ -84,6 +85,17 @@
     private boolean mShowSeconds;
     private Handler mSecondsHandler;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
+     * for text.
+     */
+    private boolean mUseWallpaperTextColor;
+
+    /**
+     * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
+     */
+    private int mNonAdaptedColor;
+
     public Clock(Context context) {
         this(context, null);
     }
@@ -101,6 +113,7 @@
         try {
             mAmPmStyle = a.getInt(R.styleable.Clock_amPmStyle, AM_PM_STYLE_GONE);
             mShowDark = a.getBoolean(R.styleable.Clock_showDark, true);
+            mNonAdaptedColor = getCurrentTextColor();
         } finally {
             a.recycle();
         }
@@ -227,7 +240,10 @@
 
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
-        setTextColor(DarkIconDispatcher.getTint(area, this, tint));
+        mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
+        if (!mUseWallpaperTextColor) {
+            setTextColor(mNonAdaptedColor);
+        }
     }
 
     @Override
@@ -242,6 +258,25 @@
                 0);
     }
 
+    /**
+     * Sets whether the clock uses the wallpaperTextColor. If we're not using it, we'll revert back
+     * to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
+            return;
+        }
+        mUseWallpaperTextColor = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColor) {
+            setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
+        } else {
+            setTextColor(mNonAdaptedColor);
+        }
+    }
+
     private void updateShowSeconds() {
         if (mShowSeconds) {
             // Wait until we have a display to start trying to show seconds.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 74a30fa..ef630c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -27,6 +27,7 @@
 import android.util.AttributeSet;
 import android.widget.TextView;
 
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 
@@ -42,6 +43,17 @@
     private String mLastText;
     private String mDatePattern;
 
+    /**
+     * Whether we should use colors that adapt based on wallpaper/the scrim behind quick settings
+     * for text.
+     */
+    private boolean mUseWallpaperTextColor;
+
+    /**
+     * Color to be set on this {@link TextView}, when wallpaperTextColor is <b>not</b> utilized.
+     */
+    private int mNonAdaptedTextColor;
+
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -62,6 +74,7 @@
 
     public DateView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mNonAdaptedTextColor = getCurrentTextColor();
         TypedArray a = context.getTheme().obtainStyledAttributes(
                 attrs,
                 R.styleable.DateView,
@@ -117,6 +130,25 @@
         }
     }
 
+    /**
+     * Sets whether the date view uses the wallpaperTextColor. If we're not using it, we'll revert
+     * back to dark-mode-based/tinted colors.
+     *
+     * @param shouldUseWallpaperTextColor whether we should use wallpaperTextColor for text color
+     */
+    public void useWallpaperTextColor(boolean shouldUseWallpaperTextColor) {
+        if (shouldUseWallpaperTextColor == mUseWallpaperTextColor) {
+            return;
+        }
+        mUseWallpaperTextColor = shouldUseWallpaperTextColor;
+
+        if (mUseWallpaperTextColor) {
+            setTextColor(Utils.getColorAttr(mContext, R.attr.wallpaperTextColor));
+        } else {
+            setTextColor(mNonAdaptedTextColor);
+        }
+    }
+
     public void setDatePattern(String pattern) {
         if (TextUtils.equals(pattern, mDatePattern)) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 2fed3fc..1b02e15 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -28,7 +28,6 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.SystemClock;
-import android.os.VibrationEffect;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.HapticFeedbackConstants;
@@ -50,7 +49,6 @@
 import com.android.systemui.R;
 import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.VibratorHelper;
 
 import static android.view.KeyEvent.KEYCODE_HOME;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
@@ -75,7 +73,6 @@
     private OnClickListener mOnClickListener;
     private final KeyButtonRipple mRipple;
     private final OverviewProxyService mOverviewProxyService;
-    private final VibratorHelper mVibratorHelper;
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
 
     private final Runnable mCheckLongPress = new Runnable() {
@@ -121,7 +118,6 @@
         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
 
         mRipple = new KeyButtonRipple(context, this);
-        mVibratorHelper = Dependency.get(VibratorHelper.class);
         mOverviewProxyService = Dependency.get(OverviewProxyService.class);
         setBackground(mRipple);
     }
@@ -262,9 +258,8 @@
                 final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
                 if (showSwipeUI) {
                     if (doIt) {
-                        if (doHapticFeedback) {
-                            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
-                        }
+                        // Apply haptic feedback on touch up since there is none on touch down
+                        performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                         playSoundEffect(SoundEffectConstants.CLICK);
                     }
                 } else if (doHapticFeedback && !mLongClicked) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
index 790135f..74b3926 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -20,8 +20,10 @@
 import android.widget.Button;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
 import java.util.Comparator;
@@ -42,6 +44,7 @@
     private static final int SQUEEZE_FAILED = -1;
 
     private final SmartReplyConstants mConstants;
+    private final KeyguardDismissUtil mKeyguardDismissUtil;
 
     /** Spacing to be applied between views. */
     private final int mSpacing;
@@ -62,6 +65,7 @@
     public SmartReplyView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mConstants = Dependency.get(SmartReplyConstants.class);
+        mKeyguardDismissUtil = Dependency.get(KeyguardDismissUtil.class);
 
         int spacing = 0;
         int singleLineButtonPaddingHorizontal = 0;
@@ -126,12 +130,13 @@
     }
 
     @VisibleForTesting
-    static Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
+    Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
             RemoteInput remoteInput, PendingIntent pendingIntent) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         b.setText(choice);
-        b.setOnClickListener(view -> {
+
+        OnDismissAction action = () -> {
             Bundle results = new Bundle();
             results.putString(remoteInput.getResultKey(), choice.toString());
             Intent intent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -142,6 +147,12 @@
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Unable to send smart reply", e);
             }
+            return false; // do not defer
+        };
+
+        b.setOnClickListener(view -> {
+            mKeyguardDismissUtil.dismissKeyguardThenExecute(
+                    action, null /* cancelAction */, false /* afterKeyguardGone */);
         });
         return b;
     }
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 da7dc07..d282f25 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3023,6 +3023,11 @@
     public void setAnimationsEnabled(boolean animationsEnabled) {
         mAnimationsEnabled = animationsEnabled;
         updateNotificationAnimationStates();
+        if (!animationsEnabled) {
+            mSwipedOutViews.clear();
+            mChildrenToRemoveAnimated.clear();
+            clearTemporaryViewsInGroup(this);
+        }
     }
 
     private void updateNotificationAnimationStates() {
@@ -3090,6 +3095,21 @@
     @Override
     public void changeViewPosition(View child, int newIndex) {
         int currentIndex = indexOfChild(child);
+
+        if (currentIndex == -1) {
+            boolean isTransient = false;
+            if (child instanceof ExpandableNotificationRow
+                    && ((ExpandableNotificationRow)child).getTransientContainer() != null) {
+                isTransient = true;
+            }
+            Log.e(TAG, "Attempting to re-position "
+                    + (isTransient ? "transient" : "")
+                    + " view {"
+                    + child
+                    + "}");
+            return;
+        }
+
         if (child != null && child.getParent() == this && currentIndex != newIndex) {
             mChangePositionInProgress = true;
             ((ExpandableView)child).setChangingPosition(true);
@@ -3569,17 +3589,17 @@
 
     private void clearTemporaryViews() {
         // lets make sure nothing is in the overlay / transient anymore
-        clearTemporaryViews(this);
+        clearTemporaryViewsInGroup(this);
         for (int i = 0; i < getChildCount(); i++) {
             ExpandableView child = (ExpandableView) getChildAt(i);
             if (child instanceof ExpandableNotificationRow) {
                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
-                clearTemporaryViews(row.getChildrenContainer());
+                clearTemporaryViewsInGroup(row.getChildrenContainer());
             }
         }
     }
 
-    private void clearTemporaryViews(ViewGroup viewGroup) {
+    private void clearTemporaryViewsInGroup(ViewGroup viewGroup) {
         while (viewGroup != null && viewGroup.getTransientViewCount() != 0) {
             viewGroup.removeTransientView(viewGroup.getTransientView(0));
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 67453d5..c6b6546 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -167,11 +167,19 @@
 
     @Test
     public void testOnFullyShown_notifiesFalsingManager() {
+        mBouncer.ensureView();
         mBouncer.onFullyShown();
         verify(mFalsingManager).onBouncerShown();
     }
 
     @Test
+    public void testOnFullyShown_notifiesKeyguardView() {
+        mBouncer.ensureView();
+        mBouncer.onFullyShown();
+        verify(mKeyguardHostView).onResume();
+    }
+
+    @Test
     public void testOnFullyHidden_notifiesFalsingManager() {
         mBouncer.onFullyHidden();
         verify(mFalsingManager).onBouncerHidden();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index a9b2c96..56882c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -18,6 +18,7 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
@@ -34,8 +35,12 @@
 import android.widget.Button;
 import android.widget.LinearLayout;
 
+import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+
+import java.util.concurrent.atomic.AtomicReference;
 
 import org.junit.After;
 import org.junit.Before;
@@ -65,6 +70,8 @@
     public void setUp() {
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> action.onDismiss());
 
         mView = SmartReplyView.inflate(mContext, null);
 
@@ -95,6 +102,42 @@
     }
 
     @Test
+    public void testSendSmartReply_keyguardCancelled() throws InterruptedException {
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> {
+                if (cancelAction != null) {
+                    cancelAction.run();
+                }
+            });
+        setRepliesFromRemoteInput(TEST_CHOICES);
+
+        mView.getChildAt(2).performClick();
+
+        assertNull(mReceiver.waitForIntent());
+    }
+
+    @Test
+    public void testSendSmartReply_waitsForKeyguard() throws InterruptedException {
+        AtomicReference<OnDismissAction> actionRef = new AtomicReference<>();
+        mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
+            (action, cancelAction, afterKeyguardGone) -> actionRef.set(action));
+        setRepliesFromRemoteInput(TEST_CHOICES);
+
+        mView.getChildAt(2).performClick();
+
+        // No intent until the screen is unlocked.
+        assertNull(mReceiver.waitForIntent());
+
+        actionRef.get().onDismiss();
+
+        // Now the intent should arrive.
+        Intent resultIntent = mReceiver.waitForIntent();
+        assertEquals(TEST_CHOICES[2],
+                RemoteInput.getResultsFromIntent(resultIntent).get(TEST_RESULT_KEY));
+        assertEquals(RemoteInput.SOURCE_CHOICE, RemoteInput.getResultsSource(resultIntent));
+    }
+
+    @Test
     public void testMeasure_empty() {
         mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
         assertEquals(500, mView.getMeasuredWidthAndState());
@@ -301,7 +344,7 @@
 
         Button previous = null;
         for (CharSequence choice : choices) {
-            Button current = SmartReplyView.inflateReplyButton(mContext, mView, choice, null, null);
+            Button current = mView.inflateReplyButton(mContext, mView, choice, null, null);
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..bd52901
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..bd52901
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..bd52901
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..bd52901
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
new file mode 100644
index 0000000..bd52901
--- /dev/null
+++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values-land/config.xml
@@ -0,0 +1,22 @@
+<!--
+  ~ 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>
+    <!-- Can't link to other dimensions here, but this should be status_bar_height_landscape -->
+    <dimen name="quick_qs_offset_height">28dp</dimen>
+    <!-- Total height of QQS in landscape; quick_qs_offset_height + 128 -->
+    <dimen name="quick_qs_total_height">156dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index c66c7b0..a3dcd45 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -5572,6 +5572,12 @@
     // OS: P
     ACTION_SETTINGS_SLICE_CHANGED = 1372;
 
+    // OPEN: Settings > Network & Internet > Wi-Fi > Wi-Fi Preferences > Turn on Wi-Fi automatically
+    //       note: Wifi Scanning must be off for this dialog to show
+    // CATEGORY: SETTINGS
+    // OS: P
+    WIFI_SCANNING_NEEDED_DIALOG = 1373;
+
     // ---- 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/backup/java/com/android/server/backup/transport/TransportClient.java b/services/backup/java/com/android/server/backup/transport/TransportClient.java
index fa881a9..e4dcb25 100644
--- a/services/backup/java/com/android/server/backup/transport/TransportClient.java
+++ b/services/backup/java/com/android/server/backup/transport/TransportClient.java
@@ -439,8 +439,17 @@
         synchronized (mStateLock) {
             log(Priority.ERROR, "Service disconnected: client UNUSABLE");
             setStateLocked(State.UNUSABLE, null);
-            // After unbindService() no calls back to mConnection
-            mContext.unbindService(mConnection);
+            try {
+                // After unbindService() no calls back to mConnection
+                mContext.unbindService(mConnection);
+            } catch (IllegalArgumentException e) {
+                // TODO: Investigate why this is happening
+                // We're UNUSABLE, so any calls to mConnection will be no-op, so it's safe to
+                // swallow this one
+                log(
+                        Priority.WARN,
+                        "Exception trying to unbind onServiceDisconnected(): " + e.getMessage());
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a1ef1ed..59cb135 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -934,7 +934,7 @@
 
     // Used only for testing.
     // TODO: Delete this and either:
-    // 1. Give Fake SettingsProvider the ability to send settings change notifications (requires
+    // 1. Give FakeSettingsProvider the ability to send settings change notifications (requires
     //    changing ContentResolver to make registerContentObserver non-final).
     // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it
     //    by subclassing SettingsObserver.
@@ -943,6 +943,12 @@
         mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON);
     }
 
+    // See FakeSettingsProvider comment above.
+    @VisibleForTesting
+    void updatePrivateDnsSettings() {
+        mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+    }
+
     private void handleMobileDataAlwaysOn() {
         final boolean enable = toBool(Settings.Global.getInt(
                 mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1));
@@ -972,8 +978,8 @@
     }
 
     private void registerPrivateDnsSettingsCallbacks() {
-        for (Uri u : DnsManager.getPrivateDnsSettingsUris()) {
-            mSettingsObserver.observe(u, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
+        for (Uri uri : DnsManager.getPrivateDnsSettingsUris()) {
+            mSettingsObserver.observe(uri, EVENT_PRIVATE_DNS_SETTINGS_CHANGED);
         }
     }
 
@@ -1026,8 +1032,12 @@
         if (network == null) {
             return null;
         }
+        return getNetworkAgentInfoForNetId(network.netId);
+    }
+
+    private NetworkAgentInfo getNetworkAgentInfoForNetId(int netId) {
         synchronized (mNetworkForNetId) {
-            return mNetworkForNetId.get(network.netId);
+            return mNetworkForNetId.get(netId);
         }
     }
 
@@ -1167,9 +1177,7 @@
         }
         NetworkAgentInfo nai;
         if (vpnNetId != NETID_UNSET) {
-            synchronized (mNetworkForNetId) {
-                nai = mNetworkForNetId.get(vpnNetId);
-            }
+            nai = getNetworkAgentInfoForNetId(vpnNetId);
             if (nai != null) return nai.network;
         }
         nai = getDefaultNetwork();
@@ -2155,41 +2163,21 @@
                 default:
                     return false;
                 case NetworkMonitor.EVENT_NETWORK_TESTED: {
-                    final NetworkAgentInfo nai;
-                    synchronized (mNetworkForNetId) {
-                        nai = mNetworkForNetId.get(msg.arg2);
-                    }
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
 
                     final boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
                     final boolean wasValidated = nai.lastValidated;
                     final boolean wasDefault = isDefaultNetwork(nai);
 
-                    final PrivateDnsConfig privateDnsCfg = (msg.obj instanceof PrivateDnsConfig)
-                            ? (PrivateDnsConfig) msg.obj : null;
                     final String redirectUrl = (msg.obj instanceof String) ? (String) msg.obj : "";
 
-                    final boolean reevaluationRequired;
-                    final String logMsg;
-                    if (valid) {
-                        reevaluationRequired = updatePrivateDns(nai, privateDnsCfg);
-                        logMsg = (DBG && (privateDnsCfg != null))
-                                 ? " with " + privateDnsCfg.toString() : "";
-                    } else {
-                        reevaluationRequired = false;
-                        logMsg = (DBG && !TextUtils.isEmpty(redirectUrl))
-                                 ? " with redirect to " + redirectUrl : "";
-                    }
                     if (DBG) {
+                        final String logMsg = !TextUtils.isEmpty(redirectUrl)
+                                 ? " with redirect to " + redirectUrl
+                                 : "";
                         log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
                     }
-                    // If there is a change in Private DNS configuration,
-                    // trigger reevaluation of the network to test it.
-                    if (reevaluationRequired) {
-                        nai.networkMonitor.sendMessage(
-                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
-                        break;
-                    }
                     if (valid != nai.lastValidated) {
                         if (wasDefault) {
                             metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
@@ -2218,10 +2206,7 @@
                 case NetworkMonitor.EVENT_PROVISIONING_NOTIFICATION: {
                     final int netId = msg.arg2;
                     final boolean visible = toBool(msg.arg1);
-                    final NetworkAgentInfo nai;
-                    synchronized (mNetworkForNetId) {
-                        nai = mNetworkForNetId.get(netId);
-                    }
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
                     // If captive portal status has changed, update capabilities or disconnect.
                     if (nai != null && (visible != nai.lastCaptivePortalDetected)) {
                         final int oldScore = nai.getCurrentScore();
@@ -2252,18 +2237,10 @@
                     break;
                 }
                 case NetworkMonitor.EVENT_PRIVATE_DNS_CONFIG_RESOLVED: {
-                    final NetworkAgentInfo nai;
-                    synchronized (mNetworkForNetId) {
-                        nai = mNetworkForNetId.get(msg.arg2);
-                    }
+                    final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2);
                     if (nai == null) break;
 
-                    final PrivateDnsConfig cfg = (PrivateDnsConfig) msg.obj;
-                    final boolean reevaluationRequired = updatePrivateDns(nai, cfg);
-                    if (nai.lastValidated && reevaluationRequired) {
-                        nai.networkMonitor.sendMessage(
-                                NetworkMonitor.CMD_FORCE_REEVALUATION, Process.SYSTEM_UID);
-                    }
+                    updatePrivateDns(nai, (PrivateDnsConfig) msg.obj);
                     break;
                 }
             }
@@ -2301,61 +2278,38 @@
         }
     }
 
+    private boolean networkRequiresValidation(NetworkAgentInfo nai) {
+        return NetworkMonitor.isValidationRequired(
+                mDefaultRequest.networkCapabilities, nai.networkCapabilities);
+    }
+
     private void handlePrivateDnsSettingsChanged() {
         final PrivateDnsConfig cfg = mDnsManager.getPrivateDnsConfig();
 
         for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
-            // Private DNS only ever applies to networks that might provide
-            // Internet access and therefore also require validation.
-            if (!NetworkMonitor.isValidationRequired(
-                    mDefaultRequest.networkCapabilities, nai.networkCapabilities)) {
-                continue;
-            }
-
-            // Notify the NetworkMonitor thread in case it needs to cancel or
-            // schedule DNS resolutions. If a DNS resolution is required the
-            // result will be sent back to us.
-            nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
-
-            if (!cfg.inStrictMode()) {
-                // No strict mode hostname DNS resolution needed, so just update
-                // DNS settings directly. In opportunistic and "off" modes this
-                // just reprograms netd with the network-supplied DNS servers
-                // (and of course the boolean of whether or not to attempt TLS).
-                //
-                // TODO: Consider code flow parity with strict mode, i.e. having
-                // NetworkMonitor relay the PrivateDnsConfig back to us and then
-                // performing this call at that time.
-                updatePrivateDns(nai, cfg);
-            }
+            handlePerNetworkPrivateDnsConfig(nai, cfg);
         }
     }
 
-    private boolean updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
-        final boolean reevaluationRequired = true;
-        final boolean dontReevaluate = false;
+    private void handlePerNetworkPrivateDnsConfig(NetworkAgentInfo nai, PrivateDnsConfig cfg) {
+        // Private DNS only ever applies to networks that might provide
+        // Internet access and therefore also require validation.
+        if (!networkRequiresValidation(nai)) return;
 
-        final PrivateDnsConfig oldCfg = mDnsManager.updatePrivateDns(nai.network, newCfg);
+        // Notify the NetworkMonitor thread in case it needs to cancel or
+        // schedule DNS resolutions. If a DNS resolution is required the
+        // result will be sent back to us.
+        nai.networkMonitor.notifyPrivateDnsSettingsChanged(cfg);
+
+        // With Private DNS bypass support, we can proceed to update the
+        // Private DNS config immediately, even if we're in strict mode
+        // and have not yet resolved the provider name into a set of IPs.
+        updatePrivateDns(nai, cfg);
+    }
+
+    private void updatePrivateDns(NetworkAgentInfo nai, PrivateDnsConfig newCfg) {
+        mDnsManager.updatePrivateDns(nai.network, newCfg);
         updateDnses(nai.linkProperties, null, nai.network.netId);
-
-        if (newCfg == null) {
-            if (oldCfg == null) return dontReevaluate;
-            return oldCfg.useTls ? reevaluationRequired : dontReevaluate;
-        }
-
-        if (oldCfg == null) {
-            return newCfg.useTls ? reevaluationRequired : dontReevaluate;
-        }
-
-        if (oldCfg.useTls != newCfg.useTls) {
-            return reevaluationRequired;
-        }
-
-        if (newCfg.inStrictMode() && !Objects.equals(oldCfg.hostname, newCfg.hostname)) {
-            return reevaluationRequired;
-        }
-
-        return dontReevaluate;
     }
 
     private void updateLingerState(NetworkAgentInfo nai, long now) {
@@ -3300,7 +3254,7 @@
         if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
             return;
         }
-        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
+        nai.networkMonitor.forceReevaluation(uid);
     }
 
     private ProxyInfo getDefaultProxy() {
@@ -4919,7 +4873,7 @@
     }
 
     public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
-        if (mNetworkForNetId.get(nai.network.netId) != nai) {
+        if (getNetworkAgentInfoForNetId(nai.network.netId) != nai) {
             // Ignore updates for disconnected networks
             return;
         }
@@ -5495,6 +5449,7 @@
         if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
             networkAgent.everConnected = true;
 
+            handlePerNetworkPrivateDnsConfig(networkAgent, mDnsManager.getPrivateDnsConfig());
             updateLinkProperties(networkAgent, null);
             notifyIfacesChangedForNetworkStats();
 
@@ -5911,4 +5866,4 @@
             pw.println("    Get airplane mode.");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f57e9ac..067566d 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2283,7 +2283,7 @@
                         + " app=" + app);
             if (app != null && app.thread != null) {
                 try {
-                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
+                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode, mAm.mProcessStats);
                     realStartServiceLocked(r, app, execInFg);
                     return null;
                 } catch (TransactionTooLargeException e) {
@@ -2645,6 +2645,8 @@
                 Message msg = mAm.mHandler.obtainMessage(
                         ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
                 msg.obj = r.app;
+                msg.getData().putCharSequence(
+                    ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
                 mAm.mHandler.sendMessage(msg);
             }
         }
@@ -3009,7 +3011,7 @@
 
                     mPendingServices.remove(i);
                     i--;
-                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
+                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.longVersionCode,
                             mAm.mProcessStats);
                     realStartServiceLocked(sr, proc, sr.createdFromFg);
                     didSomething = true;
@@ -3563,13 +3565,15 @@
 
         if (app != null) {
             mAm.mAppErrors.appNotResponding(app, null, null, false,
-                    "Context.startForegroundService() did not then call Service.startForeground()");
+                    "Context.startForegroundService() did not then call Service.startForeground(): "
+                        + r);
         }
     }
 
-    void serviceForegroundCrash(ProcessRecord app) {
+    void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
         mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
-                "Context.startForegroundService() did not then call Service.startForeground()");
+                "Context.startForegroundService() did not then call Service.startForeground(): "
+                    + serviceRecord);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2e87a44..e0820e7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1920,6 +1920,8 @@
     static final int FIRST_COMPAT_MODE_MSG = 300;
     static final int FIRST_SUPERVISOR_STACK_MSG = 100;
 
+    static final String SERVICE_RECORD_KEY = "servicerecord";
+
     static ServiceThread sKillThread = null;
     static KillHandler sKillHandler = null;
 
@@ -2168,7 +2170,8 @@
                 mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
             } break;
             case SERVICE_FOREGROUND_CRASH_MSG: {
-                mServices.serviceForegroundCrash((ProcessRecord)msg.obj);
+                mServices.serviceForegroundCrash(
+                    (ProcessRecord) msg.obj, msg.getData().getCharSequence(SERVICE_RECORD_KEY));
             } break;
             case DISPATCH_PENDING_INTENT_CANCEL_MSG: {
                 RemoteCallbackList<IResultReceiver> callbacks
@@ -21898,8 +21901,6 @@
         "com.android.frameworks.locationtests",
         "com.android.frameworks.coretests.privacy",
         "com.android.settings.ui",
-        "com.android.perftests.core",
-        "com.android.perftests.multiuser",
     };
 
     public boolean startInstrumentation(ComponentName className,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 1af4114..e539bf8 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -93,8 +93,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
 import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
 import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -154,6 +152,7 @@
 import android.graphics.Bitmap;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
@@ -2880,7 +2879,7 @@
 
         final ActivityManagerService service = stackSupervisor.mService;
         final ActivityInfo aInfo = stackSupervisor.resolveActivity(intent, resolvedType, 0, null,
-                userId);
+                userId, Binder.getCallingUid());
         if (aInfo == null) {
             throw new XmlPullParserException("restoreActivity resolver error. Intent=" + intent +
                     " resolvedType=" + resolvedType);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index ea089e0..e9a1358 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1264,10 +1264,11 @@
     }
 
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
-        return resolveIntent(intent, resolvedType, userId, 0);
+        return resolveIntent(intent, resolvedType, userId, 0, Binder.getCallingUid());
     }
 
-    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
+    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
+            int filterCallingUid) {
         synchronized (mService) {
             try {
                 Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
@@ -1278,7 +1279,7 @@
                     modifiedFlags |= PackageManager.MATCH_INSTANT;
                 }
                 return mService.getPackageManagerInternalLocked().resolveIntent(
-                        intent, resolvedType, modifiedFlags, userId, true);
+                        intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
 
             } finally {
                 Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -1287,8 +1288,8 @@
     }
 
     ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
-            ProfilerInfo profilerInfo, int userId) {
-        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId);
+            ProfilerInfo profilerInfo, int userId, int filterCallingUid) {
+        final ResolveInfo rInfo = resolveIntent(intent, resolvedType, userId, 0, filterCallingUid);
         return resolveActivity(intent, rInfo, startFlags, profilerInfo);
     }
 
@@ -1579,7 +1580,7 @@
                     // to run in multiple processes, because this is actually
                     // part of the framework so doesn't make sense to track as a
                     // separate apk in the process.
-                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
+                    app.addPackage(r.info.packageName, r.info.applicationInfo.longVersionCode,
                             mService.mProcessStats);
                 }
                 realStartActivityLocked(r, app, andResume, checkConfig);
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index fb78838..86a3fce 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -23,7 +23,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.ALLOW_FULL_ONLY;
 
-import android.app.ActivityOptions;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -33,7 +32,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.Binder;
-import android.os.Bundle;
 import android.os.FactoryTest;
 import android.os.Handler;
 import android.os.IBinder;
@@ -322,7 +320,7 @@
 
                     // Collect information about the target of the Intent.
                     ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0,
-                            null, userId);
+                            null, userId, realCallingUid);
                     // TODO: New, check if this is correct
                     aInfo = mService.getActivityInfoForUser(aInfo, userId);
 
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 2ac389d..6ca8a92 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -32,8 +32,10 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
+import android.Manifest;
 import android.app.ActivityOptions;
-import android.app.AppGlobals;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.Context;
@@ -41,6 +43,7 @@
 import android.content.Intent;
 import android.content.IntentSender;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.os.Binder;
@@ -51,6 +54,7 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.HarmfulAppWarningActivity;
+import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.server.LocalServices;
 
@@ -151,7 +155,7 @@
         mInTask = inTask;
         mActivityOptions = activityOptions;
 
-        if (interceptSuspendPackageIfNeed()) {
+        if (interceptSuspendedPackageIfNeeded()) {
             // Skip the rest of interceptions as the package is suspended by device admin so
             // no user action can undo this.
             return true;
@@ -204,12 +208,7 @@
         return true;
     }
 
-    private boolean interceptSuspendPackageIfNeed() {
-        // Do not intercept if the admin did not suspend the package
-        if (mAInfo == null || mAInfo.applicationInfo == null ||
-                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
-            return false;
-        }
+    private boolean interceptSuspendedByAdminPackage() {
         DevicePolicyManagerInternal devicePolicyManager = LocalServices
                 .getService(DevicePolicyManagerInternal.class);
         if (devicePolicyManager == null) {
@@ -232,6 +231,55 @@
         return true;
     }
 
+    private Intent createSuspendedAppInterceptIntent(String suspendedPackage,
+            String suspendingPackage, String dialogMessage, int userId) {
+        final Intent interceptIntent = new Intent(mServiceContext, SuspendedAppActivity.class)
+                .putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
+                .putExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE, dialogMessage)
+                .putExtra(Intent.EXTRA_USER_ID, userId)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+        final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS)
+                .setPackage(suspendingPackage);
+        final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
+        final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId);
+        if (resolvedInfo != null && resolvedInfo.activityInfo != null
+                && requiredPermission.equals(resolvedInfo.activityInfo.permission)) {
+            moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            interceptIntent.putExtra(SuspendedAppActivity.EXTRA_MORE_DETAILS_INTENT,
+                    moreDetailsIntent);
+        }
+        return interceptIntent;
+    }
+
+    private boolean interceptSuspendedPackageIfNeeded() {
+        // Do not intercept if the package is not suspended
+        if (mAInfo == null || mAInfo.applicationInfo == null ||
+                (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
+            return false;
+        }
+        final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
+        if (pmi == null) {
+            return false;
+        }
+        final String suspendedPackage = mAInfo.applicationInfo.packageName;
+        final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
+        if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
+            return interceptSuspendedByAdminPackage();
+        }
+        final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId);
+        mIntent = createSuspendedAppInterceptIntent(suspendedPackage, suspendingPackage,
+                dialogMessage, mUserId);
+        mCallingPid = mRealCallingPid;
+        mCallingUid = mRealCallingUid;
+        mResolvedType = null;
+        mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, 0);
+        mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+        return true;
+    }
+
     private boolean interceptWorkProfileChallengeIfNeeded() {
         final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
         if (interceptingIntent == null) {
@@ -293,9 +341,9 @@
     private boolean interceptHarmfulAppIfNeeded() {
         CharSequence harmfulAppWarning;
         try {
-            harmfulAppWarning = AppGlobals.getPackageManager().getHarmfulAppWarning(
-                    mAInfo.packageName, mUserId);
-        } catch (RemoteException e) {
+            harmfulAppWarning = mService.getPackageManager()
+                    .getHarmfulAppWarning(mAInfo.packageName, mUserId);
+        } catch (RemoteException ex) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 1b7e1ed..5337566 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -28,10 +28,10 @@
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.content.Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
@@ -903,14 +903,22 @@
         final int clearTaskFlags = FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK;
         boolean clearedTask = (mLaunchFlags & clearTaskFlags) == clearTaskFlags
                 && mReuseTask != null;
-        if (startedActivityStack.inPinnedWindowingMode()
-                && (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP
-                || clearedTask)) {
-            // The activity was already running in the pinned stack so it wasn't started, but either
-            // brought to the front or the new intent was delivered to it since it was already in
-            // front. Notify anyone interested in this piece of information.
-            mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
-                    clearedTask);
+        if (result == START_TASK_TO_FRONT || result == START_DELIVERED_TO_TOP || clearedTask) {
+            // The activity was already running so it wasn't started, but either brought to the
+            // front or the new intent was delivered to it since it was already in front. Notify
+            // anyone interested in this piece of information.
+            switch (startedActivityStack.getWindowingMode()) {
+                case WINDOWING_MODE_PINNED:
+                    mService.mTaskChangeNotificationController.notifyPinnedActivityRestartAttempt(
+                            clearedTask);
+                    break;
+                case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
+                    final ActivityStack homeStack = mSupervisor.mHomeStack;
+                    if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
+                        mService.mWindowManager.showRecentApps();
+                    }
+                    break;
+            }
         }
     }
 
@@ -966,7 +974,8 @@
                 if (profileLockedAndParentUnlockingOrUnlocked) {
                     rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
                             PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+                                    | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                            Binder.getCallingUid());
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 03acb84..b7fde1d 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -496,7 +496,7 @@
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
-        pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
+        pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.longVersionCode));
         maxAdj = ProcessList.UNKNOWN_ADJ;
         curRawAdj = setRawAdj = ProcessList.INVALID_ADJ;
         curAdj = setAdj = verifiedAdj = ProcessList.INVALID_ADJ;
@@ -521,7 +521,7 @@
                 origBase.makeInactive();
             }
             baseProcessTracker = tracker.getProcessStateLocked(info.packageName, uid,
-                    info.versionCode, processName);
+                    info.longVersionCode, processName);
             baseProcessTracker.makeActive();
             for (int i=0; i<pkgList.size(); i++) {
                 ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
@@ -529,7 +529,7 @@
                     holder.state.makeInactive();
                 }
                 holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), uid,
-                        info.versionCode, processName);
+                        info.longVersionCode, processName);
                 if (holder.state != baseProcessTracker) {
                     holder.state.makeActive();
                 }
@@ -828,9 +828,9 @@
                 }
                 pkgList.clear();
                 ProcessState ps = tracker.getProcessStateLocked(
-                        info.packageName, uid, info.versionCode, processName);
+                        info.packageName, uid, info.longVersionCode, processName);
                 ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
-                        info.versionCode);
+                        info.longVersionCode);
                 holder.state = ps;
                 pkgList.put(info.packageName, holder);
                 if (ps != baseProcessTracker) {
@@ -839,7 +839,7 @@
             }
         } else if (N != 1) {
             pkgList.clear();
-            pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
+            pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.longVersionCode));
         }
     }
 
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 5579849..2a361a0 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -61,6 +61,51 @@
  * This class it NOT designed for concurrent access. Furthermore, all non-static
  * methods MUST be called from ConnectivityService's thread.
  *
+ * [ Private DNS ]
+ * The code handling Private DNS is spread across several components, but this
+ * seems like the least bad place to collect all the observations.
+ *
+ * Private DNS handling and updating occurs in response to several different
+ * events. Each is described here with its corresponding intended handling.
+ *
+ * [A] Event: A new network comes up.
+ * Mechanics:
+ *     [1] ConnectivityService gets notifications from NetworkAgents.
+ *     [2] in updateNetworkInfo(), the first time the NetworkAgent goes into
+ *         into CONNECTED state, the Private DNS configuration is retrieved,
+ *         programmed, and strict mode hostname resolution (if applicable) is
+ *         enqueued in NetworkAgent's NetworkMonitor, via a call to
+ *         handlePerNetworkPrivateDnsConfig().
+ *     [3] Re-resolution of strict mode hostnames that fail to return any
+ *         IP addresses happens inside NetworkMonitor; it sends itself a
+ *         delayed CMD_EVALUATE_PRIVATE_DNS message in a simple backoff
+ *         schedule.
+ *     [4] Successfully resolved hostnames are sent to ConnectivityService
+ *         inside an EVENT_PRIVATE_DNS_CONFIG_RESOLVED message. The resolved
+ *         IP addresses are programmed into netd via:
+ *
+ *             updatePrivateDns() -> updateDnses()
+ *
+ *         both of which make calls into DnsManager.
+ *     [5] Upon a successful hostname resolution NetworkMonitor initiates a
+ *         validation attempt in the form of a lookup for a one-time hostname
+ *         that uses Private DNS.
+ *
+ * [B] Event: Private DNS settings are changed.
+ * Mechanics:
+ *     [1] ConnectivityService gets notifications from its SettingsObserver.
+ *     [2] handlePrivateDnsSettingsChanged() is called, which calls
+ *         handlePerNetworkPrivateDnsConfig() and the process proceeds
+ *         as if from A.3 above.
+ *
+ * [C] Event: An application calls ConnectivityManager#reportBadNetwork().
+ * Mechanics:
+ *     [1] NetworkMonitor is notified and initiates a reevaluation, which
+ *         always bypasses Private DNS.
+ *     [2] Once completed, NetworkMonitor checks if strict mode is in operation
+ *         and if so enqueues another evaluation of Private DNS, as if from
+ *         step A.5 above.
+ *
  * @hide
  */
 public class DnsManager {
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 8a2e71c..2845383 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -34,6 +34,7 @@
 import android.net.ProxyInfo;
 import android.net.TrafficStats;
 import android.net.Uri;
+import android.net.dns.ResolvUtil;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.metrics.ValidationProbeEvent;
@@ -64,6 +65,7 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
 
 import java.io.IOException;
 import java.net.HttpURLConnection;
@@ -77,6 +79,7 @@
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Random;
+import java.util.UUID;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -165,7 +168,7 @@
      * Force evaluation even if it has succeeded in the past.
      * arg1 = UID responsible for requesting this reeval.  Will be billed for data.
      */
-    public static final int CMD_FORCE_REEVALUATION = BASE + 8;
+    private static final int CMD_FORCE_REEVALUATION = BASE + 8;
 
     /**
      * Message to self indicating captive portal app finished.
@@ -205,9 +208,15 @@
      * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in
      * strict mode, then an event is sent back to ConnectivityService with the
      * result of the resolution attempt.
+     *
+     * A separate message is used to trigger (re)evaluation of the Private DNS
+     * configuration, so that the message can be handled as needed in different
+     * states, including being ignored until after an ongoing captive portal
+     * validation phase is completed.
      */
     private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = BASE + 13;
     public static final int EVENT_PRIVATE_DNS_CONFIG_RESOLVED = BASE + 14;
+    private static final int CMD_EVALUATE_PRIVATE_DNS = BASE + 15;
 
     // Start mReevaluateDelayMs at this value and double.
     private static final int INITIAL_REEVALUATE_DELAY_MS = 1000;
@@ -215,6 +224,7 @@
     // Before network has been evaluated this many times, ignore repeated reevaluate requests.
     private static final int IGNORE_REEVALUATE_ATTEMPTS = 5;
     private int mReevaluateToken = 0;
+    private static final int NO_UID = 0;
     private static final int INVALID_UID = -1;
     private int mUidResponsibleForReeval = INVALID_UID;
     // Stop blaming UID that requested re-evaluation after this many attempts.
@@ -224,6 +234,8 @@
 
     private static final int NUM_VALIDATION_LOG_LINES = 20;
 
+    private String mPrivateDnsProviderHostname = "";
+
     public static boolean isValidationRequired(
             NetworkCapabilities dfltNetCap, NetworkCapabilities nc) {
         // TODO: Consider requiring validation for DUN networks.
@@ -261,13 +273,12 @@
 
     public boolean systemReady = false;
 
-    private DnsManager.PrivateDnsConfig mPrivateDnsCfg = null;
-
     private final State mDefaultState = new DefaultState();
     private final State mValidatedState = new ValidatedState();
     private final State mMaybeNotifyState = new MaybeNotifyState();
     private final State mEvaluatingState = new EvaluatingState();
     private final State mCaptivePortalState = new CaptivePortalState();
+    private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState();
 
     private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
 
@@ -293,6 +304,10 @@
         // Add suffix indicating which NetworkMonitor we're talking about.
         super(TAG + networkAgentInfo.name());
 
+        // Logs with a tag of the form given just above, e.g.
+        //     <timestamp>   862  2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ...
+        setDbg(VDBG);
+
         mContext = context;
         mMetricsLog = logger;
         mConnectivityServiceHandler = handler;
@@ -305,10 +320,11 @@
         mDefaultRequest = defaultRequest;
 
         addState(mDefaultState);
-        addState(mValidatedState, mDefaultState);
         addState(mMaybeNotifyState, mDefaultState);
             addState(mEvaluatingState, mMaybeNotifyState);
             addState(mCaptivePortalState, mMaybeNotifyState);
+        addState(mEvaluatingPrivateDnsState, mDefaultState);
+        addState(mValidatedState, mDefaultState);
         setInitialState(mDefaultState);
 
         mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
@@ -321,6 +337,17 @@
         start();
     }
 
+    public void forceReevaluation(int responsibleUid) {
+        sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0);
+    }
+
+    public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) {
+        // Cancel any outstanding resolutions.
+        removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
+        // Send the update to the proper thread.
+        sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
+    }
+
     @Override
     protected void log(String s) {
         if (DBG) Log.d(TAG + "/" + mNetworkAgentInfo.name(), s);
@@ -349,6 +376,12 @@
                 mDefaultRequest.networkCapabilities, mNetworkAgentInfo.networkCapabilities);
     }
 
+
+    private void notifyNetworkTestResultInvalid(Object obj) {
+        mConnectivityServiceHandler.sendMessage(obtainMessage(
+                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId, obj));
+    }
+
     // DefaultState is the parent of all States.  It exists only to handle CMD_* messages but
     // does not entail any real state (hence no enter() or exit() routines).
     private class DefaultState extends State {
@@ -392,41 +425,66 @@
 
                     switch (message.arg1) {
                         case APP_RETURN_DISMISSED:
-                            sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */, 0);
+                            sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0);
                             break;
                         case APP_RETURN_WANTED_AS_IS:
                             mDontDisplaySigninNotification = true;
                             // TODO: Distinguish this from a network that actually validates.
-                            // Displaying the "!" on the system UI icon may still be a good idea.
-                            transitionTo(mValidatedState);
+                            // Displaying the "x" on the system UI icon may still be a good idea.
+                            transitionTo(mEvaluatingPrivateDnsState);
                             break;
                         case APP_RETURN_UNWANTED:
                             mDontDisplaySigninNotification = true;
                             mUserDoesNotWant = true;
-                            mConnectivityServiceHandler.sendMessage(obtainMessage(
-                                    EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID,
-                                    mNetId, null));
+                            notifyNetworkTestResultInvalid(null);
                             // TODO: Should teardown network.
                             mUidResponsibleForReeval = 0;
                             transitionTo(mEvaluatingState);
                             break;
                     }
                     return HANDLED;
-                case CMD_PRIVATE_DNS_SETTINGS_CHANGED:
-                    if (isValidationRequired()) {
-                        // This performs a blocking DNS resolution of the
-                        // strict mode hostname, if required.
-                        resolvePrivateDnsConfig((DnsManager.PrivateDnsConfig) message.obj);
-                        if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode()) {
-                            mConnectivityServiceHandler.sendMessage(obtainMessage(
-                                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId,
-                                    new DnsManager.PrivateDnsConfig(mPrivateDnsCfg)));
-                        }
+                case CMD_PRIVATE_DNS_SETTINGS_CHANGED: {
+                    final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj;
+                    if (!isValidationRequired() || cfg == null || !cfg.inStrictMode()) {
+                        // No DNS resolution required.
+                        //
+                        // We don't force any validation in opportunistic mode
+                        // here. Opportunistic mode nameservers are validated
+                        // separately within netd.
+                        //
+                        // Reset Private DNS settings state.
+                        mPrivateDnsProviderHostname = "";
+                        break;
                     }
-                    return HANDLED;
+
+                    mPrivateDnsProviderHostname = cfg.hostname;
+
+                    // DNS resolutions via Private DNS strict mode block for a
+                    // few seconds (~4.2) checking for any IP addresses to
+                    // arrive and validate. Initiating a (re)evaluation now
+                    // should not significantly alter the validation outcome.
+                    //
+                    // No matter what: enqueue a validation request; one of
+                    // three things can happen with this request:
+                    //     [1] ignored (EvaluatingState or CaptivePortalState)
+                    //     [2] transition to EvaluatingPrivateDnsState
+                    //         (DefaultState and ValidatedState)
+                    //     [3] handled (EvaluatingPrivateDnsState)
+                    //
+                    // The Private DNS configuration to be evaluated will:
+                    //     [1] be skipped (not in strict mode), or
+                    //     [2] validate (huzzah), or
+                    //     [3] encounter some problem (invalid hostname,
+                    //         no resolved IP addresses, IPs unreachable,
+                    //         port 853 unreachable, port 853 is not running a
+                    //         DNS-over-TLS server, et cetera).
+                    sendMessage(CMD_EVALUATE_PRIVATE_DNS);
+                    break;
+                }
                 default:
-                    return HANDLED;
+                    break;
             }
+            return HANDLED;
         }
     }
 
@@ -440,7 +498,7 @@
             maybeLogEvaluationResult(
                     networkEventType(validationStage(), EvaluationResult.VALIDATED));
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                    NETWORK_TEST_RESULT_VALID, mNetId, mPrivateDnsCfg));
+                    NETWORK_TEST_RESULT_VALID, mNetId, null));
             mValidations++;
         }
 
@@ -449,10 +507,14 @@
             switch (message.what) {
                 case CMD_NETWORK_CONNECTED:
                     transitionTo(mValidatedState);
-                    return HANDLED;
+                    break;
+                case CMD_EVALUATE_PRIVATE_DNS:
+                    transitionTo(mEvaluatingPrivateDnsState);
+                    break;
                 default:
                     return NOT_HANDLED;
             }
+            return HANDLED;
         }
     }
 
@@ -569,11 +631,11 @@
                 case CMD_REEVALUATE:
                     if (message.arg1 != mReevaluateToken || mUserDoesNotWant)
                         return HANDLED;
-                    // Don't bother validating networks that don't satisify the default request.
+                    // Don't bother validating networks that don't satisfy the default request.
                     // This includes:
                     //  - VPNs which can be considered explicitly desired by the user and the
                     //    user's desire trumps whether the network validates.
-                    //  - Networks that don't provide internet access.  It's unclear how to
+                    //  - Networks that don't provide Internet access.  It's unclear how to
                     //    validate such networks.
                     //  - Untrusted networks.  It's unsafe to prompt the user to sign-in to
                     //    such networks and the user didn't express interest in connecting to
@@ -588,7 +650,6 @@
                     //    expensive metered network, or unwanted leaking of the User Agent string.
                     if (!isValidationRequired()) {
                         validationLog("Network would not satisfy default request, not validating");
-                        mPrivateDnsCfg = null;
                         transitionTo(mValidatedState);
                         return HANDLED;
                     }
@@ -601,20 +662,18 @@
                     // if this is found to cause problems.
                     CaptivePortalProbeResult probeResult = isCaptivePortal();
                     if (probeResult.isSuccessful()) {
-                        resolvePrivateDnsConfig();
-                        transitionTo(mValidatedState);
+                        // Transit EvaluatingPrivateDnsState to get to Validated
+                        // state (even if no Private DNS validation required).
+                        transitionTo(mEvaluatingPrivateDnsState);
                     } else if (probeResult.isPortal()) {
-                        mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
-                                NETWORK_TEST_RESULT_INVALID, mNetId, probeResult.redirectUrl));
+                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
                         mLastPortalProbeResult = probeResult;
                         transitionTo(mCaptivePortalState);
                     } else {
                         final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);
                         sendMessageDelayed(msg, mReevaluateDelayMs);
                         logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED);
-                        mConnectivityServiceHandler.sendMessage(obtainMessage(
-                                EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, mNetId,
-                                probeResult.redirectUrl));
+                        notifyNetworkTestResultInvalid(probeResult.redirectUrl);
                         if (mAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) {
                             // Don't continue to blame UID forever.
                             TrafficStats.clearThreadStatsUid();
@@ -700,6 +759,110 @@
         }
     }
 
+    private class EvaluatingPrivateDnsState extends State {
+        private int mPrivateDnsReevalDelayMs;
+        private PrivateDnsConfig mPrivateDnsConfig;
+
+        @Override
+        public void enter() {
+            mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS;
+            mPrivateDnsConfig = null;
+            sendMessage(CMD_EVALUATE_PRIVATE_DNS);
+        }
+
+        @Override
+        public boolean processMessage(Message msg) {
+            switch (msg.what) {
+                case CMD_EVALUATE_PRIVATE_DNS:
+                    if (inStrictMode()) {
+                        if (!isStrictModeHostnameResolved()) {
+                            resolveStrictModeHostname();
+
+                            if (isStrictModeHostnameResolved()) {
+                                notifyPrivateDnsConfigResolved();
+                            } else {
+                                handlePrivateDnsEvaluationFailure();
+                                break;
+                            }
+                        }
+
+                        // Look up a one-time hostname, to bypass caching.
+                        //
+                        // Note that this will race with ConnectivityService
+                        // code programming the DNS-over-TLS server IP addresses
+                        // into netd (if invoked, above). If netd doesn't know
+                        // the IP addresses yet, or if the connections to the IP
+                        // addresses haven't yet been validated, netd will block
+                        // for up to a few seconds before failing the lookup.
+                        if (!sendPrivateDnsProbe()) {
+                            handlePrivateDnsEvaluationFailure();
+                            break;
+                        }
+                    }
+
+                    // All good!
+                    transitionTo(mValidatedState);
+                    break;
+                default:
+                    return NOT_HANDLED;
+            }
+            return HANDLED;
+        }
+
+        private boolean inStrictMode() {
+            return !TextUtils.isEmpty(mPrivateDnsProviderHostname);
+        }
+
+        private boolean isStrictModeHostnameResolved() {
+            return (mPrivateDnsConfig != null) &&
+                   mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) &&
+                   (mPrivateDnsConfig.ips.length > 0);
+        }
+
+        private void resolveStrictModeHostname() {
+            try {
+                // Do a blocking DNS resolution using the network-assigned nameservers.
+                mPrivateDnsConfig = new PrivateDnsConfig(
+                        mPrivateDnsProviderHostname,
+                        mNetwork.getAllByName(mPrivateDnsProviderHostname));
+            } catch (UnknownHostException uhe) {
+                mPrivateDnsConfig = null;
+            }
+        }
+
+        private void notifyPrivateDnsConfigResolved() {
+            mConnectivityServiceHandler.sendMessage(obtainMessage(
+                    EVENT_PRIVATE_DNS_CONFIG_RESOLVED, 0, mNetId, mPrivateDnsConfig));
+        }
+
+        private void handlePrivateDnsEvaluationFailure() {
+            notifyNetworkTestResultInvalid(null);
+
+            // Queue up a re-evaluation with backoff.
+            //
+            // TODO: Consider abandoning this state after a few attempts and
+            // transitioning back to EvaluatingState, to perhaps give ourselves
+            // the opportunity to (re)detect a captive portal or something.
+            sendMessageDelayed(CMD_EVALUATE_PRIVATE_DNS, mPrivateDnsReevalDelayMs);
+            mPrivateDnsReevalDelayMs *= 2;
+            if (mPrivateDnsReevalDelayMs > MAX_REEVALUATE_DELAY_MS) {
+                mPrivateDnsReevalDelayMs = MAX_REEVALUATE_DELAY_MS;
+            }
+        }
+
+        private boolean sendPrivateDnsProbe() {
+            // q.v. system/netd/server/dns/DnsTlsTransport.cpp
+            final String ONE_TIME_HOSTNAME_SUFFIX = "-dnsotls-ds.metric.gstatic.com";
+            final String host = UUID.randomUUID().toString().substring(0, 8) +
+                    ONE_TIME_HOSTNAME_SUFFIX;
+            try {
+                final InetAddress[] ips = mNetworkAgentInfo.network().getAllByName(host);
+                return (ips != null && ips.length > 0);
+            } catch (UnknownHostException uhe) {}
+            return false;
+        }
+    }
+
     // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
     // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
     // to complete, regardless of how many IP addresses a host has.
@@ -710,7 +873,9 @@
 
         @Override
         public InetAddress[] getAllByName(String host) throws UnknownHostException {
-            List<InetAddress> addrs = Arrays.asList(super.getAllByName(host));
+            // Always bypass Private DNS.
+            final List<InetAddress> addrs = Arrays.asList(
+                    ResolvUtil.blockingResolveAllLocally(this, host));
 
             // Ensure the address family of the first address is tried first.
             LinkedHashMap<Class, InetAddress> addressByFamily = new LinkedHashMap<>();
@@ -1065,44 +1230,6 @@
         return null;
     }
 
-    public void notifyPrivateDnsSettingsChanged(DnsManager.PrivateDnsConfig newCfg) {
-        // Cancel any outstanding resolutions.
-        removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED);
-        // Send the update to the proper thread.
-        sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg);
-    }
-
-    private void resolvePrivateDnsConfig() {
-        resolvePrivateDnsConfig(DnsManager.getPrivateDnsConfig(mContext.getContentResolver()));
-    }
-
-    private void resolvePrivateDnsConfig(DnsManager.PrivateDnsConfig cfg) {
-        // Nothing to do.
-        if (cfg == null) {
-            mPrivateDnsCfg = null;
-            return;
-        }
-
-        // No DNS resolution required.
-        if (!cfg.inStrictMode()) {
-            mPrivateDnsCfg = cfg;
-            return;
-        }
-
-        if ((mPrivateDnsCfg != null) && mPrivateDnsCfg.inStrictMode() &&
-                (mPrivateDnsCfg.ips.length > 0) && mPrivateDnsCfg.hostname.equals(cfg.hostname)) {
-            // We have already resolved this strict mode hostname. Assume that
-            // Private DNS services won't be changing serving IP addresses very
-            // frequently and save ourselves one re-resolve.
-            return;
-        }
-
-        mPrivateDnsCfg = cfg;
-        final DnsManager.PrivateDnsConfig resolvedCfg = DnsManager.tryBlockingResolveOf(
-                mNetwork, mPrivateDnsCfg.hostname);
-        if (resolvedCfg != null) mPrivateDnsCfg = resolvedCfg;
-    }
-
     /**
      * @param responseReceived - whether or not we received a valid HTTP response to our request.
      * If false, isCaptivePortal and responseTimestampMs are ignored
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c7ae1f4..93e0bd5 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -61,12 +61,15 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.IntArray;
 import android.util.Slog;
@@ -1030,7 +1033,7 @@
     }
 
     private void setBrightnessConfigurationForUserInternal(
-            @NonNull BrightnessConfiguration c, @UserIdInt int userId,
+            @Nullable BrightnessConfiguration c, @UserIdInt int userId,
             @Nullable String packageName) {
         final int userSerial = getUserManager().getUserSerialNumber(userId);
         synchronized (mSyncRoot) {
@@ -1983,6 +1986,29 @@
             }
         }
 
+        @Override // Binder call
+        public void onShellCommand(FileDescriptor in, FileDescriptor out,
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                DisplayManagerShellCommand command = new DisplayManagerShellCommand(this);
+                command.exec(this, in, out, err, args, callback, resultReceiver);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        void setBrightness(int brightness) {
+            Settings.System.putIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
+        }
+
+        void resetBrightnessConfiguration() {
+            setBrightnessConfigurationForUserInternal(null, mContext.getUserId(),
+                    mContext.getPackageName());
+        }
+
         private boolean validatePackageName(int uid, String packageName) {
             if (packageName != null) {
                 String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
new file mode 100644
index 0000000..27cad1e
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -0,0 +1,92 @@
+/*
+ * 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.display;
+
+import android.content.Intent;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.os.ShellCommand;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.lang.NumberFormatException;
+
+class DisplayManagerShellCommand extends ShellCommand {
+    private static final String TAG = "DisplayManagerShellCommand";
+
+    private final DisplayManagerService.BinderService mService;
+
+    DisplayManagerShellCommand(DisplayManagerService.BinderService service) {
+        mService = service;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch(cmd) {
+            case "set-brightness":
+                return setBrightness();
+            case "reset-brightness-configuration":
+                return resetBrightnessConfiguration();
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("Display manager commands:");
+        pw.println("  help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("  set-brightness BRIGHTNESS");
+        pw.println("    Sets the current brightness to BRIGHTNESS (a number between 0 and 1).");
+        pw.println("  reset-brightness-configuration");
+        pw.println("    Reset the brightness to its default configuration.");
+        pw.println();
+        Intent.printIntentArgsHelp(pw , "");
+    }
+
+    private int setBrightness() {
+        String brightnessText = getNextArg();
+        if (brightnessText == null) {
+            getErrPrintWriter().println("Error: no brightness specified");
+            return 1;
+        }
+        float brightness = -1;
+        try {
+            brightness = Float.parseFloat(brightnessText);
+        } catch (NumberFormatException e) {
+        }
+        if (brightness < 0 || brightness > 1) {
+            getErrPrintWriter().println("Error: brightness should be a number between 0 and 1");
+            return 1;
+        }
+        mService.setBrightness((int) brightness * 255);
+        return 0;
+    }
+
+    private int resetBrightnessConfiguration() {
+        mService.resetBrightnessConfiguration();
+        return 0;
+    }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 5ca9abc..b9a279a 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -436,6 +436,10 @@
                                 com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
                     }
+
+                    if (res.getBoolean(com.android.internal.R.bool.config_localDisplaysPrivate)) {
+                        mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
+                    }
                 }
             }
             return mInfo;
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 8be2c9e..afd1a94 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
-import android.hardware.biometrics.BiometricDialog;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -46,8 +46,8 @@
     public static final int LOCKOUT_PERMANENT = 2;
 
     // Callback mechanism received from the client
-    // (BiometricDialog -> FingerprintManager -> FingerprintService -> AuthenticationClient)
-    private IBiometricDialogReceiver mDialogReceiverFromClient;
+    // (BiometricPrompt -> FingerprintManager -> FingerprintService -> AuthenticationClient)
+    private IBiometricPromptReceiver mDialogReceiverFromClient;
     private Bundle mBundle;
     private IStatusBarService mStatusBarService;
     private boolean mInLockout;
@@ -55,13 +55,13 @@
     protected boolean mDialogDismissed;
 
     // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
-    protected IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
+    protected IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
         @Override // binder call
         public void onDialogDismissed(int reason) {
             if (mBundle != null && mDialogReceiverFromClient != null) {
                 try {
                     mDialogReceiverFromClient.onDialogDismissed(reason);
-                    if (reason == BiometricDialog.DISMISSED_REASON_USER_CANCEL) {
+                    if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
                         onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
                                 0 /* vendorCode */);
                     }
@@ -88,7 +88,7 @@
     public AuthenticationClient(Context context, long halDeviceId, IBinder token,
             IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
             boolean restricted, String owner, Bundle bundle,
-            IBiometricDialogReceiver dialogReceiver, IStatusBarService statusBarService) {
+            IBiometricPromptReceiver dialogReceiver, IStatusBarService statusBarService) {
         super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
         mOpId = opId;
         mBundle = bundle;
@@ -299,7 +299,7 @@
             // If the user already cancelled authentication (via some interaction with the
             // dialog, we do not need to hide it since it's already hidden.
             // If the device is in lockout, don't hide the dialog - it will automatically hide
-            // after BiometricDialog.HIDE_DIALOG_DELAY
+            // after BiometricPrompt.HIDE_DIALOG_DELAY
             if (mBundle != null && !mDialogDismissed && !mInLockout) {
                 try {
                     mStatusBarService.hideFingerprintDialog();
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 92d3772..4e95bdf 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -38,7 +38,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
@@ -849,7 +849,7 @@
 
     private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
                 IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-                String opPackageName, Bundle bundle, IBiometricDialogReceiver dialogReceiver) {
+                String opPackageName, Bundle bundle, IBiometricPromptReceiver dialogReceiver) {
         updateActiveGroup(groupId, opPackageName);
 
         if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
@@ -1160,7 +1160,7 @@
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName, final Bundle bundle,
-                final IBiometricDialogReceiver dialogReceiver) {
+                final IBiometricPromptReceiver dialogReceiver) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5ba7380..58bca19 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -83,7 +83,11 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.location.gnssmetrics.GnssMetrics;
+import com.android.server.location.GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback;
 import com.android.server.location.NtpTimeHelper.InjectNtpTimeCallback;
+
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -99,14 +103,13 @@
 import java.util.Map.Entry;
 import java.util.Properties;
 
-import libcore.io.IoUtils;
-
 /**
  * A GNSS implementation of LocationProvider used by LocationManager.
  *
  * {@hide}
  */
-public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback {
+public class GnssLocationProvider implements LocationProviderInterface, InjectNtpTimeCallback,
+        GnssSatelliteBlacklistCallback {
 
     private static final String TAG = "GnssLocationProvider";
 
@@ -308,7 +311,7 @@
         }
     }
 
-    private Object mLock = new Object();
+    private final Object mLock = new Object();
 
     // current status
     private int mStatus = LocationProvider.TEMPORARILY_UNAVAILABLE;
@@ -411,6 +414,7 @@
     private final ILocationManager mILocationManager;
     private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mListenerHelper;
+    private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
     private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener();
@@ -577,6 +581,16 @@
                 }
             };
 
+    /**
+     * Implements {@link GnssSatelliteBlacklistCallback#onUpdateSatelliteBlacklist}.
+     */
+    @Override
+    public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
+        mHandler.post(()->{
+            native_set_satellite_blacklist(constellations, svids);
+        });
+    }
+
     private void subscriptionOrSimChanged(Context context) {
         if (DEBUG) Log.d(TAG, "received SIM related action: ");
         TelephonyManager phone = (TelephonyManager)
@@ -869,7 +883,10 @@
         };
         mGnssMetrics = new GnssMetrics(mBatteryStats);
 
-        mNtpTimeHelper = new NtpTimeHelper(mContext, Looper.myLooper(), this);
+        mNtpTimeHelper = new NtpTimeHelper(mContext, looper, this);
+        mGnssSatelliteBlacklistHelper = new GnssSatelliteBlacklistHelper(mContext,
+                looper, this);
+        mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
     }
 
     /**
@@ -2900,6 +2917,8 @@
 
     private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
 
+    private static native boolean native_set_satellite_blacklist(int[] constellations, int[] svIds);
+
     // GNSS Batching
     private static native int native_get_batch_size();
 
diff --git a/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java b/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java
new file mode 100644
index 0000000..77951aa
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssSatelliteBlacklistHelper.java
@@ -0,0 +1,102 @@
+package com.android.server.location;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Detects blacklist change and updates the blacklist.
+ */
+class GnssSatelliteBlacklistHelper {
+
+    private static final String TAG = "GnssBlacklistHelper";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final String BLACKLIST_DELIMITER = ",";
+
+    private final Context mContext;
+    private final GnssSatelliteBlacklistCallback mCallback;
+
+    interface GnssSatelliteBlacklistCallback {
+        void onUpdateSatelliteBlacklist(int[] constellations, int[] svids);
+    }
+
+    GnssSatelliteBlacklistHelper(Context context, Looper looper,
+            GnssSatelliteBlacklistCallback callback) {
+        mContext = context;
+        mCallback = callback;
+        ContentObserver contentObserver = new ContentObserver(new Handler(looper)) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateSatelliteBlacklist();
+            }
+        };
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(
+                        Settings.Global.GNSS_SATELLITE_BLACKLIST),
+                true,
+                contentObserver, UserHandle.USER_ALL);
+    }
+
+    void updateSatelliteBlacklist() {
+        ContentResolver resolver = mContext.getContentResolver();
+        String blacklist = Settings.Global.getString(
+                resolver,
+                Settings.Global.GNSS_SATELLITE_BLACKLIST);
+        if (blacklist == null) {
+            blacklist = "";
+        }
+        if (DEBUG) {
+            Log.d(TAG, String.format("Update GNSS satellite blacklist: %s", blacklist));
+        }
+
+        List<Integer> blacklistValues;
+        try {
+            blacklistValues = parseSatelliteBlacklist(blacklist);
+        } catch (NumberFormatException e) {
+            Log.e(TAG, "Exception thrown when parsing blacklist string.", e);
+            return;
+        }
+
+        if (blacklistValues.size() % 2 != 0) {
+            Log.e(TAG, "blacklist string has odd number of values."
+                    + "Aborting updateSatelliteBlacklist");
+            return;
+        }
+
+        int length = blacklistValues.size() / 2;
+        int[] constellations = new int[length];
+        int[] svids = new int[length];
+        for (int i = 0; i < length; i++) {
+            constellations[i] = blacklistValues.get(i * 2);
+            svids[i] = blacklistValues.get(i * 2 + 1);
+        }
+        mCallback.onUpdateSatelliteBlacklist(constellations, svids);
+    }
+
+    @VisibleForTesting
+    static List<Integer> parseSatelliteBlacklist(String blacklist) throws NumberFormatException {
+        String[] strings = blacklist.split(BLACKLIST_DELIMITER);
+        List<Integer> parsed = new ArrayList<>(strings.length);
+        for (String string : strings) {
+            string = string.trim();
+            if (!"".equals(string)) {
+                int value = Integer.parseInt(string);
+                if (value < 0) {
+                    throw new NumberFormatException("Negative value is invalid.");
+                }
+                parsed.add(value);
+            }
+        }
+        return parsed;
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index a26581b..26d4037 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -274,6 +274,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IMediaContainerService;
 import com.android.internal.app.ResolverActivity;
+import com.android.internal.app.SuspendedAppActivity;
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.PackageHelper;
 import com.android.internal.logging.MetricsLogger;
@@ -3946,7 +3947,7 @@
             ai.uid = UserHandle.getUid(userId, ps.appId);
             ai.primaryCpuAbi = ps.primaryCpuAbiString;
             ai.secondaryCpuAbi = ps.secondaryCpuAbiString;
-            ai.versionCode = ps.versionCode;
+            ai.setVersionCode(ps.versionCode);
             ai.flags = ps.pkgFlags;
             ai.privateFlags = ps.pkgPrivateFlags;
             pi.applicationInfo = PackageParser.generateApplicationInfo(ai, flags, state, userId);
@@ -5943,8 +5944,8 @@
     @Override
     public ResolveInfo resolveIntent(Intent intent, String resolvedType,
             int flags, int userId) {
-        return resolveIntentInternal(
-                intent, resolvedType, flags, userId, false /*resolveForStart*/);
+        return resolveIntentInternal(intent, resolvedType, flags, userId, false,
+                Binder.getCallingUid());
     }
 
     /**
@@ -5953,19 +5954,19 @@
      * since we need to allow the system to start any installed application.
      */
     private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
-            int flags, int userId, boolean resolveForStart) {
+            int flags, int userId, boolean resolveForStart, int filterCallingUid) {
         try {
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
 
             if (!sUserManager.exists(userId)) return null;
             final int callingUid = Binder.getCallingUid();
-            flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
+            flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart);
             mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                     false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
             final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
-                    flags, callingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
+                    flags, filterCallingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
 
             final ResolveInfo bestChoice =
@@ -6773,7 +6774,7 @@
                 // the instant application, we'll do the right thing.
                 final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
                 auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
-                                        ai.packageName, ai.versionCode, null /* splitName */);
+                                        ai.packageName, ai.longVersionCode, null /* splitName */);
             }
         }
         if (intent.isWebIntent() && auxiliaryResponse == null) {
@@ -6957,7 +6958,7 @@
                 installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                         installFailureActivity,
                         info.activityInfo.packageName,
-                        info.activityInfo.applicationInfo.versionCode,
+                        info.activityInfo.applicationInfo.longVersionCode,
                         info.activityInfo.splitName);
                 // add a non-generic filter
                 installerInfo.filter = new IntentFilter();
@@ -7703,7 +7704,7 @@
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                             null /* installFailureActivity */,
                             info.serviceInfo.packageName,
-                            info.serviceInfo.applicationInfo.versionCode,
+                            info.serviceInfo.applicationInfo.longVersionCode,
                             info.serviceInfo.splitName);
                     // add a non-generic filter
                     installerInfo.filter = new IntentFilter();
@@ -7821,7 +7822,7 @@
                     installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                             null /*failureActivity*/,
                             info.providerInfo.packageName,
-                            info.providerInfo.applicationInfo.versionCode,
+                            info.providerInfo.applicationInfo.longVersionCode,
                             info.providerInfo.splitName);
                     // add a non-generic filter
                     installerInfo.filter = new IntentFilter();
@@ -8700,7 +8701,7 @@
                             disabledPkgSetting /* pkgSetting */, null /* disabledPkgSetting */,
                             null /* originalPkgSetting */, null, parseFlags, scanFlags,
                             (pkg == mPlatformPackage), user);
-                    applyPolicy(pkg, parseFlags, scanFlags);
+                    applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
                     scanPackageOnlyLI(request, mFactoryTest, -1L);
                 }
             }
@@ -10019,7 +10020,7 @@
 
         scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, pkg);
         synchronized (mPackages) {
-            applyPolicy(pkg, parseFlags, scanFlags);
+            applyPolicy(pkg, parseFlags, scanFlags, mPlatformPackage);
             assertPackageIsValid(pkg, parseFlags, scanFlags);
 
             SharedUserSetting sharedUserSetting = null;
@@ -10699,7 +10700,7 @@
      * ideally be static, but, it requires locks to read system state.
      */
     private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags) {
+            final @ScanFlags int scanFlags, PackageParser.Package platformPkg) {
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
             if (pkg.applicationInfo.isDirectBootAware()) {
@@ -10785,6 +10786,15 @@
             pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRODUCT;
         }
 
+        // Check if the package is signed with the same key as the platform package.
+        if (PLATFORM_PACKAGE_NAME.equals(pkg.packageName) ||
+                (platformPkg != null && compareSignatures(
+                        platformPkg.mSigningDetails.signatures,
+                        pkg.mSigningDetails.signatures) == PackageManager.SIGNATURE_MATCH)) {
+            pkg.applicationInfo.privateFlags |=
+                ApplicationInfo.PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY;
+        }
+
         if (!isSystemApp(pkg)) {
             // Only system apps can use these features.
             pkg.mOriginalPackages = null;
@@ -13997,8 +14007,8 @@
 
     @Override
     public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
-            PersistableBundle appExtras, PersistableBundle launcherExtras, String callingPackage,
-            int userId) {
+            PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
+            String callingPackage, int userId) {
         try {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null);
         } catch (SecurityException e) {
@@ -14046,7 +14056,7 @@
                             unactionedPackages.add(packageName);
                             continue;
                         }
-                        pkgSetting.setSuspended(suspended, callingPackage, appExtras,
+                        pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
                                 launcherExtras, userId);
                         changedPackagesList.add(packageName);
                     }
@@ -14168,7 +14178,7 @@
                 for (int user : userIds) {
                     final PackageUserState pus = ps.readUserState(user);
                     if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
-                        ps.setSuspended(false, null, null, null, user);
+                        ps.setSuspended(false, null, null, null, null, user);
                     }
                 }
             }
@@ -18902,6 +18912,7 @@
                     false /*hidden*/,
                     false /*suspended*/,
                     null, /*suspendingPackage*/
+                    null, /*dialogMessage*/
                     null, /*suspendedAppExtras*/
                     null, /*suspendedLauncherExtras*/
                     false /*instantApp*/,
@@ -23794,6 +23805,22 @@
         }
 
         @Override
+        public String getSuspendingPackage(String suspendedPackage, int userId) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
+                return (ps != null) ? ps.readUserState(userId).suspendingPackage : null;
+            }
+        }
+
+        @Override
+        public String getSuspendedDialogMessage(String suspendedPackage, int userId) {
+            synchronized (mPackages) {
+                final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
+                return (ps != null) ? ps.readUserState(userId).dialogMessage : null;
+            }
+        }
+
+        @Override
         public int getPackageUid(String packageName, int flags, int userId) {
             return PackageManagerService.this
                     .getPackageUid(packageName, flags, userId);
@@ -24015,9 +24042,9 @@
 
         @Override
         public ResolveInfo resolveIntent(Intent intent, String resolvedType,
-                int flags, int userId, boolean resolveForStart) {
+                int flags, int userId, boolean resolveForStart, int filterCallingUid) {
             return resolveIntentInternal(
-                    intent, resolvedType, flags, userId, resolveForStart);
+                    intent, resolvedType, flags, userId, resolveForStart, filterCallingUid);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 28e32a5..a92fbb6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1505,6 +1505,7 @@
     private int runSuspend(boolean suspendedState) {
         final PrintWriter pw = getOutPrintWriter();
         int userId = UserHandle.USER_SYSTEM;
+        String dialogMessage = null;
         final PersistableBundle appExtras = new PersistableBundle();
         final PersistableBundle launcherExtras = new PersistableBundle();
         String opt;
@@ -1513,6 +1514,9 @@
                 case "--user":
                     userId = UserHandle.parseUserArg(getNextArgRequired());
                     break;
+                case "--dialogMessage":
+                    dialogMessage = getNextArgRequired();
+                    break;
                 case "--ael":
                 case "--aes":
                 case "--aed":
@@ -1553,7 +1557,7 @@
                 (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
         try {
             mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
-                    appExtras, launcherExtras, callingPackage, userId);
+                    appExtras, launcherExtras, dialogMessage, callingPackage, userId);
             pw.println("Package " + packageName + " new suspended state: "
                     + mInterface.isPackageSuspendedForUser(packageName, userId));
             return 0;
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 008a81c..fd4c5e9 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -398,11 +398,12 @@
         return readUserState(userId).suspended;
     }
 
-    void setSuspended(boolean suspended, String suspendingPackage, PersistableBundle appExtras,
-            PersistableBundle launcherExtras, int userId) {
+    void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
+            PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
         final PackageUserState existingUserState = modifyUserState(userId);
         existingUserState.suspended = suspended;
         existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
+        existingUserState.dialogMessage = suspended ? dialogMessage : null;
         existingUserState.suspendedAppExtras = suspended ? appExtras : null;
         existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
     }
@@ -425,8 +426,8 @@
 
     void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
             boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
-            PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras,
-            boolean instantApp,
+            String dialogMessage, PersistableBundle suspendedAppExtras,
+            PersistableBundle suspendedLauncherExtras, boolean instantApp,
             boolean virtualPreload, String lastDisableAppCaller,
             ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
             int domainVerifState, int linkGeneration, int installReason,
@@ -440,6 +441,7 @@
         state.hidden = hidden;
         state.suspended = suspended;
         state.suspendingPackage = suspendingPackage;
+        state.dialogMessage = dialogMessage;
         state.suspendedAppExtras = suspendedAppExtras;
         state.suspendedLauncherExtras = suspendedLauncherExtras;
         state.lastDisableAppCaller = lastDisableAppCaller;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d0e8544..898ecf3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -222,6 +222,7 @@
     private static final String ATTR_HIDDEN = "hidden";
     private static final String ATTR_SUSPENDED = "suspended";
     private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
+    private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message";
     // Legacy, uninstall blocks are stored separately.
     @Deprecated
     private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -734,6 +735,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 null, /*suspendingPackage*/
+                                null, /*dialogMessage*/
                                 null, /*suspendedAppExtras*/
                                 null, /*suspendedLauncherExtras*/
                                 instantApp,
@@ -1628,6 +1630,7 @@
                                 false /*hidden*/,
                                 false /*suspended*/,
                                 null, /*suspendingPackage*/
+                                null, /*dialogMessage*/
                                 null, /*suspendedAppExtras*/
                                 null, /*suspendedLauncherExtras*/
                                 false /*instantApp*/,
@@ -1704,6 +1707,8 @@
                             false);
                     String suspendingPackage = parser.getAttributeValue(null,
                             ATTR_SUSPENDING_PACKAGE);
+                    final String dialogMessage = parser.getAttributeValue(null,
+                            ATTR_SUSPEND_DIALOG_MESSAGE);
                     if (suspended && suspendingPackage == null) {
                         suspendingPackage = PLATFORM_PACKAGE_NAME;
                     }
@@ -1767,7 +1772,7 @@
                         setBlockUninstallLPw(userId, name, true);
                     }
                     ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
-                            hidden, suspended, suspendingPackage, suspendedAppExtras,
+                            hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
                             suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
                             enabledComponents, disabledComponents, verifState, linkGeneration,
                             installReason, harmfulAppWarning);
@@ -2077,7 +2082,14 @@
                 }
                 if (ustate.suspended) {
                     serializer.attribute(null, ATTR_SUSPENDED, "true");
-                    serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, ustate.suspendingPackage);
+                    if (ustate.suspendingPackage != null) {
+                        serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
+                                ustate.suspendingPackage);
+                    }
+                    if (ustate.dialogMessage != null) {
+                        serializer.attribute(null, ATTR_SUSPEND_DIALOG_MESSAGE,
+                                ustate.dialogMessage);
+                    }
                     if (ustate.suspendedAppExtras != null) {
                         serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
                         try {
@@ -4750,8 +4762,11 @@
             pw.print(" suspended=");
             pw.print(ps.getSuspended(user.id));
             if (ps.getSuspended(user.id)) {
+                final PackageUserState pus = ps.readUserState(user.id);
                 pw.print(" suspendingPackage=");
-                pw.print(ps.readUserState(user.id).suspendingPackage);
+                pw.print(pus.suspendingPackage);
+                pw.print(" dialogMessage=");
+                pw.print(pus.dialogMessage);
             }
             pw.print(" stopped=");
             pw.print(ps.getStopped(user.id));
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 518d464..4055a47 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1052,7 +1052,8 @@
     private PackageParser.Package getDefaultSystemHandlerActivityPackage(
             Intent intent, int userId) {
         ResolveInfo handler = mServiceInternal.resolveIntent(intent,
-                intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false);
+                intent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS, userId, false,
+                Binder.getCallingUid());
         if (handler == null || handler.activityInfo == null) {
             return null;
         }
@@ -1093,7 +1094,7 @@
 
             ResolveInfo homeActivity = mServiceInternal.resolveIntent(homeIntent,
                     homeIntent.resolveType(mContext.getContentResolver()), DEFAULT_FLAGS,
-                    userId, false);
+                    userId, false, Binder.getCallingUid());
             if (homeActivity != null) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d4625e9..5f2ac4f 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -273,7 +273,8 @@
 
         // Add in all the apps for every user/profile.
         for (UserInfo profile : users) {
-            List<PackageInfo> pi = pm.getInstalledPackagesAsUser(0, profile.id);
+            List<PackageInfo> pi =
+                pm.getInstalledPackagesAsUser(PackageManager.MATCH_DISABLED_COMPONENTS, profile.id);
             for (int j = 0; j < pi.size(); j++) {
                 if (pi.get(j).applicationInfo != null) {
                     uids.add(pi.get(j).applicationInfo.uid);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8af1101..2db8039 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -23,7 +23,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.biometrics.IBiometricDialogReceiver;
+import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -547,7 +547,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricPromptReceiver receiver) {
         if (mBar != null) {
             try {
                 mBar.showFingerprintDialog(bundle, receiver);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0771b53..79eb2c9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3728,8 +3728,12 @@
 
             mLastWindowForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
-            if (policy.isKeyguardShowingAndNotOccluded()
-                    || mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE) {
+            // Only allow force setting the orientation when all unknown visibilities have been
+            // resolved, as otherwise we just may be starting another occluding activity.
+            final boolean isUnoccluding =
+                    mService.mAppTransition.getAppTransition() == TRANSIT_KEYGUARD_UNOCCLUDE
+                            && mService.mUnknownAppVisibilityController.allResolved();
+            if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
                 return mLastKeyguardForcedOrientation;
             }
 
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 21fea1c..e18eee2 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -81,6 +81,7 @@
 using android::hardware::Void;
 using android::hardware::hidl_vec;
 using android::hardware::hidl_death_recipient;
+using android::hardware::gnss::V1_0::GnssConstellationType;
 using android::hardware::gnss::V1_0::GnssLocation;
 using android::hardware::gnss::V1_0::GnssLocationFlags;
 
@@ -91,7 +92,6 @@
 using android::hardware::gnss::V1_0::IAGnssRilCallback;
 using android::hardware::gnss::V1_0::IGnssBatching;
 using android::hardware::gnss::V1_0::IGnssBatchingCallback;
-using android::hardware::gnss::V1_0::IGnssConfiguration;
 using android::hardware::gnss::V1_0::IGnssDebug;
 using android::hardware::gnss::V1_0::IGnssGeofenceCallback;
 using android::hardware::gnss::V1_0::IGnssGeofencing;
@@ -108,6 +108,8 @@
 
 using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss;
 using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss;
+using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration;
+using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration;
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurementCallback_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurementCallback;
@@ -137,7 +139,8 @@
 sp<IAGnss> agnssIface = nullptr;
 sp<IGnssBatching> gnssBatchingIface = nullptr;
 sp<IGnssDebug> gnssDebugIface = nullptr;
-sp<IGnssConfiguration> gnssConfigurationIface = nullptr;
+sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr;
+sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr;
 sp<IGnssNi> gnssNiIface = nullptr;
 sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr;
 sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr;
@@ -1098,13 +1101,11 @@
     * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback
     * follow.
     */
-    Return<void> gnssLocationBatchCb(
-        const ::android::hardware::hidl_vec<GnssLocation> & locations)
+    Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations)
         override;
 };
 
-Return<void> GnssBatchingCallback::gnssLocationBatchCb(
-        const ::android::hardware::hidl_vec<GnssLocation> & locations) {
+Return<void> GnssBatchingCallback::gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations) {
     JNIEnv* env = getJniEnv();
 
     jobjectArray jLocations = env->NewObjectArray(locations.size(),
@@ -1257,11 +1258,21 @@
             gnssNiIface = gnssNi;
         }
 
-        auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration();
-        if (!gnssConfiguration.isOk()) {
-            ALOGD("Unable to get a handle to GnssConfiguration");
+        if (gnssHal_V1_1 != nullptr) {
+            auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1();
+            if (!gnssConfiguration.isOk()) {
+                ALOGD("Unable to get a handle to GnssConfiguration");
+            } else {
+                gnssConfigurationIface_V1_1 = gnssConfiguration;
+                gnssConfigurationIface = gnssConfigurationIface_V1_1;
+            }
         } else {
-            gnssConfigurationIface = gnssConfiguration;
+            auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration();
+            if (!gnssConfiguration_V1_0.isOk()) {
+                ALOGD("Unable to get a handle to GnssConfiguration");
+            } else {
+                gnssConfigurationIface = gnssConfiguration_V1_0;
+            }
         }
 
         auto gnssGeofencing = gnssHal->getExtensionGnssGeofencing();
@@ -1997,6 +2008,48 @@
     }
 }
 
+static jboolean android_location_GnssLocationProvider_set_satellite_blacklist(
+        JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) {
+    if (gnssConfigurationIface_V1_1 == nullptr) {
+        ALOGI("No GNSS Satellite Blacklist interface available");
+        return JNI_FALSE;
+    }
+
+    jint *constellation_array = env->GetIntArrayElements(constellations, 0);
+    if (NULL == constellation_array) {
+        ALOGI("GetIntArrayElements returns NULL.");
+        return JNI_FALSE;
+    }
+    jsize length = env->GetArrayLength(constellations);
+
+    jint *sv_id_array = env->GetIntArrayElements(sv_ids, 0);
+    if (NULL == sv_id_array) {
+        ALOGI("GetIntArrayElements returns NULL.");
+        return JNI_FALSE;
+    }
+
+    if (length != env->GetArrayLength(sv_ids)) {
+        ALOGI("Lengths of constellations and sv_ids are inconsistent.");
+        return JNI_FALSE;
+    }
+
+    hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources;
+    sources.resize(length);
+
+    for (int i = 0; i < length; i++) {
+        sources[i].constellation = static_cast<GnssConstellationType>(constellation_array[i]);
+        sources[i].svid = sv_id_array[i];
+    }
+
+    auto result = gnssConfigurationIface_V1_1->setBlacklist(sources);
+    if (result.isOk()) {
+        return result;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+
 static jint android_location_GnssLocationProvider_get_batch_size(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return 0; // batching not supported, size = 0
@@ -2185,6 +2238,9 @@
     {"native_set_emergency_supl_pdn",
             "(I)Z",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
+    {"native_set_satellite_blacklist",
+            "([I[I)Z",
+            reinterpret_cast<void *>(android_location_GnssLocationProvider_set_satellite_blacklist)},
     {"native_get_batch_size",
             "()I",
             reinterpret_cast<void *>(android_location_GnssLocationProvider_get_batch_size)},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 36dc121..9bc5fca 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -9193,7 +9193,7 @@
             long id = mInjector.binderClearCallingIdentity();
             try {
                 return mIPackageManager.setPackagesSuspendedAsUser(
-                        packageNames, suspended, null, null, "android", callingUserId);
+                        packageNames, suspended, null, null, null, "android", callingUserId);
             } catch (RemoteException re) {
                 // Shouldn't happen.
                 Slog.e(LOG_TAG, "Failed talking to the package manager", re);
diff --git a/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java b/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java
new file mode 100644
index 0000000..d6f5446
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssSatelliteBlacklistHelperTest.java
@@ -0,0 +1,130 @@
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+import android.provider.Settings;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.Shadows;
+import org.robolectric.annotation.Config;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Unit tests for {@link GnssSatelliteBlacklistHelper}.
+ */
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+        manifest = Config.NONE,
+        shadows = {
+        },
+        sdk = 27
+)
+@SystemLoaderPackages({"com.android.server.location"})
+@Presubmit
+public class GnssSatelliteBlacklistHelperTest {
+
+    private Context mContext;
+    private ContentResolver mContentResolver;
+    @Mock
+    private GnssSatelliteBlacklistHelper.GnssSatelliteBlacklistCallback mCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+        mContentResolver = mContext.getContentResolver();
+        new GnssSatelliteBlacklistHelper(mContext, Looper.myLooper(), mCallback);
+    }
+
+    @Test
+    public void blacklistOf2Satellites_callbackIsCalled() {
+        String blacklist = "3,0,5,24";
+        updateBlacklistAndVerifyCallbackIsCalled(blacklist);
+    }
+
+    @Test
+    public void blacklistWithSpaces_callbackIsCalled() {
+        String blacklist = "3, 11";
+        updateBlacklistAndVerifyCallbackIsCalled(blacklist);
+    }
+
+    @Test
+    public void emptyBlacklist_callbackIsCalled() {
+        String blacklist = "";
+        updateBlacklistAndVerifyCallbackIsCalled(blacklist);
+    }
+
+    @Test
+    public void blacklistWithOddNumberOfValues_callbackIsNotCalled() {
+        String blacklist = "3,0,5";
+        updateBlacklistAndNotifyContentObserver(blacklist);
+        verify(mCallback, never()).onUpdateSatelliteBlacklist(any(int[].class), any(int[].class));
+    }
+
+    @Test
+    public void blacklistWithNegativeValue_callbackIsNotCalled() {
+        String blacklist = "3,-11";
+        updateBlacklistAndNotifyContentObserver(blacklist);
+        verify(mCallback, never()).onUpdateSatelliteBlacklist(any(int[].class), any(int[].class));
+    }
+
+    @Test
+    public void blacklistWithNonDigitCharacter_callbackIsNotCalled() {
+        String blacklist = "3,1a,5,11";
+        updateBlacklistAndNotifyContentObserver(blacklist);
+        verify(mCallback, never()).onUpdateSatelliteBlacklist(any(int[].class), any(int[].class));
+    }
+
+    private void updateBlacklistAndNotifyContentObserver(String blacklist) {
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.GNSS_SATELLITE_BLACKLIST, blacklist);
+        notifyContentObserverFor(Settings.Global.GNSS_SATELLITE_BLACKLIST);
+    }
+
+    private void updateBlacklistAndVerifyCallbackIsCalled(String blacklist) {
+        updateBlacklistAndNotifyContentObserver(blacklist);
+
+        ArgumentCaptor<int[]> constellationsCaptor = ArgumentCaptor.forClass(int[].class);
+        ArgumentCaptor<int[]> svIdsCaptor = ArgumentCaptor.forClass(int[].class);
+        verify(mCallback).onUpdateSatelliteBlacklist(constellationsCaptor.capture(),
+                svIdsCaptor.capture());
+
+        int[] constellations = constellationsCaptor.getValue();
+        int[] svIds = svIdsCaptor.getValue();
+        List<Integer> values = GnssSatelliteBlacklistHelper.parseSatelliteBlacklist(blacklist);
+        assertThat(values.size()).isEqualTo(constellations.length * 2);
+        assertThat(svIds.length).isEqualTo(constellations.length);
+        for (int i = 0; i < constellations.length; i++) {
+            assertThat(constellations[i]).isEqualTo(values.get(i * 2));
+            assertThat(svIds[i]).isEqualTo(values.get(i * 2 + 1));
+        }
+    }
+
+    private static void notifyContentObserverFor(String globalSetting) {
+        Collection<ContentObserver> contentObservers =
+                Shadows.shadowOf(RuntimeEnvironment.application.getContentResolver())
+                        .getContentObservers(Settings.Global.getUriFor(globalSetting));
+        assertThat(contentObservers).isNotEmpty();
+        contentObservers.iterator().next().onChange(false /* selfChange */);
+    }
+}
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index cdb339a..a85f21c 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -29,6 +29,7 @@
     truth-prebuilt \
     testables \
     testng \
+    ub-uiautomator\
     platformprotosnano
 
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
@@ -69,8 +70,6 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
-LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
-
 LOCAL_PROGUARD_ENABLED := disabled
 
 include $(BUILD_PACKAGE)
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index ce98d65..22bec44 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -63,6 +63,7 @@
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
+    <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
@@ -145,6 +146,15 @@
         <activity android:name="com.android.server.pm.ShortcutTestActivity"
                  android:enabled="true" android:exported="true" />
 
+        <activity android:name="com.android.server.pm.SuspendedDetailsActivity"
+                  android:enabled="true"
+                  android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
+            <intent-filter>
+                <action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
 
         <activity-alias android:name="a.ShortcutEnabled"
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index f76eb56..a14b950 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -17,6 +17,9 @@
 package com.android.server.am;
 
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+
+import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -30,6 +33,7 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.UserInfo;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -38,6 +42,7 @@
 
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.server.LocalServices;
+import com.android.server.pm.PackageManagerService;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -79,11 +84,15 @@
     @Mock
     private DevicePolicyManagerInternal mDevicePolicyManager;
     @Mock
+    private PackageManagerInternal mPackageManagerInternal;
+    @Mock
     private UserManager mUserManager;
     @Mock
     private UserController mUserController;
     @Mock
     private KeyguardManager mKeyguardManager;
+    @Mock
+    private PackageManagerService mPackageManager;
 
     private ActivityStartInterceptor mInterceptor;
     private ActivityInfo mAInfo = new ActivityInfo();
@@ -103,6 +112,7 @@
         when(mDevicePolicyManager
                         .createShowAdminSupportIntent(TEST_USER_ID, true))
                 .thenReturn(ADMIN_SUPPORT_INTENT);
+        when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
 
         // Mock UserManager
         when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -114,16 +124,24 @@
                 nullable(CharSequence.class), nullable(CharSequence.class), eq(TEST_USER_ID))).
                 thenReturn(CONFIRM_CREDENTIALS_INTENT);
 
+        // Mock PackageManager
+        when(mService.getPackageManager()).thenReturn(mPackageManager);
+        when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(null);
+
         // Initialise activity info
-        mAInfo.packageName = TEST_PACKAGE_NAME;
         mAInfo.applicationInfo = new ApplicationInfo();
+        mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
     }
 
     @Test
-    public void testSuspendedPackage() {
+    public void testSuspendedByAdminPackage() {
         // GIVEN the package we're about to launch is currently suspended
         mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
 
+        when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+                .thenReturn(PLATFORM_PACKAGE_NAME);
+
         // THEN calling intercept returns true
         assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
 
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
index f740654..e4e9701 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/SystemUpdatePolicyTest.java
@@ -24,7 +24,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import android.app.admin.FreezeInterval;
+import android.app.admin.FreezePeriod;
 import android.app.admin.SystemUpdatePolicy;
 import android.os.Parcel;
 import android.support.test.runner.AndroidJUnit4;
@@ -42,15 +42,15 @@
 import java.io.ByteArrayOutputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
-import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
-import java.time.LocalTime;
+import java.time.MonthDay;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+
 /**
  * Unit tests for {@link android.app.admin.SystemUpdatePolicy}.
  * Throughout this test, we use "MM-DD" format to denote dates without year.
@@ -224,36 +224,36 @@
 
     @Test
     public void testDistanceWithoutLeapYear() {
-        assertEquals(364, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(364, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 12, 31), LocalDate.of(2016, 1, 1)));
-        assertEquals(365, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2017, 1, 1), LocalDate.of(2016, 1, 1)));
-        assertEquals(365, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2017, 2, 28), LocalDate.of(2016, 2, 29)));
-        assertEquals(-365, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(-365, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 1, 1), LocalDate.of(2017, 1, 1)));
-        assertEquals(1, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(1, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 29)));
-        assertEquals(1, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(1, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 3, 1), LocalDate.of(2016, 2, 28)));
-        assertEquals(0, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(0, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 2, 29), LocalDate.of(2016, 2, 28)));
-        assertEquals(0, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(0, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 2, 28), LocalDate.of(2016, 2, 28)));
 
-        assertEquals(59, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(59, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2016, 3, 1), LocalDate.of(2016, 1, 1)));
-        assertEquals(59, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(59, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2017, 3, 1), LocalDate.of(2017, 1, 1)));
 
-        assertEquals(365 * 40, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365 * 40, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2040, 1, 1), LocalDate.of(2000, 1, 1)));
 
-        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2019, 3, 1), LocalDate.of(2017, 3, 1)));
-        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2018, 3, 1), LocalDate.of(2016, 3, 1)));
-        assertEquals(365 * 2, FreezeInterval.distanceWithoutLeapYear(
+        assertEquals(365 * 2, FreezePeriod.distanceWithoutLeapYear(
                 LocalDate.of(2017, 3, 1), LocalDate.of(2015, 3, 1)));
 
     }
@@ -386,10 +386,10 @@
 
         // Two freeze periods
         p = SystemUpdatePolicy.createAutomaticInstallPolicy();
-        setFreezePeriods(p, "05-01", "06-01", "11-01", "01-29");
-        // automatic policy for July, August, September and October
+        setFreezePeriods(p, "05-01", "06-01", "10-15", "01-10");
+        // automatic policy for July, August, September and October until 15th
         assertInstallationOption(
-                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(92),
+                SystemUpdatePolicy.TYPE_INSTALL_AUTOMATIC, TimeUnit.DAYS.toMillis(31 + 30 + 14),
                 millis_2018_08_01, p);
     }
 
@@ -435,18 +435,18 @@
             String... dates) throws Exception {
         SystemUpdatePolicy p = SystemUpdatePolicy.createPostponeInstallPolicy();
         setFreezePeriods(p, dates);
-        p.validateAgainstPreviousFreezePeriod(parseDate(prevStart), parseDate(prevEnd),
-                parseDate(now));
+        p.validateAgainstPreviousFreezePeriod(parseLocalDate(prevStart),
+                parseLocalDate(prevEnd), parseLocalDate(now));
     }
 
     // "MM-DD" format for date
     private void setFreezePeriods(SystemUpdatePolicy policy, String... dates) throws Exception {
-        List<Pair<Integer, Integer>> periods = new ArrayList<>();
-        LocalDate lastDate = null;
+        List<FreezePeriod> periods = new ArrayList<>();
+        MonthDay lastDate = null;
         for (String date : dates) {
-            LocalDate currentDate = parseDate(date);
+            MonthDay currentDate = parseMonthDay(date);
             if (lastDate != null) {
-                periods.add(new Pair<>(lastDate.getDayOfYear(), currentDate.getDayOfYear()));
+                periods.add(new FreezePeriod(lastDate, currentDate));
                 lastDate = null;
             } else {
                 lastDate = currentDate;
@@ -457,7 +457,7 @@
     }
 
     private void testSerialization(SystemUpdatePolicy policy,
-            List<Pair<Integer, Integer>> expectedPeriods) throws Exception {
+            List<FreezePeriod> expectedPeriods) throws Exception {
         // Test parcel / unparcel
         Parcel parcel = Parcel.obtain();
         policy.writeToParcel(parcel, 0);
@@ -485,36 +485,27 @@
     }
 
     private void checkFreezePeriods(SystemUpdatePolicy policy,
-            List<Pair<Integer, Integer>> expectedPeriods) {
+            List<FreezePeriod> expectedPeriods) {
         int i = 0;
-        for (Pair<Integer, Integer> period : policy.getFreezePeriods()) {
-            assertEquals(expectedPeriods.get(i).first, period.first);
-            assertEquals(expectedPeriods.get(i).second, period.second);
+        for (FreezePeriod period : policy.getFreezePeriods()) {
+            assertEquals(expectedPeriods.get(i).getStart(), period.getStart());
+            assertEquals(expectedPeriods.get(i).getEnd(), period.getEnd());
             i++;
         }
     }
 
-    private LocalDate parseDate(String date) {
-        // Use leap year when parsing date string to handle "02-29", but force round down
-        // to Feb 28th by overriding the year to non-leap year.
-        final int year;
-        boolean monthDateOnly = false;
-        if (date.length() == 5) {
-            year = 2000;
-            monthDateOnly = true;
-        } else {
-            year = Integer.parseInt(date.substring(0, 4));
-            date = date.substring(5);
-        }
-        LocalDate result = LocalDate.of(year, Integer.parseInt(date.substring(0, 2)),
+    // MonthDay is of format MM-dd
+    private MonthDay parseMonthDay(String date) {
+        return MonthDay.of(Integer.parseInt(date.substring(0, 2)),
                 Integer.parseInt(date.substring(3, 5)));
-        if (monthDateOnly) {
-            return result.withYear(2001);
-        } else {
-            return result;
-        }
     }
 
+    // LocalDat is of format YYYY-MM-dd
+    private LocalDate parseLocalDate(String date) {
+        return parseMonthDay(date.substring(5)).atYear(Integer.parseInt(date.substring(0, 4)));
+    }
+
+
     private long toMillis(int year, int month, int day) {
         return LocalDateTime.of(year, month, day, 0, 0, 0).atZone(ZoneId.systemDefault())
                 .toInstant().toEpochMilli();
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index a6e0a66..ee83b62 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1037,7 +1037,7 @@
         pi.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED
                 | ApplicationInfo.FLAG_ALLOW_BACKUP;
         pi.versionCode = version;
-        pi.applicationInfo.versionCode = version;
+        pi.applicationInfo.setVersionCode(version);
         pi.signatures = null;
         pi.signingCertificateHistory = new Signature[][] {genSignatures(signatures)};
 
@@ -1055,7 +1055,7 @@
     protected void updatePackageVersion(String packageName, int increment) {
         updatePackageInfo(packageName, pi -> {
             pi.versionCode += increment;
-            pi.applicationInfo.versionCode += increment;
+            pi.applicationInfo.setVersionCode(pi.applicationInfo.longVersionCode + increment);
         });
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ebb4248..97ff94f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -199,13 +199,13 @@
                 PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
         final PersistableBundle launcherExtras1 = getPersistableBundle(
                 PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
-        ps1.setSuspended(true, "suspendingPackage1", appExtras1, launcherExtras1, 0);
+        ps1.setSuspended(true, "suspendingPackage1", "dialogMsg1", appExtras1, launcherExtras1, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
 
-        ps2.setSuspended(true, "suspendingPackage2", null, null, 0);
+        ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
 
-        ps3.setSuspended(false, "irrelevant", null, null, 0);
+        ps3.setSuspended(false, "irrelevant", "irrevelant2", null, null, 0);
         settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
 
         settingsUnderTest.writePackageRestrictionsLPr(0);
@@ -220,6 +220,7 @@
                 readUserState(0);
         assertThat(readPus1.suspended, is(true));
         assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
+        assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
         assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
                 is(true));
@@ -228,12 +229,17 @@
                 readUserState(0);
         assertThat(readPus2.suspended, is(true));
         assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
+        assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
         assertThat(readPus2.suspendedAppExtras, is(nullValue()));
         assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
 
         final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
                 readUserState(0);
         assertThat(readPus3.suspended, is(false));
+        assertThat(readPus3.suspendingPackage, is(nullValue()));
+        assertThat(readPus3.dialogMessage, is(nullValue()));
+        assertThat(readPus3.suspendedAppExtras, is(nullValue()));
+        assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 4e1418c..2a4ea8c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -184,30 +184,40 @@
         launcherExtras2.putString("name", "launcherExtras2");
         final String suspendingPackage1 = "package1";
         final String suspendingPackage2 = "package2";
+        final String dialogMessage1 = "dialogMessage1";
+        final String dialogMessage2 = "dialogMessage2";
 
         final PackageUserState testUserState1 = new PackageUserState();
         testUserState1.suspended = true;
         testUserState1.suspendedAppExtras = appExtras1;
         testUserState1.suspendedLauncherExtras = launcherExtras1;
         testUserState1.suspendingPackage = suspendingPackage1;
+        testUserState1.dialogMessage = dialogMessage1;
 
-        final PackageUserState testUserState2 = new PackageUserState(testUserState1);
+        PackageUserState testUserState2 = new PackageUserState(testUserState1);
         assertThat(testUserState1.equals(testUserState2), is(true));
         testUserState2.suspendingPackage = suspendingPackage2;
         assertThat(testUserState1.equals(testUserState2), is(false));
 
-        testUserState2.suspendingPackage = testUserState1.suspendingPackage;
+        testUserState2 = new PackageUserState(testUserState1);
         testUserState2.suspendedAppExtras = appExtras2;
         assertThat(testUserState1.equals(testUserState2), is(false));
 
-        testUserState2.suspendedAppExtras = testUserState1.suspendedAppExtras;
+        testUserState2 = new PackageUserState(testUserState1);
         testUserState2.suspendedLauncherExtras = launcherExtras2;
         assertThat(testUserState1.equals(testUserState2), is(false));
 
-        // Everything is different but irrelevant if suspended is false
+        testUserState2 = new PackageUserState(testUserState1);
+        testUserState2.dialogMessage = dialogMessage2;
+        assertThat(testUserState1.equals(testUserState2), is(false));
+
+        testUserState2 = new PackageUserState(testUserState1);
         testUserState2.suspended = testUserState1.suspended = false;
-        testUserState2.suspendedAppExtras = appExtras2;
+        // Everything is different but irrelevant if suspended is false
         testUserState2.suspendingPackage = suspendingPackage2;
+        testUserState2.dialogMessage = dialogMessage2;
+        testUserState2.suspendedAppExtras = appExtras2;
+        testUserState2.suspendedLauncherExtras = launcherExtras2;
         assertThat(testUserState1.equals(testUserState2), is(true));
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
index 43a439b..36e4753 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
@@ -41,8 +42,15 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
 import android.util.Log;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
 
+import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity;
 import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
 
 import org.junit.After;
@@ -68,14 +76,25 @@
             INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED";
     public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED =
             INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED";
+    public static final String ACTION_REPORT_TEST_ACTIVITY_STARTED =
+            INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STARTED";
+    public static final String ACTION_REPORT_TEST_ACTIVITY_STOPPED =
+            INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STOPPED";
+    public static final String ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED =
+            INSTRUMENTATION_PACKAGE + ".action.REPORT_MORE_DETAILS_ACTIVITY_STARTED";
+    public static final String ACTION_FINISH_TEST_ACTIVITY =
+            INSTRUMENTATION_PACKAGE + ".action.FINISH_TEST_ACTIVITY";
+    public static final String EXTRA_RECEIVED_PACKAGE_NAME =
+            SuspendPackagesTest.INSTRUMENTATION_PACKAGE + ".extra.RECEIVED_PACKAGE_NAME";
+
 
     private Context mContext;
     private PackageManager mPackageManager;
     private LauncherApps mLauncherApps;
     private Handler mReceiverHandler;
-    private ComponentName mTestReceiverComponent;
     private AppCommunicationReceiver mAppCommsReceiver;
     private StubbedCallback mTestCallback;
+    private UiDevice mUiDevice;
 
     private static final class AppCommunicationReceiver extends BroadcastReceiver {
         private Context context;
@@ -86,11 +105,12 @@
             this.context = context;
         }
 
-        void register(Handler handler) {
+        void register(Handler handler, String... actions) {
             registered = true;
             final IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_SUSPENDED);
-            intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED);
+            for (String action : actions) {
+                intentFilter.addAction(action);
+            }
             context.registerReceiver(this, intentFilter, null, handler);
         }
 
@@ -110,19 +130,28 @@
             }
         }
 
-        Intent receiveIntentFromApp() {
+        Intent pollForIntent(long secondsToWait) {
             if (!registered) {
                 throw new IllegalStateException("Receiver not registered");
             }
             final Intent intent;
             try {
-                intent = intentQueue.poll(5, TimeUnit.SECONDS);
+                intent = intentQueue.poll(secondsToWait, TimeUnit.SECONDS);
             } catch (InterruptedException ie) {
                 throw new RuntimeException("Interrupted while waiting for app broadcast", ie);
             }
-            assertNotNull("No intent received from app within 5 seconds", intent);
             return intent;
         }
+
+        void drainPendingBroadcasts() {
+            while (pollForIntent(5) != null);
+        }
+
+        Intent receiveIntentFromApp() {
+            final Intent intentReceived = pollForIntent(5);
+            assertNotNull("No intent received from app within 5 seconds", intentReceived);
+            return intentReceived;
+        }
     }
 
     @Before
@@ -131,8 +160,7 @@
         mPackageManager = mContext.getPackageManager();
         mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
         mReceiverHandler = new Handler(Looper.getMainLooper());
-        mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
-                SuspendTestReceiver.class.getCanonicalName());
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
         IPackageManager ipm = AppGlobals.getPackageManager();
         try {
             // Otherwise implicit broadcasts will not be delivered.
@@ -151,9 +179,10 @@
     private Bundle requestAppAction(String action) throws InterruptedException {
         final AtomicReference<Bundle> result = new AtomicReference<>();
         final CountDownLatch receiverLatch = new CountDownLatch(1);
-
+        final ComponentName testReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
+                SuspendTestReceiver.class.getCanonicalName());
         final Intent broadcastIntent = new Intent(action)
-                .setComponent(mTestReceiverComponent)
+                .setComponent(testReceiverComponent)
                 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() {
             @Override
@@ -175,9 +204,10 @@
         return extras;
     }
 
-    private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras) {
+    private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
+            String dialogMessage) {
         final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
-                PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, null);
+                PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogMessage);
         assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
     }
 
@@ -187,6 +217,13 @@
         assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
     }
 
+    private void startTestAppActivity() {
+        final Intent testActivity = new Intent()
+                .setComponent(new ComponentName(TEST_APP_PACKAGE_NAME,
+                        SuspendTestActivity.class.getCanonicalName()))
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(testActivity);
+    }
 
     private static boolean areSameExtras(BaseBundle expected, BaseBundle received) {
         if (expected != null) {
@@ -199,13 +236,14 @@
     }
 
     private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
-        assertTrue(message + ": [expected: " + expected + "; received: " + received + "]",
-                areSameExtras(expected, received));
+        if (!areSameExtras(expected, received)) {
+            fail(message + ": [expected: " + expected + "; received: " + received + "]");
+        }
     }
 
     @Test
     public void testIsPackageSuspended() {
-        suspendTestPackage(null, null);
+        suspendTestPackage(null, null, null);
         assertTrue("isPackageSuspended is false",
                 mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
     }
@@ -217,7 +255,7 @@
         assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
 
         final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2);
-        suspendTestPackage(appExtras, null);
+        suspendTestPackage(appExtras, null, null);
 
         resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
         assertTrue("resultFromApp:suspended is false",
@@ -230,37 +268,39 @@
 
     @Test
     public void testMyPackageSuspendedUnsuspended() {
-        mAppCommsReceiver.register(mReceiverHandler);
+        mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED,
+                ACTION_REPORT_MY_PACKAGE_UNSUSPENDED);
+        mAppCommsReceiver.drainPendingBroadcasts();
         final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1);
-        suspendTestPackage(appExtras, null);
+        suspendTestPackage(appExtras, null, null);
         Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
-        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
-                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
         assertSameExtras("Received app extras different to the ones supplied", appExtras,
                 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
         unsuspendTestPackage();
         intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
-        assertTrue("MY_PACKAGE_UNSUSPENDED delivery not reported",
-                ACTION_REPORT_MY_PACKAGE_UNSUSPENDED.equals(intentFromApp.getAction()));
+        assertEquals("MY_PACKAGE_UNSUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction());
     }
 
     @Test
     public void testUpdatingAppExtras() {
-        mAppCommsReceiver.register(mReceiverHandler);
+        mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED);
         final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1,
                 "1", 0.1);
-        suspendTestPackage(extras1, null);
+        suspendTestPackage(extras1, null, null);
         Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
-        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
-                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
         assertSameExtras("Received app extras different to the ones supplied", extras1,
                 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
         final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
                 "2", 0.2);
         mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
         intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
-        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
-                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
         assertSameExtras("Received app extras different to the updated extras", extras2,
                 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
     }
@@ -274,12 +314,26 @@
     }
 
     @Test
+    public void testActivityStoppedOnSuspend() {
+        mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_TEST_ACTIVITY_STARTED,
+                ACTION_REPORT_TEST_ACTIVITY_STOPPED);
+        startTestAppActivity();
+        Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertEquals("Test activity start not reported",
+                ACTION_REPORT_TEST_ACTIVITY_STARTED, intentFromApp.getAction());
+        suspendTestPackage(null, null, null);
+        intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertEquals("Test activity stop not reported on suspending the test app",
+                ACTION_REPORT_TEST_ACTIVITY_STOPPED, intentFromApp.getAction());
+    }
+
+    @Test
     public void testGetLauncherExtrasNonNull() {
         final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras(
                 TEST_APP_PACKAGE_NAME, mContext.getUser());
         assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended);
         final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1);
-        suspendTestPackage(null, launcherExtras);
+        suspendTestPackage(null, launcherExtras, null);
         final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras(
                 TEST_APP_PACKAGE_NAME, mContext.getUser());
         assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras,
@@ -288,7 +342,7 @@
 
     @Test
     public void testGetLauncherExtrasNull() {
-        suspendTestPackage(null, null);
+        suspendTestPackage(null, null, null);
         final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras(
                 TEST_APP_PACKAGE_NAME, mContext.getUser());
         assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven);
@@ -339,7 +393,7 @@
             }
         };
         mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
-        suspendTestPackage(null, suppliedExtras);
+        suspendTestPackage(null, suppliedExtras, null);
         assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS));
         twoCallbackLatch.countDown();
         assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS));
@@ -373,19 +427,53 @@
             }
         };
         mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
-        suspendTestPackage(null, suppliedExtras);
+        suspendTestPackage(null, suppliedExtras, null);
         assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS));
         final String result = overridingOneCallbackResult.get();
         assertTrue("Callback did not complete as expected: " + result, result.isEmpty());
     }
 
+    private void turnScreenOn() throws Exception {
+        if (!mUiDevice.isScreenOn()) {
+            mUiDevice.wakeUp();
+        }
+        final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        wm.dismissKeyguard(null, null);
+    }
+
+    @Test
+    public void testInterceptorActivity() throws Exception {
+        turnScreenOn();
+        mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
+                ACTION_REPORT_TEST_ACTIVITY_STARTED);
+        final String testMessage = "This is a test message";
+        suspendTestPackage(null, null, testMessage);
+        startTestAppActivity();
+        assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
+        assertNotNull("Given dialog message not shown",
+                mUiDevice.wait(Until.findObject(By.text(testMessage)), 5000));
+        final String buttonText = "More details";
+        final UiObject2 moreDetailsButton = mUiDevice.findObject(
+                By.clickable(true).text(buttonText));
+        assertNotNull("\"More Details\" button not shown", moreDetailsButton);
+        moreDetailsButton.click();
+        final Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertEquals("\"More Details\" activity start not reported",
+                ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED, intentFromApp.getAction());
+        final String receivedPackageName = intentFromApp.getStringExtra(
+                EXTRA_RECEIVED_PACKAGE_NAME);
+        assertEquals("Wrong package name received by \"More Details\" activity",
+                TEST_APP_PACKAGE_NAME, receivedPackageName);
+    }
+
     @After
-    public void tearDown() throws Exception {
+    public void tearDown() {
         mAppCommsReceiver.unregister();
         if (mTestCallback != null) {
             mLauncherApps.unregisterCallback(mTestCallback);
         }
-        Thread.sleep(250); // To prevent any race with the next registerReceiver
+        mContext.sendBroadcast(new Intent(ACTION_FINISH_TEST_ACTIVITY)
+                .setPackage(TEST_APP_PACKAGE_NAME));
     }
 
     private static abstract class StubbedCallback extends LauncherApps.Callback {
diff --git a/services/tests/servicestests/src/com/android/server/pm/SuspendedDetailsActivity.java b/services/tests/servicestests/src/com/android/server/pm/SuspendedDetailsActivity.java
new file mode 100644
index 0000000..18f0123
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendedDetailsActivity.java
@@ -0,0 +1,42 @@
+/*
+ * 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.pm;
+
+import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED;
+import static com.android.server.pm.SuspendPackagesTest.EXTRA_RECEIVED_PACKAGE_NAME;
+import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class SuspendedDetailsActivity extends Activity {
+    private static final String TAG = SuspendedDetailsActivity.class.getSimpleName();
+
+    @Override
+    protected void onStart() {
+        Log.d(TAG, "onStart");
+        final String suspendedPackage =  getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+        super.onStart();
+        final Intent reportStart = new Intent(ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED)
+                .putExtra(EXTRA_RECEIVED_PACKAGE_NAME, suspendedPackage)
+                .setPackage(INSTRUMENTATION_PACKAGE);
+        sendBroadcast(reportStart);
+        finish();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 9ad41cd..dca09af 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -21,7 +21,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.Signature;
@@ -206,7 +205,7 @@
         PackageInfo p = createPackageInfo(packageName, enabled, valid, installed, signatures,
                 updateTime, hidden);
         p.setLongVersionCode(versionCode);
-        p.applicationInfo.versionCode = versionCode;
+        p.applicationInfo.setVersionCode(versionCode);
         if (isSystemApp) p.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
         return p;
     }
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
index afdde72..ae0b0f9 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
index fa5fc58..ab7ddbb 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestActivity.java
@@ -16,9 +16,45 @@
 
 package com.android.servicestests.apps.suspendtestapp;
 
+import static com.android.server.pm.SuspendPackagesTest.ACTION_FINISH_TEST_ACTIVITY;
+import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_TEST_ACTIVITY_STARTED;
+import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_TEST_ACTIVITY_STOPPED;
+import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE;
+
 import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
 
 public class SuspendTestActivity extends Activity {
     private static final String TAG = SuspendTestActivity.class.getSimpleName();
 
-}
\ No newline at end of file
+    private BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "Finishing test activity from receiver");
+            SuspendTestActivity.this.finish();
+        }
+    };
+
+    @Override
+    public void onStart() {
+        Log.d(TAG, "onStart");
+        final Intent reportStart = new Intent(ACTION_REPORT_TEST_ACTIVITY_STARTED)
+                .setPackage(INSTRUMENTATION_PACKAGE);
+        sendBroadcast(reportStart);
+        registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_TEST_ACTIVITY));
+        super.onStart();
+    }
+    @Override
+    public void onStop() {
+        Log.d(TAG, "onStop");
+        final Intent reportStop = new Intent(ACTION_REPORT_TEST_ACTIVITY_STOPPED)
+                .setPackage(INSTRUMENTATION_PACKAGE);
+        sendBroadcast(reportStop);
+        unregisterReceiver(mFinishReceiver);
+        super.onStop();
+    }
+}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 288411a..aa76eab 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2187,7 +2187,7 @@
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
-        sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, false);
+        sDefaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
         sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index 35682a7..f7e6840 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -206,8 +206,10 @@
         }
     }
 
-    /** @hide */
-    protected NetworkService() {
+    /**
+     * Default constructor.
+     */
+    public NetworkService() {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
 
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index e8c1cb1..4ca5ce3 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -429,8 +429,10 @@
         }
     }
 
-    /** @hide */
-    protected DataService() {
+    /**
+     * Default constructor.
+     */
+    public DataService() {
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
 
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index a946e50..13210e8 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -260,12 +260,14 @@
         IpSecManager.IpSecTunnelInterface tunnelIntf =
                 createAndValidateVti(DUMMY_RESOURCE_ID, VTI_INTF_NAME);
 
-        tunnelIntf.addAddress(VTI_INNER_ADDRESS);
+        tunnelIntf.addAddress(VTI_INNER_ADDRESS.getAddress(),
+                VTI_INNER_ADDRESS.getPrefixLength());
         verify(mMockIpSecService)
                 .addAddressToTunnelInterface(
                         eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
 
-        tunnelIntf.removeAddress(VTI_INNER_ADDRESS);
+        tunnelIntf.removeAddress(VTI_INNER_ADDRESS.getAddress(),
+                VTI_INNER_ADDRESS.getPrefixLength());
         verify(mMockIpSecService)
                 .addAddressToTunnelInterface(
                         eq(DUMMY_RESOURCE_ID), eq(VTI_INNER_ADDRESS), anyString());
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 163dd2a..b0e11c4 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -17,6 +17,9 @@
 package com.android.server;
 
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
 import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -70,6 +73,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 
@@ -181,6 +185,9 @@
     private static final int TIMEOUT_MS = 500;
     private static final int TEST_LINGER_DELAY_MS = 120;
 
+    private static final String MOBILE_IFNAME = "test_rmnet_data0";
+    private static final String WIFI_IFNAME = "test_wlan0";
+
     private MockContext mServiceContext;
     private WrappedConnectivityService mService;
     private WrappedConnectivityManager mCm;
@@ -751,7 +758,7 @@
 
     // NetworkMonitor implementation allowing overriding of Internet connectivity probe result.
     private class WrappedNetworkMonitor extends NetworkMonitor {
-        public Handler connectivityHandler;
+        public final Handler connectivityHandler;
         // HTTP response code fed back to NetworkMonitor for Internet connectivity probe.
         public int gen204ProbeResult = 500;
         public String gen204ProbeRedirectUrl = null;
@@ -928,6 +935,7 @@
         // Ensure that the default setting for Captive Portals is used for most tests
         setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
         setMobileDataAlwaysOn(false);
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
     }
 
     @After
@@ -2582,6 +2590,14 @@
         waitForIdle();
     }
 
+    private void setPrivateDnsSettings(String mode, String specifier) {
+        final ContentResolver cr = mServiceContext.getContentResolver();
+        Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_MODE, mode);
+        Settings.Global.putString(cr, Settings.Global.PRIVATE_DNS_SPECIFIER, specifier);
+        mService.updatePrivateDnsSettings();
+        waitForIdle();
+    }
+
     private boolean isForegroundNetwork(MockNetworkAgent network) {
         NetworkCapabilities nc = mCm.getNetworkCapabilities(network.getNetwork());
         assertNotNull(nc);
@@ -3583,7 +3599,7 @@
         mCm.registerNetworkCallback(networkRequest, networkCallback);
 
         LinkProperties lp = new LinkProperties();
-        lp.setInterfaceName("wlan0");
+        lp.setInterfaceName(WIFI_IFNAME);
         LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24");
         RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null,
                 NetworkUtils.numericToInetAddress("192.168.12.1"), lp.getInterfaceName());
@@ -3672,52 +3688,63 @@
 
     @Test
     public void testBasicDnsConfigurationPushed() throws Exception {
-        final String IFNAME = "test_rmnet_data0";
-        final String[] EMPTY_TLS_SERVERS = new String[0];
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+        ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
+
+        // Clear any interactions that occur as a result of CS starting up.
+        reset(mNetworkManagementService);
+
+        final String[] EMPTY_STRING_ARRAY = new String[0];
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         waitForIdle();
         verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
-                anyInt(), any(), any(), any(), anyString(), eq(EMPTY_TLS_SERVERS));
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
+        verifyNoMoreInteractions(mNetworkManagementService);
 
         final LinkProperties cellLp = new LinkProperties();
-        cellLp.setInterfaceName(IFNAME);
+        cellLp.setInterfaceName(MOBILE_IFNAME);
         // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
         // "is-reachable" testing in order to not program netd with unreachable
         // nameservers that it might try repeated to validate.
         cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
-        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"), IFNAME));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+                MOBILE_IFNAME));
         cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
-        cellLp.addRoute(
-                new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"), IFNAME));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
+                MOBILE_IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         mCellNetworkAgent.connect(false);
         waitForIdle();
-        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
-                anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                anyString(), eq(EMPTY_TLS_SERVERS));
         // CS tells netd about the empty DNS config for this network.
-        assertEmpty(mStringArrayCaptor.getValue());
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
         reset(mNetworkManagementService);
 
         cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                anyString(), eq(EMPTY_TLS_SERVERS));
+                eq(""), tlsServers.capture());
         assertEquals(1, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.contains(mStringArrayCaptor.getValue(), "2001:db8::1"));
+        // Opportunistic mode.
+        assertTrue(ArrayUtils.contains(tlsServers.getValue(), "2001:db8::1"));
         reset(mNetworkManagementService);
 
         cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
         waitForIdle();
-        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
-                anyString(), eq(EMPTY_TLS_SERVERS));
+                eq(""), tlsServers.capture());
         assertEquals(2, mStringArrayCaptor.getValue().length);
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
+        // Opportunistic mode.
+        assertEquals(2, tlsServers.getValue().length);
+        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
         reset(mNetworkManagementService);
 
         final String TLS_SPECIFIER = "tls.example.com";
@@ -3730,7 +3757,7 @@
                 mCellNetworkAgent.getNetwork().netId,
                 new DnsManager.PrivateDnsConfig(TLS_SPECIFIER, TLS_IPS)));
         waitForIdle();
-        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
                 anyInt(), mStringArrayCaptor.capture(), any(), any(),
                 eq(TLS_SPECIFIER), eq(TLS_SERVERS));
         assertEquals(2, mStringArrayCaptor.getValue().length);
@@ -3739,6 +3766,77 @@
         reset(mNetworkManagementService);
     }
 
+    @Test
+    public void testPrivateDnsSettingsChange() throws Exception {
+        final String[] EMPTY_STRING_ARRAY = new String[0];
+        ArgumentCaptor<String[]> tlsServers = ArgumentCaptor.forClass(String[].class);
+
+        // Clear any interactions that occur as a result of CS starting up.
+        reset(mNetworkManagementService);
+
+        // The default on Android is opportunistic mode ("Automatic").
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        waitForIdle();
+        // CS tells netd about the empty DNS config for this network.
+        verify(mNetworkManagementService, never()).setDnsConfigurationForNetwork(
+                anyInt(), eq(EMPTY_STRING_ARRAY), any(), any(), eq(""), eq(EMPTY_STRING_ARRAY));
+        verifyNoMoreInteractions(mNetworkManagementService);
+
+        final LinkProperties cellLp = new LinkProperties();
+        cellLp.setInterfaceName(MOBILE_IFNAME);
+        // Add IPv4 and IPv6 default routes, because DNS-over-TLS code does
+        // "is-reachable" testing in order to not program netd with unreachable
+        // nameservers that it might try repeated to validate.
+        cellLp.addLinkAddress(new LinkAddress("192.0.2.4/24"));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("192.0.2.4"),
+                MOBILE_IFNAME));
+        cellLp.addLinkAddress(new LinkAddress("2001:db8:1::1/64"));
+        cellLp.addRoute(new RouteInfo((IpPrefix) null, InetAddress.getByName("2001:db8:1::1"),
+                MOBILE_IFNAME));
+        cellLp.addDnsServer(InetAddress.getByName("2001:db8::1"));
+        cellLp.addDnsServer(InetAddress.getByName("192.0.2.1"));
+
+        mCellNetworkAgent.sendLinkProperties(cellLp);
+        mCellNetworkAgent.connect(false);
+        waitForIdle();
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                eq(""), tlsServers.capture());
+        assertEquals(2, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        // Opportunistic mode.
+        assertEquals(2, tlsServers.getValue().length);
+        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        reset(mNetworkManagementService);
+
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
+        verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                eq(""), eq(EMPTY_STRING_ARRAY));
+        assertEquals(2, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        reset(mNetworkManagementService);
+
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+        verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
+                anyInt(), mStringArrayCaptor.capture(), any(), any(),
+                eq(""), tlsServers.capture());
+        assertEquals(2, mStringArrayCaptor.getValue().length);
+        assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        assertEquals(2, tlsServers.getValue().length);
+        assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
+                new String[]{"2001:db8::1", "192.0.2.1"}));
+        reset(mNetworkManagementService);
+
+        // Can't test strict mode without properly mocking out the DNS lookups.
+    }
+
     private void checkDirectlyConnectedRoutes(Object callbackObj,
             Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
         assertTrue(callbackObj instanceof LinkProperties);
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 948422c..17819db 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -101,7 +101,6 @@
     cflags: [
         "-Wall",
         "-Werror",
-        "-fexceptions",
     ],
     export_generated_headers: ["statslog.h"],
     shared_libs: [
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 499f254..057772f 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -101,7 +101,8 @@
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
 
-    fprintf(out, "#include <exception>\n");
+    fprintf(out, "#include <chrono>\n");
+    fprintf(out, "#include <thread>\n");
     fprintf(out, "#include <log/log_event_list.h>\n");
     fprintf(out, "#include <log/log.h>\n");
     fprintf(out, "#include <statslog.h>\n");
@@ -212,7 +213,7 @@
         int argIndex;
 
         fprintf(out, "int\n");
-        fprintf(out, "stats_write(int32_t code");
+        fprintf(out, "try_stats_write(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
             arg != signature->end(); arg++) {
@@ -247,10 +248,6 @@
                     if (chainField.javaType == JAVA_TYPE_STRING) {
                         fprintf(out, "    if (%s_length != %s.size()) {\n",
                             attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
-                        fprintf(out, "        throw std::invalid_argument(\"attribution fields with"
-                            " diff length: %s vs %s\");\n",
-                            attributionDecl.fields.front().name.c_str(),
-                            chainField.name.c_str());
                         fprintf(out, "        return -EINVAL;\n");
                         fprintf(out, "    }\n");
                     }
@@ -289,12 +286,74 @@
         fprintf(out, "\n");
     }
 
+   for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
+       signature != atoms.signatures.end(); signature++) {
+       int argIndex;
+
+       fprintf(out, "int \n");
+       fprintf(out, "stats_write(int32_t code");
+       argIndex = 1;
+       for (vector<java_type_t>::const_iterator arg = signature->begin();
+           arg != signature->end(); arg++) {
+           if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+               for (auto chainField : attributionDecl.fields) {
+                   if (chainField.javaType == JAVA_TYPE_STRING) {
+                           fprintf(out, ", const std::vector<%s>& %s",
+                                cpp_type_name(chainField.javaType),
+                                chainField.name.c_str());
+                   } else {
+                           fprintf(out, ", const %s* %s, size_t %s_length",
+                                cpp_type_name(chainField.javaType),
+                                chainField.name.c_str(), chainField.name.c_str());
+                   }
+               }
+           } else {
+               fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+           }
+           argIndex++;
+       }
+       fprintf(out, ")\n");
+
+       fprintf(out, "{\n");
+       fprintf(out, "  int ret = 0;\n");
+
+       fprintf(out, "  for(int retry = 0; retry < 3; ++retry) {\n");
+       fprintf(out, "      ret =  try_stats_write(code");
+
+       argIndex = 1;
+       for (vector<java_type_t>::const_iterator arg = signature->begin();
+           arg != signature->end(); arg++) {
+           if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+               for (auto chainField : attributionDecl.fields) {
+                   if (chainField.javaType == JAVA_TYPE_STRING) {
+                           fprintf(out, ", %s",
+                                chainField.name.c_str());
+                   } else {
+                           fprintf(out, ",  %s,  %s_length",
+                                chainField.name.c_str(), chainField.name.c_str());
+                   }
+               }
+           } else {
+               fprintf(out, ", arg%d", argIndex);
+           }
+           argIndex++;
+       }
+       fprintf(out, ");\n");
+       fprintf(out, "      if (ret >= 0) { return retry; }\n");
+       fprintf(out,
+               "      std::this_thread::sleep_for(std::chrono::milliseconds(10 + 10 * retry));\n");
+       fprintf(out, "  }\n");
+       fprintf(out, "  return ret;\n");
+       fprintf(out, "}\n");
+       fprintf(out, "\n");
+   }
+
     for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
         signature != atoms.non_chained_signatures.end(); signature++) {
         int argIndex;
 
         fprintf(out, "int\n");
-        fprintf(out, "stats_write_non_chained(int32_t code");
+        fprintf(out, "try_stats_write_non_chained(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
             arg != signature->end(); arg++) {
@@ -331,6 +390,45 @@
         fprintf(out, "}\n");
         fprintf(out, "\n");
     }
+
+   for (set<vector<java_type_t>>::const_iterator signature = atoms.non_chained_signatures.begin();
+       signature != atoms.non_chained_signatures.end(); signature++) {
+       int argIndex;
+
+       fprintf(out, "int\n");
+       fprintf(out, "stats_write_non_chained(int32_t code");
+       argIndex = 1;
+       for (vector<java_type_t>::const_iterator arg = signature->begin();
+           arg != signature->end(); arg++) {
+           fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+           argIndex++;
+       }
+       fprintf(out, ")\n");
+
+       fprintf(out, "{\n");
+
+       fprintf(out, "  int ret = 0;\n");
+       fprintf(out, "  for(int retry = 0; retry < 3; ++retry) {\n");
+       fprintf(out, "      ret =  try_stats_write_non_chained(code");
+
+       argIndex = 1;
+       for (vector<java_type_t>::const_iterator arg = signature->begin();
+           arg != signature->end(); arg++) {
+           fprintf(out, ", arg%d",   argIndex);
+           argIndex++;
+       }
+       fprintf(out, ");\n");
+       fprintf(out, "      if (ret >= 0) { return retry; }\n");
+       fprintf(out,
+               "      std::this_thread::sleep_for(std::chrono::milliseconds(10 + 10 * retry));\n");
+       fprintf(out, "  }\n");
+       fprintf(out, "  return ret;\n");
+       fprintf(out, "}\n");
+
+       fprintf(out, "\n");
+   }
+
+
     // Print footer
     fprintf(out, "\n");
     fprintf(out, "} // namespace util\n");
@@ -775,10 +873,6 @@
                         fprintf(out, "    if (%s_length != %s_length) {\n",
                             chainField.name.c_str(),
                             attributionDecl.fields.front().name.c_str());
-                        fprintf(out, "        jniThrowException(env, "
-                            "\"java/lang/IllegalArgumentException\", "
-                            "\"invalid attribution field(%s) length.\");\n",
-                            chainField.name.c_str());
                         fprintf(out, "        return -EINVAL;\n");
                         fprintf(out, "    }\n");
                     }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 2f7b50d..b333126 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -143,7 +143,7 @@
 
     WifiConfiguration getWifiApConfiguration();
 
-    void setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
+    boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName);
 
     Messenger getWifiServiceMessenger(String packageName);
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 433285b..9c6c8a9 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2141,7 +2141,8 @@
     }
 
     /**
-     * Sets the Wi-Fi AP Configuration.
+     * Sets the Wi-Fi AP Configuration.  The AP configuration must either be open or
+     * WPA2 PSK networks.
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
      * @hide
@@ -2150,8 +2151,7 @@
     @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE)
     public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
         try {
-            mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
-            return true;
+            return mService.setWifiApConfiguration(wifiConfig, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index f3a78bd..f3ffcad 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -1034,4 +1034,40 @@
         verifyNoMoreInteractions(mWifiService);
     }
 
+    /**
+     * Verify that a successful call properly returns true.
+     */
+    @Test
+    public void testSetWifiApConfigurationSuccessReturnsTrue() throws Exception {
+        WifiConfiguration apConfig = new WifiConfiguration();
+
+        when(mWifiService.setWifiApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+                .thenReturn(true);
+        assertTrue(mWifiManager.setWifiApConfiguration(apConfig));
+    }
+
+    /**
+     * Verify that a failed call properly returns false.
+     */
+    @Test
+    public void testSetWifiApConfigurationFailureReturnsFalse() throws Exception {
+        WifiConfiguration apConfig = new WifiConfiguration();
+
+        when(mWifiService.setWifiApConfiguration(eq(apConfig), eq(TEST_PACKAGE_NAME)))
+                .thenReturn(false);
+        assertFalse(mWifiManager.setWifiApConfiguration(apConfig));
+    }
+
+    /**
+     * Verify Exceptions are rethrown when underlying calls to WifiService throw exceptions.
+     */
+    @Test
+    public void testSetWifiApConfigurationRethrowsException() throws Exception {
+        doThrow(new SecurityException()).when(mWifiService).setWifiApConfiguration(any(), any());
+
+        try {
+            mWifiManager.setWifiApConfiguration(new WifiConfiguration());
+            fail("setWifiApConfiguration should rethrow Exceptions from WifiService");
+        } catch (SecurityException e) { }
+    }
 }