Merge "Remove RenderPipelineType::OpenGL (1/many)"
diff --git a/Android.bp b/Android.bp
index 9daeb37..93d1f7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -853,6 +853,8 @@
     "core/tests/utiltests/jni",
 ]
 
+// TODO(b/77285514): remove this once the last few hidl interfaces have been
+// updated to use hwbinder.stubs.
 java_library {
     name: "hwbinder",
     no_framework_libs: true,
@@ -1072,3 +1074,58 @@
     exact_api_filename: "test-exact.txt",
     args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.TestApi -nodocs",
 }
+
+droiddoc {
+    name: "hwbinder-stubs-docs",
+    srcs: [
+        "core/java/android/os/HidlSupport.java",
+        "core/java/android/annotation/IntDef.java",
+        "core/java/android/annotation/NonNull.java",
+        "core/java/android/annotation/SystemApi.java",
+        "core/java/android/os/HwBinder.java",
+        "core/java/android/os/HwBlob.java",
+        "core/java/android/os/HwParcel.java",
+        "core/java/android/os/IHwBinder.java",
+        "core/java/android/os/IHwInterface.java",
+        "core/java/android/os/DeadObjectException.java",
+        "core/java/android/os/DeadSystemException.java",
+        "core/java/android/os/RemoteException.java",
+        "core/java/android/util/AndroidException.java",
+    ],
+    custom_template: "droiddoc-templates-sdk",
+    installable: false,
+    no_framework_libs: true,
+    args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly",
+}
+
+java_library_static {
+    name: "hwbinder.stubs",
+    sdk_version: "core_current",
+    srcs: [
+        ":hwbinder-stubs-docs",
+    ],
+}
+
+filegroup {
+    name: "apache-http-stubs-sources",
+    srcs: [
+        "core/java/org/apache/http/conn/ConnectTimeoutException.java",
+        "core/java/org/apache/http/conn/scheme/HostNameResolver.java",
+        "core/java/org/apache/http/conn/scheme/LayeredSocketFactory.java",
+        "core/java/org/apache/http/conn/scheme/SocketFactory.java",
+        "core/java/org/apache/http/conn/ssl/AbstractVerifier.java",
+        "core/java/org/apache/http/conn/ssl/AllowAllHostnameVerifier.java",
+        "core/java/org/apache/http/conn/ssl/AndroidDistinguishedNameParser.java",
+        "core/java/org/apache/http/conn/ssl/BrowserCompatHostnameVerifier.java",
+        "core/java/org/apache/http/conn/ssl/SSLSocketFactory.java",
+        "core/java/org/apache/http/conn/ssl/StrictHostnameVerifier.java",
+        "core/java/org/apache/http/conn/ssl/X509HostnameVerifier.java",
+        "core/java/org/apache/http/params/CoreConnectionPNames.java",
+        "core/java/org/apache/http/params/HttpConnectionParams.java",
+        "core/java/org/apache/http/params/HttpParams.java",
+        "core/java/android/net/http/HttpResponseCache.java",
+        "core/java/android/net/http/SslCertificate.java",
+        "core/java/android/net/http/SslError.java",
+        "core/java/com/android/internal/util/HexDump.java",
+    ],
+}
diff --git a/api/current.txt b/api/current.txt
index 5b5bd24..deaf467 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6464,7 +6464,6 @@
     method public int getLockTaskFeatures(android.content.ComponentName);
     method public java.lang.String[] getLockTaskPackages(android.content.ComponentName);
     method public java.lang.CharSequence getLongSupportMessage(android.content.ComponentName);
-    method public android.content.ComponentName getMandatoryBackupTransport();
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
     method public java.util.List<java.lang.String> getMeteredDataDisabledPackages(android.content.ComponentName);
@@ -6571,7 +6570,6 @@
     method public void setLockTaskPackages(android.content.ComponentName, java.lang.String[]) throws java.lang.SecurityException;
     method public void setLogoutEnabled(android.content.ComponentName, boolean);
     method public void setLongSupportMessage(android.content.ComponentName, java.lang.CharSequence);
-    method public boolean setMandatoryBackupTransport(android.content.ComponentName, android.content.ComponentName);
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -6732,7 +6730,6 @@
     field public static final int PERMISSION_POLICY_PROMPT = 0; // 0x0
     field public static final java.lang.String POLICY_DISABLE_CAMERA = "policy_disable_camera";
     field public static final java.lang.String POLICY_DISABLE_SCREEN_CAPTURE = "policy_disable_screen_capture";
-    field public static final java.lang.String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
     field public static final int RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT = 2; // 0x2
     field public static final int RESET_PASSWORD_REQUIRE_ENTRY = 1; // 0x1
     field public static final int SKIP_SETUP_WIZARD = 1; // 0x1
@@ -7206,8 +7203,6 @@
 
   public final class Slice implements android.os.Parcelable {
     ctor protected Slice(android.os.Parcel);
-    method public static deprecated android.app.slice.Slice bindSlice(android.content.ContentResolver, android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-    method public static deprecated android.app.slice.Slice bindSlice(android.content.Context, android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int describeContents();
     method public java.util.List<java.lang.String> getHints();
     method public java.util.List<android.app.slice.SliceItem> getItems();
@@ -7297,15 +7292,18 @@
   }
 
   public class SliceManager {
-    method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
-    method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
+    method public android.app.slice.Slice bindSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public android.app.slice.Slice bindSlice(android.content.Intent, java.util.Set<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
     method public int checkSlicePermission(android.net.Uri, int, int);
     method public java.util.List<android.net.Uri> getPinnedSlices();
-    method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+    method public java.util.Set<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
     method public java.util.Collection<android.net.Uri> getSliceDescendants(android.net.Uri);
     method public void grantSlicePermission(java.lang.String, android.net.Uri);
     method public android.net.Uri mapIntentToUri(android.content.Intent);
-    method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public void pinSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+    method public deprecated void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
     method public void revokeSlicePermission(java.lang.String, android.net.Uri);
     method public void unpinSlice(android.net.Uri);
     field public static final java.lang.String CATEGORY_SLICE = "android.app.slice.category.SLICE";
@@ -7325,7 +7323,8 @@
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
     method public final android.net.Uri insert(android.net.Uri, android.content.ContentValues);
-    method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.Set<android.app.slice.SliceSpec>);
+    method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
     method public android.app.PendingIntent onCreatePermissionRequest(android.net.Uri);
     method public java.util.Collection<android.net.Uri> onGetSliceDescendants(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
@@ -7398,7 +7397,7 @@
 
   public static class NetworkStats.Bucket {
     ctor public NetworkStats.Bucket();
-    method public int getDefaultNetwork();
+    method public int getDefaultNetworkStatus();
     method public long getEndTimeStamp();
     method public int getMetered();
     method public int getRoaming();
@@ -7474,12 +7473,12 @@
 
   public static final class UsageEvents.Event {
     ctor public UsageEvents.Event();
+    method public int getAppStandbyBucket();
     method public java.lang.String getClassName();
     method public android.content.res.Configuration getConfiguration();
     method public int getEventType();
     method public java.lang.String getPackageName();
     method public java.lang.String getShortcutId();
-    method public int getStandbyBucket();
     method public long getTimeStamp();
     field public static final int CONFIGURATION_CHANGE = 5; // 0x5
     field public static final int KEYGUARD_HIDDEN = 18; // 0x12
@@ -10954,7 +10953,7 @@
     method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
     method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
     method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle);
-    method public void onPackagesSuspended(java.lang.String[], android.os.Bundle, android.os.UserHandle);
+    method public void onPackagesSuspended(java.lang.String[], android.os.UserHandle, android.os.Bundle);
     method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
     method public void onPackagesUnsuspended(java.lang.String[], android.os.UserHandle);
     method public void onShortcutsChanged(java.lang.String, java.util.List<android.content.pm.ShortcutInfo>, android.os.UserHandle);
@@ -11021,7 +11020,7 @@
     field public java.lang.String sharedUserId;
     field public int sharedUserLabel;
     field public deprecated android.content.pm.Signature[] signatures;
-    field public android.content.pm.Signature[][] signingCertificateHistory;
+    field public android.content.pm.SigningInfo signingInfo;
     field public java.lang.String[] splitNames;
     field public int[] splitRevisionCodes;
     field public deprecated int versionCode;
@@ -11649,6 +11648,18 @@
     field public static final android.os.Parcelable.Creator<android.content.pm.Signature> CREATOR;
   }
 
+  public final class SigningInfo implements android.os.Parcelable {
+    ctor public SigningInfo();
+    ctor public SigningInfo(android.content.pm.SigningInfo);
+    method public int describeContents();
+    method public android.content.pm.Signature[] getApkContentsSigners();
+    method public android.content.pm.Signature[] getSigningCertificateHistory();
+    method public boolean hasMultipleSigners();
+    method public boolean hasPastSigningCertificates();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.SigningInfo> CREATOR;
+  }
+
   public final class VersionedPackage implements android.os.Parcelable {
     ctor public VersionedPackage(java.lang.String, int);
     ctor public VersionedPackage(java.lang.String, long);
@@ -27629,6 +27640,7 @@
   public class X509TrustManagerExtensions {
     ctor public X509TrustManagerExtensions(javax.net.ssl.X509TrustManager) throws java.lang.IllegalArgumentException;
     method public java.util.List<java.security.cert.X509Certificate> checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.lang.String) throws java.security.cert.CertificateException;
+    method public boolean isSameTrustConfiguration(java.lang.String, java.lang.String);
     method public boolean isUserAddedCertificate(java.security.cert.X509Certificate);
   }
 
@@ -42407,7 +42419,7 @@
   }
 
   public final class SubscriptionPlan implements android.os.Parcelable {
-    method public java.util.Iterator<android.util.Pair<java.time.ZonedDateTime, java.time.ZonedDateTime>> cycleIterator();
+    method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
     method public int describeContents();
     method public int getDataLimitBehavior();
     method public long getDataLimitBytes();
@@ -42429,9 +42441,7 @@
   public static class SubscriptionPlan.Builder {
     method public android.telephony.SubscriptionPlan build();
     method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
+    method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
     method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
     method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
     method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
diff --git a/api/system-current.txt b/api/system-current.txt
index 76a71cd..d27a1bf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -383,12 +383,18 @@
   }
 
   public final class StatsManager {
+    method public void addConfig(long, byte[]) throws android.app.StatsManager.StatsUnavailableException;
     method public boolean addConfiguration(long, byte[]);
     method public byte[] getData(long);
     method public byte[] getMetadata();
+    method public byte[] getReports(long) throws android.app.StatsManager.StatsUnavailableException;
+    method public byte[] getStatsMetadata() throws android.app.StatsManager.StatsUnavailableException;
+    method public void removeConfig(long) throws android.app.StatsManager.StatsUnavailableException;
     method public boolean removeConfiguration(long);
+    method public void setBroadcastSubscriber(android.app.PendingIntent, long, long) throws android.app.StatsManager.StatsUnavailableException;
     method public boolean setBroadcastSubscriber(long, long, android.app.PendingIntent);
     method public boolean setDataFetchOperation(long, android.app.PendingIntent);
+    method public void setFetchReportsOperation(android.app.PendingIntent, long) throws android.app.StatsManager.StatsUnavailableException;
     field public static final java.lang.String ACTION_STATSD_STARTED = "android.app.action.STATSD_STARTED";
     field public static final java.lang.String EXTRA_STATS_BROADCAST_SUBSCRIBER_COOKIES = "android.app.extra.STATS_BROADCAST_SUBSCRIBER_COOKIES";
     field public static final java.lang.String EXTRA_STATS_CONFIG_KEY = "android.app.extra.STATS_CONFIG_KEY";
@@ -398,6 +404,11 @@
     field public static final java.lang.String EXTRA_STATS_SUBSCRIPTION_RULE_ID = "android.app.extra.STATS_SUBSCRIPTION_RULE_ID";
   }
 
+  public static class StatsManager.StatsUnavailableException extends android.util.AndroidException {
+    ctor public StatsManager.StatsUnavailableException(java.lang.String);
+    ctor public StatsManager.StatsUnavailableException(java.lang.String, java.lang.Throwable);
+  }
+
   public class VrManager {
     method public void setAndBindVrCompositor(android.content.ComponentName);
     method public void setPersistentVrModeEnabled(boolean);
@@ -1027,11 +1038,10 @@
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public android.os.PersistableBundle getSuspendedPackageAppExtras(java.lang.String);
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
-    method public boolean isPackageSuspended(java.lang.String);
+    method public boolean isPackageSuspended(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle);
     method public abstract void registerDexModule(java.lang.String, android.content.pm.PackageManager.DexModuleRegisterCallback);
     method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
@@ -1039,7 +1049,6 @@
     method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
     method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
     method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
-    method public void setSuspendedPackageAppExtras(java.lang.String, android.os.PersistableBundle);
     method public abstract void setUpdateAvailable(java.lang.String, boolean);
     method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
     method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
@@ -1255,6 +1264,7 @@
     method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
     method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
     method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
+    method public android.util.Pair<float[], float[]> getMinimumBrightnessCurve();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
     method public void setSaturationLevel(float);
@@ -4397,6 +4407,7 @@
     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 static boolean isRecoverableKeyStoreEnabled(android.content.Context);
     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;
@@ -5191,7 +5202,7 @@
   }
 
   public final class SubscriptionPlan implements android.os.Parcelable {
-    method public java.util.Iterator<android.util.Pair<java.time.ZonedDateTime, java.time.ZonedDateTime>> cycleIterator();
+    method public java.util.Iterator<android.util.Range<java.time.ZonedDateTime>> cycleIterator();
     method public int describeContents();
     method public int getDataLimitBehavior();
     method public long getDataLimitBytes();
@@ -5213,9 +5224,10 @@
   public static class SubscriptionPlan.Builder {
     method public android.telephony.SubscriptionPlan build();
     method public static android.telephony.SubscriptionPlan.Builder createNonrecurring(java.time.ZonedDateTime, java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
-    method public static android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
+    method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
+    method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringDaily(java.time.ZonedDateTime);
+    method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringMonthly(java.time.ZonedDateTime);
+    method public static deprecated android.telephony.SubscriptionPlan.Builder createRecurringWeekly(java.time.ZonedDateTime);
     method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
     method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
     method public android.telephony.SubscriptionPlan.Builder setSummary(java.lang.CharSequence);
diff --git a/api/test-current.txt b/api/test-current.txt
index d5b1399..6b52d10 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -492,6 +492,27 @@
     method public int getProgramId();
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialMarkMs();
+    method public int getResumePlaybackMarkMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialMarkMs(int);
+    method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
+  }
+
+  public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
+    method public android.media.BufferingParams getBufferingParams();
+    method public void setBufferingParams(android.media.BufferingParams);
+  }
+
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -743,6 +764,15 @@
 
 }
 
+package android.security {
+
+  public class KeyStoreException extends java.lang.Exception {
+    ctor public KeyStoreException(int, java.lang.String);
+    method public int getErrorCode();
+  }
+
+}
+
 package android.security.keystore {
 
   public abstract class AttestationUtils {
@@ -752,6 +782,18 @@
     field public static final int ID_TYPE_SERIAL = 1; // 0x1
   }
 
+  public static final class KeyGenParameterSpec.Builder {
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUniqueIdIncluded(boolean);
+  }
+
+  public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
+    method public long getBoundToSpecificSecureUserId();
+  }
+
+  public static final class KeyProtection.Builder {
+    method public android.security.keystore.KeyProtection.Builder setBoundToSpecificSecureUserId(long);
+  }
+
 }
 
 package android.service.autofill {
@@ -920,6 +962,10 @@
 
 package android.telephony.mbms {
 
+  public static class DownloadRequest.Builder {
+    method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
+  }
+
   public final class FileInfo implements android.os.Parcelable {
     ctor public FileInfo(android.net.Uri, java.lang.String);
   }
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 8e46714..e0222d9 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -205,6 +205,8 @@
     tests/e2e/Alarm_e2e_test.cpp \
     tests/e2e/Attribution_e2e_test.cpp \
     tests/e2e/GaugeMetric_e2e_push_test.cpp \
+    tests/e2e/GaugeMetric_e2e_pull_test.cpp \
+    tests/e2e/ValueMetric_pull_e2e_test.cpp \
     tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp \
     tests/e2e/DimensionInCondition_e2e_combination_OR_cond_test.cpp \
     tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp \
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index 7d6c47b..e6272ed 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -365,7 +365,8 @@
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
-        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
+        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec * NS_PER_SEC,
+        [](const ConfigKey&){});
     processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
     return processor;
 }
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 90ce735..f2443e8 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -72,14 +72,15 @@
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
                                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                      const sp<AlarmMonitor>& periodicAlarmMonitor,
-                                     const long timeBaseSec,
+                                     const int64_t timeBaseNs,
                                      const std::function<void(const ConfigKey&)>& sendBroadcast)
     : mUidMap(uidMap),
       mAnomalyAlarmMonitor(anomalyAlarmMonitor),
       mPeriodicAlarmMonitor(periodicAlarmMonitor),
       mSendBroadcast(sendBroadcast),
-      mTimeBaseSec(timeBaseSec),
-      mLastLogTimestamp(0) {
+      mTimeBaseNs(timeBaseNs),
+      mLargestTimestampSeen(0),
+      mLastTimestampSeen(0) {
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
@@ -156,18 +157,54 @@
 }
 
 void StatsLogProcessor::OnLogEvent(LogEvent* event) {
+    OnLogEvent(event, false);
+}
+
+void StatsLogProcessor::OnLogEvent(LogEvent* event, bool reconnected) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     const int64_t currentTimestampNs = event->GetElapsedTimestampNs();
 
-    if (currentTimestampNs < mLastLogTimestamp) {
-        StatsdStats::getInstance().noteLogEventSkipped(
-            event->GetTagId(), event->GetElapsedTimestampNs());
-        return;
+    if (reconnected && mLastTimestampSeen != 0) {
+        // LogReader tells us the connection has just been reset. Now we need
+        // to enter reconnection state to find the last CP.
+        mInReconnection = true;
+    }
+
+    if (mInReconnection) {
+        // We see the checkpoint
+        if (currentTimestampNs == mLastTimestampSeen) {
+            mInReconnection = false;
+            // Found the CP. ignore this event, and we will start to read from next event.
+            return;
+        }
+        if (currentTimestampNs > mLargestTimestampSeen) {
+            // We see a new log but CP has not been found yet. Give up now.
+            mLogLossCount++;
+            mInReconnection = false;
+            StatsdStats::getInstance().noteLogLost(currentTimestampNs);
+            // Persist the data before we reset. Do we want this?
+            WriteDataToDiskLocked();
+            // We see fresher event before we see the checkpoint. We might have lost data.
+            // The best we can do is to reset.
+            std::vector<ConfigKey> configKeys;
+            for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
+                configKeys.push_back(it->first);
+            }
+            resetConfigsLocked(currentTimestampNs, configKeys);
+        } else {
+            // Still in search of the CP. Keep going.
+            return;
+        }
+    }
+
+    mLogCount++;
+    mLastTimestampSeen = currentTimestampNs;
+    if (mLargestTimestampSeen < currentTimestampNs) {
+        mLargestTimestampSeen = currentTimestampNs;
     }
 
     resetIfConfigTtlExpiredLocked(currentTimestampNs);
 
-    mLastLogTimestamp = currentTimestampNs;
     StatsdStats::getInstance().noteAtomLogged(
         event->GetTagId(), event->GetElapsedTimestampNs() / NS_PER_SEC);
 
@@ -210,7 +247,7 @@
         const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
     VLOG("Updated configuration for key %s", key.ToString().c_str());
     sp<MetricsManager> newMetricsManager =
-        new MetricsManager(key, config, mTimeBaseSec, (timestampNs - 1) / NS_PER_SEC + 1, mUidMap,
+        new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap,
                            mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
@@ -339,15 +376,9 @@
                 (long long)getWallClockNs());
 }
 
-void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
-    std::vector<ConfigKey> configKeysTtlExpired;
-    for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
-        if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
-            configKeysTtlExpired.push_back(it->first);
-        }
-    }
-
-    for (const auto& key : configKeysTtlExpired) {
+void StatsLogProcessor::resetConfigsLocked(const int64_t timestampNs,
+                                           const std::vector<ConfigKey>& configs) {
+    for (const auto& key : configs) {
         StatsdConfig config;
         if (StorageManager::readConfigFromDisk(key, &config)) {
             OnConfigUpdatedLocked(timestampNs, key, config);
@@ -362,6 +393,18 @@
     }
 }
 
+void StatsLogProcessor::resetIfConfigTtlExpiredLocked(const int64_t timestampNs) {
+    std::vector<ConfigKey> configKeysTtlExpired;
+    for (auto it = mMetricsManagers.begin(); it != mMetricsManagers.end(); it++) {
+        if (it->second != nullptr && !it->second->isInTtl(timestampNs)) {
+            configKeysTtlExpired.push_back(it->first);
+        }
+    }
+    if (configKeysTtlExpired.size() > 0) {
+        resetConfigsLocked(timestampNs, configKeysTtlExpired);
+    }
+}
+
 void StatsLogProcessor::OnConfigRemoved(const ConfigKey& key) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     auto it = mMetricsManagers.find(key);
@@ -438,6 +481,10 @@
     WriteDataToDiskLocked();
 }
 
+void StatsLogProcessor::informPullAlarmFired(const int64_t timestampNs) {
+    mStatsPullerManager.OnAlarmFired(timestampNs);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1e82b1e..6efdf8c 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -36,10 +36,13 @@
 public:
     StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor,
-                      const long timeBaseSec,
+                      const int64_t timeBaseNs,
                       const std::function<void(const ConfigKey&)>& sendBroadcast);
     virtual ~StatsLogProcessor();
 
+    void OnLogEvent(LogEvent* event, bool reconnectionStarts);
+
+    // for testing only.
     void OnLogEvent(LogEvent* event);
 
     void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
@@ -70,6 +73,7 @@
 
     void dumpStates(FILE* out, bool verbose);
 
+    void informPullAlarmFired(const int64_t timestampNs);
 
 private:
     // For testing only.
@@ -121,16 +125,30 @@
     // Handler over the isolated uid change event.
     void onIsolatedUidChangedEventLocked(const LogEvent& event);
 
+    void resetConfigsLocked(const int64_t timestampNs, const std::vector<ConfigKey>& configs);
+
     // Function used to send a broadcast so that receiver for the config key can call getData
     // to retrieve the stored data.
     std::function<void(const ConfigKey& key)> mSendBroadcast;
 
-    const long mTimeBaseSec;
+    const int64_t mTimeBaseNs;
 
-    int64_t mLastLogTimestamp;
+    // Largest timestamp of the events that we have processed.
+    int64_t mLargestTimestampSeen = 0;
+
+    int64_t mLastTimestampSeen = 0;
+
+    bool mInReconnection = false;
+
+    // Processed log count
+    uint64_t mLogCount = 0;
+
+    // Log loss detected count
+    int mLogLossCount = 0;
 
     long mLastPullerCacheClearTimeSec = 0;
 
+    FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
     FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
@@ -145,6 +163,12 @@
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+    FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
+
     FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d3cda63..f7cc00c 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -81,7 +81,7 @@
     StatsPuller::SetUidMap(mUidMap);
     mConfigManager = new ConfigManager();
     mProcessor = new StatsLogProcessor(mUidMap, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
-                                       getElapsedRealtimeSec(), [this](const ConfigKey& key) {
+                                       getElapsedRealtimeNs(), [this](const ConfigKey& key) {
         sp<IStatsCompanionService> sc = getStatsCompanionService();
         auto receiver = mConfigManager->GetConfigReceiver(key);
         if (sc == nullptr) {
@@ -745,7 +745,7 @@
                                          "Only system uid can call informPollAlarmFired");
     }
 
-    mStatsPullerManager.OnAlarmFired();
+    mProcessor->informPullAlarmFired(getElapsedRealtimeNs());
 
     VLOG("StatsService::informPollAlarmFired succeeded");
 
@@ -780,8 +780,6 @@
 }
 
 void StatsService::sayHiToStatsCompanion() {
-    // TODO: This method needs to be private. It is temporarily public and unsecured for testing
-    // purposes.
     sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
     if (statsCompanion != nullptr) {
         VLOG("Telling statsCompanion that statsd is ready");
@@ -818,49 +816,44 @@
     mConfigManager->Startup();
 }
 
-void StatsService::OnLogEvent(LogEvent* event) {
-    mProcessor->OnLogEvent(event);
+void StatsService::OnLogEvent(LogEvent* event, bool reconnectionStarts) {
+    mProcessor->OnLogEvent(event, reconnectionStarts);
 }
 
 Status StatsService::getData(int64_t key, vector<uint8_t>* output) {
     IPCThreadState* ipc = IPCThreadState::self();
     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(),
-                                 false /* include_current_bucket*/, output);
-        return Status::ok();
-    } else {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    ConfigKey configKey(ipc->getCallingUid(), key);
+    mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(),
+                             false /* include_current_bucket*/, output);
+    return Status::ok();
 }
 
 Status StatsService::getMetadata(vector<uint8_t>* output) {
     IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::getMetadata with Pid %i, Uid %i", ipc->getCallingPid(),
          ipc->getCallingUid());
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
-        return Status::ok();
-    } else {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+    return Status::ok();
 }
 
-Status StatsService::addConfiguration(int64_t key,
-                                      const vector <uint8_t>& config,
-                                      bool* success) {
+Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config) {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
+        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+    }
     IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
-            *success = true;
-        } else {
-            *success = false;
-        }
+    if (addConfigurationChecked(ipc->getCallingUid(), key, config)) {
         return Status::ok();
     } else {
-        *success = false;
-        return Status::fromExceptionCode(binder::Status::EX_SECURITY);
+        ALOGE("Could not parse malformatted StatsdConfig");
+        return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
+                                         "config does not correspond to a StatsdConfig proto");
     }
 }
 
@@ -876,80 +869,62 @@
     return true;
 }
 
-Status StatsService::removeDataFetchOperation(int64_t key, bool* success) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), key);
-        mConfigManager->RemoveConfigReceiver(configKey);
-        *success = true;
-        return Status::ok();
-    } else {
-        *success = false;
+Status StatsService::removeDataFetchOperation(int64_t key) {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    IPCThreadState* ipc = IPCThreadState::self();
+    ConfigKey configKey(ipc->getCallingUid(), key);
+    mConfigManager->RemoveConfigReceiver(configKey);
+    return Status::ok();
 }
 
-Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
-                                           bool* success) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), key);
-        mConfigManager->SetConfigReceiver(configKey, intentSender);
-        *success = true;
-        return Status::ok();
-    } else {
-        *success = false;
+Status StatsService::setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender) {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    IPCThreadState* ipc = IPCThreadState::self();
+    ConfigKey configKey(ipc->getCallingUid(), key);
+    mConfigManager->SetConfigReceiver(configKey, intentSender);
+    return Status::ok();
 }
 
-Status StatsService::removeConfiguration(int64_t key, bool* success) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), key);
-        mConfigManager->RemoveConfig(configKey);
-        SubscriberReporter::getInstance().removeConfig(configKey);
-        *success = true;
-        return Status::ok();
-    } else {
-        *success = false;
+Status StatsService::removeConfiguration(int64_t key) {
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    IPCThreadState* ipc = IPCThreadState::self();
+    ConfigKey configKey(ipc->getCallingUid(), key);
+    mConfigManager->RemoveConfig(configKey);
+    SubscriberReporter::getInstance().removeConfig(configKey);
+    return Status::ok();
 }
 
 Status StatsService::setBroadcastSubscriber(int64_t configId,
                                             int64_t subscriberId,
-                                            const sp<android::IBinder>& intentSender,
-                                            bool* success) {
+                                            const sp<android::IBinder>& intentSender) {
     VLOG("StatsService::setBroadcastSubscriber called.");
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), configId);
-        SubscriberReporter::getInstance()
-                .setBroadcastSubscriber(configKey, subscriberId, intentSender);
-        *success = true;
-        return Status::ok();
-    } else {
-        *success = false;
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    IPCThreadState* ipc = IPCThreadState::self();
+    ConfigKey configKey(ipc->getCallingUid(), configId);
+    SubscriberReporter::getInstance()
+            .setBroadcastSubscriber(configKey, subscriberId, intentSender);
+    return Status::ok();
 }
 
 Status StatsService::unsetBroadcastSubscriber(int64_t configId,
-                                              int64_t subscriberId,
-                                              bool* success) {
+                                              int64_t subscriberId) {
     VLOG("StatsService::unsetBroadcastSubscriber called.");
-    IPCThreadState* ipc = IPCThreadState::self();
-    if (checkCallingPermission(String16(kPermissionDump))) {
-        ConfigKey configKey(ipc->getCallingUid(), configId);
-        SubscriberReporter::getInstance()
-                .unsetBroadcastSubscriber(configKey, subscriberId);
-        *success = true;
-        return Status::ok();
-    } else {
-        *success = false;
+    if (!checkCallingPermission(String16(kPermissionDump))) {
         return Status::fromExceptionCode(binder::Status::EX_SECURITY);
     }
+    IPCThreadState* ipc = IPCThreadState::self();
+    ConfigKey configKey(ipc->getCallingUid(), configId);
+    SubscriberReporter::getInstance()
+            .unsetBroadcastSubscriber(configKey, subscriberId);
+    return Status::ok();
 }
 
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 648e9c5..d502796 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -76,7 +76,7 @@
     /**
      * Called by LogReader when there's a log event to process.
      */
-    virtual void OnLogEvent(LogEvent* event);
+    virtual void OnLogEvent(LogEvent* event, bool reconnectionStarts);
 
     /**
      * Binder call for clients to request data for this configuration key.
@@ -94,24 +94,23 @@
      * Binder call to let clients send a configuration and indicate they're interested when they
      * should requestData for this configuration.
      */
-    virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config,
-                                    bool* success) override;
+    virtual Status addConfiguration(int64_t key, const vector<uint8_t>& config) override;
 
     /**
      * Binder call to let clients register the data fetch operation for a configuration.
      */
-    virtual Status setDataFetchOperation(int64_t key, const sp<android::IBinder>& intentSender,
-                                         bool* success) override;
+    virtual Status setDataFetchOperation(int64_t key,
+                                         const sp<android::IBinder>& intentSender) override;
 
     /**
      * Binder call to remove the data fetch operation for the specified config key.
      */
-    virtual Status removeDataFetchOperation(int64_t key, bool* success) override;
+    virtual Status removeDataFetchOperation(int64_t key) override;
 
     /**
      * Binder call to allow clients to remove the specified configuration.
      */
-    virtual Status removeConfiguration(int64_t key, bool* success) override;
+    virtual Status removeConfiguration(int64_t key) override;
 
     /**
      * Binder call to associate the given config's subscriberId with the given intentSender.
@@ -119,17 +118,13 @@
      */
     virtual Status setBroadcastSubscriber(int64_t configId,
                                           int64_t subscriberId,
-                                          const sp<android::IBinder>& intentSender,
-                                          bool* success) override;
+                                          const sp<android::IBinder>& intentSender) override;
 
     /**
      * Binder call to unassociate the given config's subscriberId with any intentSender.
      */
-    virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId,
-                                            bool* success) override;
+    virtual Status unsetBroadcastSubscriber(int64_t configId, int64_t subscriberId) override;
 
-    // TODO: public for testing since statsd doesn't run when system starts. Change to private
-    // later.
     /** Inform statsCompanion that statsd is ready. */
     virtual void sayHiToStatsCompanion();
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 83d59c0..50ffe17 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -40,8 +40,8 @@
         return mPullerManager.PullerForMatcherExists(tagId);
     }
 
-    void OnAlarmFired() {
-        mPullerManager.OnAlarmFired();
+    void OnAlarmFired(const int64_t currentTimeNs) {
+        mPullerManager.OnAlarmFired(currentTimeNs);
     }
 
     virtual bool Pull(const int tagId, const int64_t timesNs,
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index 2f0e885..610faad 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -275,11 +275,9 @@
     }
 }
 
-void StatsPullerManagerImpl::OnAlarmFired() {
+void StatsPullerManagerImpl::OnAlarmFired(const int64_t currentTimeNs) {
     AutoMutex _l(mLock);
 
-    int64_t currentTimeNs = getElapsedRealtimeNs();
-
     int64_t minNextPullTimeNs = LONG_MAX;
 
     vector<pair<int, vector<ReceiverInfo*>>> needToPull =
@@ -288,7 +286,7 @@
         vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
         if (pair.second.size() != 0) {
             for (ReceiverInfo& receiverInfo : pair.second) {
-                if (receiverInfo.nextPullTimeNs < currentTimeNs) {
+                if (receiverInfo.nextPullTimeNs <= currentTimeNs) {
                     receivers.push_back(&receiverInfo);
                 } else {
                     if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
@@ -311,10 +309,9 @@
                     receiverPtr->onDataPulled(data);
                     // we may have just come out of a coma, compute next pull time
                     receiverInfo->nextPullTimeNs =
-                            ceil((double_t)(currentTimeNs - receiverInfo->nextPullTimeNs) /
-                                 receiverInfo->intervalNs) *
-                                    receiverInfo->intervalNs +
-                            receiverInfo->nextPullTimeNs;
+                            (currentTimeNs - receiverInfo->nextPullTimeNs) /
+                                receiverInfo->intervalNs * receiverInfo->intervalNs +
+                            receiverInfo->intervalNs + receiverInfo->nextPullTimeNs;
                     if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
                         minNextPullTimeNs = receiverInfo->nextPullTimeNs;
                     }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 8c771f3..56d04b4 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -58,7 +58,7 @@
     // Verify if we know how to pull for this matcher
     bool PullerForMatcherExists(int tagId) const;
 
-    void OnAlarmFired();
+    void OnAlarmFired(const int64_t timeNs);
 
     bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
 
@@ -90,6 +90,11 @@
     void updateAlarmLocked();
 
     int64_t mNextPullTimeNs;
+
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
index b589d0d..ee3ed23 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.cpp
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -50,7 +50,7 @@
 // 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_PERIODIC_ALARM_STATS = 12;
-const int FIELD_ID_SKIPPED_LOG_EVENT_STATS = 13;
+const int FIELD_ID_LOG_LOSS_STATS = 14;
 
 const int FIELD_ID_ATOM_STATS_TAG = 1;
 const int FIELD_ID_ATOM_STATS_COUNT = 2;
@@ -61,9 +61,6 @@
 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;
@@ -182,6 +179,14 @@
     noteConfigResetInternalLocked(key);
 }
 
+void StatsdStats::noteLogLost(int64_t timestampNs) {
+    lock_guard<std::mutex> lock(mLock);
+    if (mLogLossTimestampNs.size() == kMaxLoggerErrors) {
+        mLogLossTimestampNs.pop_front();
+    }
+    mLogLossTimestampNs.push_back(timestampNs);
+}
+
 void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
     noteBroadcastSent(key, getWallClockSec());
 }
@@ -350,15 +355,6 @@
     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
@@ -381,7 +377,7 @@
     mAnomalyAlarmRegisteredStats = 0;
     mPeriodicAlarmRegisteredStats = 0;
     mLoggerErrors.clear();
-    mSkippedLogEvents.clear();
+    mLogLossTimestampNs.clear();
     for (auto& config : mConfigStats) {
         config.second->broadcast_sent_time_sec.clear();
         config.second->data_drop_time_sec.clear();
@@ -395,6 +391,14 @@
     }
 }
 
+string buildTimeString(int64_t timeSec) {
+    time_t t = timeSec;
+    struct tm* tm = localtime(&t);
+    char timeBuffer[80];
+    strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p\n", tm);
+    return string(timeBuffer);
+}
+
 void StatsdStats::dumpStats(FILE* out) const {
     lock_guard<std::mutex> lock(mLock);
     time_t t = mStartTimeSec;
@@ -437,15 +441,19 @@
         }
 
         for (const auto& broadcastTime : configStats->broadcast_sent_time_sec) {
-            fprintf(out, "\tbroadcast time: %d\n", broadcastTime);
+            fprintf(out, "\tbroadcast time: %s(%lld)\n",
+                    buildTimeString(broadcastTime).c_str(), (long long)broadcastTime);
         }
 
         for (const auto& dataDropTime : configStats->data_drop_time_sec) {
-            fprintf(out, "\tdata drop time: %d\n", dataDropTime);
+            fprintf(out, "\tdata drop time: %s(%lld)\n",
+                    buildTimeString(dataDropTime).c_str(), (long long)dataDropTime);
         }
 
         for (const auto& dump : configStats->dump_report_stats) {
-            fprintf(out, "\tdump report time: %d bytes: %lld\n", dump.first, (long long)dump.second);
+            fprintf(out, "\tdump report time: %s(%lld) bytes: %lld\n",
+                    buildTimeString(dump.first).c_str(), (long long)dump.first,
+                    (long long)dump.second);
         }
 
         for (const auto& stats : pair.second->matcher_stats) {
@@ -503,8 +511,8 @@
         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);
+    for (const auto& loss : mLogLossTimestampNs) {
+        fprintf(out, "Log loss detected at %lld (elapsedRealtimeNs)\n", (long long)loss);
     }
 }
 
@@ -660,13 +668,9 @@
         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);
+    for (const auto& loss : mLogLossTimestampNs) {
+        proto.write(FIELD_TYPE_INT64 | FIELD_ID_LOG_LOSS_STATS | FIELD_COUNT_REPEATED,
+                    (long long)loss);
     }
 
     output->clear();
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 123a703..2cbcca3 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -102,9 +102,7 @@
     // The max number of old config stats we keep.
     const static int kMaxIceBoxSize = 20;
 
-    const static int kMaxLoggerErrors = 10;
-
-    const static int kMaxSkippedLogEvents = 200;
+    const static int kMaxLoggerErrors = 20;
 
     const static int kMaxTimestampCount = 20;
 
@@ -280,7 +278,7 @@
     /**
      * Records statsd skipped an event.
      */
-    void noteLogEventSkipped(int tag, int64_t timestamp);
+    void noteLogLost(int64_t timestamp);
 
     /**
      * Reset the historical stats. Including all stats in icebox, and the tracked stats about
@@ -337,8 +335,8 @@
     // 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;
+    // Timestamps when we detect log loss after logd reconnect.
+    std::list<int64_t> mLogLossTimestampNs;
 
     // Stores the number of times statsd modified the anomaly alarm registered with
     // StatsCompanionService.
diff --git a/cmds/statsd/src/logd/LogListener.h b/cmds/statsd/src/logd/LogListener.h
index 69ca571..f924040 100644
--- a/cmds/statsd/src/logd/LogListener.h
+++ b/cmds/statsd/src/logd/LogListener.h
@@ -33,7 +33,7 @@
     LogListener();
     virtual ~LogListener();
 
-    virtual void OnLogEvent(LogEvent* msg) = 0;
+    virtual void OnLogEvent(LogEvent* msg, bool reconnectionStarts) = 0;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/logd/LogReader.cpp b/cmds/statsd/src/logd/LogReader.cpp
index 0fe896b..26ae6a3 100644
--- a/cmds/statsd/src/logd/LogReader.cpp
+++ b/cmds/statsd/src/logd/LogReader.cpp
@@ -113,7 +113,8 @@
             LogEvent event(msg);
 
             // Call the listener
-            mListener->OnLogEvent(&event);
+            mListener->OnLogEvent(&event,
+                                  lineCount == 1 /* indicate whether it's a new connection */);
         }
     }
 
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 4ce4768..8ce9ec7 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -116,11 +116,6 @@
         ALOGE("Failed to add service");
         return -1;
     }
-
-    // TODO: This line is temporary, since statsd doesn't start up automatically (and therefore
-    // the call in StatsService::SystemRunning() won't ever be called right now).
-    // TODO: Are you sure? Don't we need to reconnect to the system process if we get restarted?
-    //  --joeo
     service->sayHiToStatsCompanion();
 
     // Start the log reader thread
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index c77e07b..e21392c 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -92,7 +92,7 @@
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
-         (long long)mBucketSizeNs, (long long)mStartTimeNs);
+         (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
 CountMetricProducer::~CountMetricProducer() {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 3125fa7..3661b31 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -122,7 +122,7 @@
         }
     }
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
-         (long long)mBucketSizeNs, (long long)mStartTimeNs);
+         (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
 DurationMetricProducer::~DurationMetricProducer() {
@@ -154,13 +154,13 @@
             return make_unique<OringDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
                     mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
-                    mStartTimeNs, mBucketSizeNs, mConditionSliced,
+                    mTimeBaseNs, mBucketSizeNs, mConditionSliced,
                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
                     mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
                     mDimensionsInCondition, mNested, mCurrentBucketStartTimeNs, mCurrentBucketNum,
-                    mStartTimeNs, mBucketSizeNs, mConditionSliced,
+                    mTimeBaseNs, mBucketSizeNs, mConditionSliced,
                     mHasLinksToAllConditionDimensionsInTracker, mAnomalyTrackers);
     }
 }
@@ -650,7 +650,7 @@
 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
                                                      const LogEvent& event) {
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
-    if (eventTimeNs < mStartTimeNs) {
+    if (eventTimeNs < mTimeBaseNs) {
         return;
     }
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 33ab9aa..2f2679e 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -68,7 +68,7 @@
     }
     mProto = std::make_unique<ProtoOutputStream>();
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
-         (long long)mBucketSizeNs, (long long)mStartTimeNs);
+         (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
 EventMetricProducer::~EventMetricProducer() {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 3c77aae..6886f7c 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -61,9 +61,9 @@
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t startTimeNs,
+                                         const int64_t timeBaseNs, const int64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId),
       mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
@@ -110,14 +110,15 @@
     }
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
+    flushIfNeededLocked(startTimeNs);
     // Kicks off the puller immediately.
     if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         mStatsPullerManager->RegisterReceiver(
-                mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+                mPullTagId, this, getCurrentBucketEndTimeNs(), mBucketSizeNs);
     }
 
     VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
-         (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs,
+         (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
          mConditionSliced);
 }
 
@@ -125,14 +126,14 @@
 GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t startTimeNs)
-    : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
+                                         const int64_t timeBaseNs, const int64_t startTimeNs)
+    : GaugeMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
                           make_shared<StatsPullerManager>()) {
 }
 
 GaugeMetricProducer::~GaugeMetricProducer() {
     VLOG("~GaugeMetricProducer() called");
-    if (mPullTagId != -1) {
+    if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
         mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
     }
 }
@@ -214,18 +215,20 @@
                         android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.find(
                                 mTagId) ==
                         android::util::AtomsInfo::kNotTruncatingTimestampAtomWhiteList.end();
-                const int64_t wall_clock_ns = truncateTimestamp ?
-                    truncateTimestampNsToFiveMinutes(getWallClockNs()) : getWallClockNs();
                 for (const auto& atom : bucket.mGaugeAtoms) {
-                    int64_t timestampNs =  truncateTimestamp ?
-                        truncateTimestampNsToFiveMinutes(atom.mTimestamps) : atom.mTimestamps;
+                    const int64_t elapsedTimestampNs =  truncateTimestamp ?
+                        truncateTimestampNsToFiveMinutes(atom.mElapsedTimestamps) :
+                            atom.mElapsedTimestamps;
+                    const int64_t wallClockNs = truncateTimestamp ?
+                        truncateTimestampNsToFiveMinutes(atom.mWallClockTimestampNs) :
+                            atom.mWallClockTimestampNs;
                     protoOutput->write(
                         FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED | FIELD_ID_ELAPSED_ATOM_TIMESTAMP,
-                        (long long)timestampNs);
+                        (long long)elapsedTimestampNs);
                     protoOutput->write(
                         FIELD_TYPE_INT64 | FIELD_COUNT_REPEATED |
                             FIELD_ID_WALL_CLOCK_ATOM_TIMESTAMP,
-                        (long long)wall_clock_ns);
+                        (long long)wallClockNs);
                 }
             }
             protoOutput->end(bucketInfoToken);
@@ -241,7 +244,7 @@
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
-void GaugeMetricProducer::pullLocked() {
+void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
     bool triggerPuller = false;
     switch(mSamplingType) {
         // When the metric wants to do random sampling and there is already one gauge atom for the
@@ -262,7 +265,7 @@
     }
 
     vector<std::shared_ptr<LogEvent>> allData;
-    if (!mStatsPullerManager->Pull(mPullTagId, getElapsedRealtimeNs(), &allData)) {
+    if (!mStatsPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
         ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
         return;
     }
@@ -273,26 +276,26 @@
 }
 
 void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
-                                                   const int64_t eventTime) {
+                                                   const int64_t eventTimeNs) {
     VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
-    flushIfNeededLocked(eventTime);
+    flushIfNeededLocked(eventTimeNs);
     mCondition = conditionMet;
 
-    if (mPullTagId != -1) {
-        pullLocked();
+    if (mPullTagId != -1 && mCondition) {
+        pullLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
-                                                           const int64_t eventTime) {
+                                                           const int64_t eventTimeNs) {
     VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
          overallCondition);
-    flushIfNeededLocked(eventTime);
+    flushIfNeededLocked(eventTimeNs);
     // If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
     // pull for every dimension.
     mCondition = overallCondition;
     if (mPullTagId != -1) {
-        pullLocked();
+        pullLocked(eventTimeNs);
     }  // else: Push mode. No need to proactively pull the gauge data.
 }
 
@@ -360,7 +363,7 @@
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
-    GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs);
+    GaugeAtom gaugeAtom(getGaugeFields(event), eventTimeNs, getWallClockNs());
     (*mCurrentSlicedBucket)[eventKey].push_back(gaugeAtom);
     // Anomaly detection on gauge metric only works when there is one numeric
     // field specified.
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 04b7df9..08765c2 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -33,11 +33,12 @@
 namespace statsd {
 
 struct GaugeAtom {
-    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t timeNs)
-        : mFields(fields), mTimestamps(timeNs) {
+    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs, int wallClockNs)
+        : mFields(fields), mElapsedTimestamps(elapsedTimeNs), mWallClockTimestampNs(wallClockNs) {
     }
     std::shared_ptr<vector<FieldValue>> mFields;
-    int64_t mTimestamps;
+    int64_t mElapsedTimestamps;
+    int64_t mWallClockTimestampNs;
 };
 
 struct GaugeBucket {
@@ -57,7 +58,7 @@
 public:
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t startTimeNs);
+                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
 
     virtual ~GaugeMetricProducer();
 
@@ -76,7 +77,7 @@
         flushCurrentBucketLocked(eventTimeNs);
         mCurrentBucketStartTimeNs = eventTimeNs;
         if (mPullTagId != -1) {
-            pullLocked();
+            pullLocked(eventTimeNs);
         }
     };
 
@@ -94,7 +95,8 @@
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t startTimeNs,
+                        const int pullTagId,
+                        const int64_t timeBaseNs, const int64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     // Internal interface to handle condition change.
@@ -115,7 +117,7 @@
 
     void flushCurrentBucketLocked(const int64_t& eventTimeNs) override;
 
-    void pullLocked();
+    void pullLocked(const int64_t timestampNs);
 
     int mTagId;
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index bcf0e62..5ff8082 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -27,7 +27,7 @@
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
     // this is old event, maybe statsd restarted?
-    if (eventTimeNs < mStartTimeNs) {
+    if (eventTimeNs < mTimeBaseNs) {
         return;
     }
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index f931e57..532ecbf 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -40,12 +40,12 @@
 // be a no-op.
 class MetricProducer : public virtual PackageInfoListener {
 public:
-    MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t startTimeNs,
+    MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const sp<ConditionWizard>& wizard)
         : mMetricId(metricId),
           mConfigKey(key),
-          mStartTimeNs(startTimeNs),
-          mCurrentBucketStartTimeNs(startTimeNs),
+          mTimeBaseNs(timeBaseNs),
+          mCurrentBucketStartTimeNs(timeBaseNs),
           mCurrentBucketNum(0),
           mCondition(conditionIndex >= 0 ? false : true),
           mConditionSliced(false),
@@ -165,6 +165,11 @@
         dropDataLocked(dropTimeNs);
     }
 
+    // For test only.
+    inline int64_t getCurrentBucketNum() const {
+        return mCurrentBucketNum;
+    }
+
 protected:
     virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
     virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
@@ -204,7 +209,7 @@
     // Convenience to compute the current bucket's end time, which is always aligned with the
     // start time of the metric.
     int64_t getCurrentBucketEndTimeNs() const {
-        return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
+        return mTimeBaseNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
     }
 
     virtual void dropDataLocked(const int64_t dropTimeNs) = 0;
@@ -215,7 +220,7 @@
 
     // The time when this metric producer was first created. The end time for the current bucket
     // can be computed from this based on mCurrentBucketNum.
-    int64_t mStartTimeNs;
+    int64_t mTimeBaseNs;
 
     // Start time may not be aligned with the start of statsd if there is an app upgrade in the
     // middle of a bucket.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index b7f1bd5..47a1a86 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -54,21 +54,21 @@
 const int FIELD_ID_ANNOTATIONS_INT32 = 2;
 
 MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config,
-                               const long timeBaseSec, const long currentTimeSec,
+                               const int64_t timeBaseNs, const int64_t currentTimeNs,
                                const sp<UidMap> &uidMap,
                                const sp<AlarmMonitor>& anomalyAlarmMonitor,
                                const sp<AlarmMonitor>& periodicAlarmMonitor)
     : mConfigKey(key), mUidMap(uidMap),
       mTtlNs(config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1),
       mTtlEndNs(-1),
-      mLastReportTimeNs(timeBaseSec * NS_PER_SEC),
+      mLastReportTimeNs(timeBaseNs),
       mLastReportWallClockNs(getWallClockNs()) {
     // Init the ttl end timestamp.
-    refreshTtl(timeBaseSec * NS_PER_SEC);
+    refreshTtl(timeBaseNs);
 
     mConfigValid =
             initStatsdConfig(key, config, *uidMap, anomalyAlarmMonitor, periodicAlarmMonitor,
-                             timeBaseSec, currentTimeSec, mTagIds, mAllAtomMatchers,
+                             timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers,
                              mAllConditionTrackers, mAllMetricProducers, mAllAnomalyTrackers,
                              mAllPeriodicAlarmTrackers, mConditionToMetricMap, mTrackerToMetricMap,
                              mTrackerToConditionMap, mNoReportMetricIds);
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 6aa260a..3d2c595 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -37,7 +37,7 @@
 class MetricsManager : public PackageInfoListener {
 public:
     MetricsManager(const ConfigKey& configKey, const StatsdConfig& config,
-                   const long timeBaseSec, const long currentTimeSec,
+                   const int64_t timeBaseNs, const int64_t currentTimeNs,
                    const sp<UidMap>& uidMap, const sp<AlarmMonitor>& anomalyAlarmMonitor,
                    const sp<AlarmMonitor>& periodicAlarmMonitor);
 
@@ -187,6 +187,11 @@
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByChain);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents);
+    FRIEND_TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm);
+    FRIEND_TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents);
+    FRIEND_TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm);
     FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_NoLink_OR_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestCreateCountMetric_Link_OR_CombinationCondition);
     FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_OR_CombinationCondition);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 51fac8c..844c728 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -63,9 +63,9 @@
 ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t startTimeNs,
+                                         const int64_t timeBaseNs, const int64_t startTimestampNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+    : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
       mValueField(metric.value_field()),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId),
@@ -105,27 +105,28 @@
         }
     }
 
-    if (mValueField.child_size()) {
+    if (mValueField.child_size() > 0) {
         mField = mValueField.child(0).field();
     }
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
     // Kicks off the puller immediately.
+    flushIfNeededLocked(startTimestampNs);
     if (mPullTagId != -1) {
         mStatsPullerManager->RegisterReceiver(
             mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
     }
 
     VLOG("value metric %lld created. bucket size %lld start_time: %lld",
-        (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs);
+        (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
 }
 
 // for testing
 ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
-                                         const int64_t startTimeNs)
-    : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
+                                         const int64_t timeBaseNs, const int64_t startTimeNs)
+    : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, timeBaseNs, startTimeNs,
                           make_shared<StatsPullerManager>()) {
 }
 
@@ -198,7 +199,6 @@
 
     VLOG("metric %lld dump report now...", (long long)mMetricId);
     mPastBuckets.clear();
-    // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -237,8 +237,8 @@
         // For scheduled pulled data, the effective event time is snap to the nearest
         // bucket boundary to make bucket finalize.
         int64_t realEventTime = allData.at(0)->GetElapsedTimestampNs();
-        int64_t eventTime = mStartTimeNs +
-            ((realEventTime - mStartTimeNs) / mBucketSizeNs) * mBucketSizeNs;
+        int64_t eventTime = mTimeBaseNs +
+            ((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
 
         mCondition = false;
         for (const auto& data : allData) {
@@ -310,7 +310,7 @@
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
     int error = 0;
-    const long value = event.GetLong(mField, &error);
+    const int64_t value = event.GetLong(mField, &error);
     if (error < 0) {
         return;
     }
@@ -334,14 +334,16 @@
                 } else {
                     interval.sum += value;
                 }
+                interval.hasValue = true;
                 interval.startUpdated = false;
             } else {
-                VLOG("No start for matching end %ld", value);
+                VLOG("No start for matching end %lld", (long long)value);
                 interval.tainted += 1;
             }
         }
     } else {    // for pushed events
         interval.sum += value;
+        interval.hasValue = true;
     }
 
     long wholeBucketVal = interval.sum;
@@ -357,7 +359,7 @@
 void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
 
-    if (currentBucketEndTimeNs > eventTimeNs) {
+    if (eventTimeNs < currentBucketEndTimeNs) {
         VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
              (long long)(currentBucketEndTimeNs));
         return;
@@ -393,10 +395,12 @@
     for (const auto& slice : mCurrentSlicedBucket) {
         tainted += slice.second.tainted;
         tainted += slice.second.startUpdated;
-        info.mValue = slice.second.sum;
-        // it will auto create new vector of ValuebucketInfo if the key is not found.
-        auto& bucketList = mPastBuckets[slice.first];
-        bucketList.push_back(info);
+        if (slice.second.hasValue) {
+            info.mValue = slice.second.sum;
+            // it will auto create new vector of ValuebucketInfo if the key is not found.
+            auto& bucketList = mPastBuckets[slice.first];
+            bucketList.push_back(info);
+        }
     }
     VLOG("%d tainted pairs in the bucket", tainted);
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index b5f6429..9c5a56c 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -40,7 +40,7 @@
 public:
     ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t startTimeNs);
+                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs);
 
     virtual ~ValueMetricProducer();
 
@@ -115,7 +115,7 @@
     // for testing
     ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
                         const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int pullTagId, const int64_t startTimeNs,
+                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     // tagId for pulled data. -1 if this is not pulled
@@ -127,19 +127,22 @@
     typedef struct {
         // Pulled data always come in pair of <start, end>. This holds the value
         // for start. The diff (end - start) is added to sum.
-        long start;
+        int64_t start;
         // Whether the start data point is updated
         bool startUpdated;
         // If end data point comes before the start, record this pair as tainted
         // and the value is not added to the running sum.
         int tainted;
         // Running sum of known pairs in this bucket
-        long sum;
+        int64_t sum;
+        // If this dimension has any non-tainted value. If not, don't report the
+        // dimension.
+        bool hasValue;
     } Interval;
 
     std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
 
-    std::unordered_map<MetricDimensionKey, long> mCurrentFullBucket;
+    std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 566d34e..811a00e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -262,7 +262,8 @@
     return true;
 }
 
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
+                 const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
                  UidMap& uidMap, const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
@@ -277,8 +278,6 @@
     allMetricProducers.reserve(allMetricsCount);
     StatsPullerManager statsPullerManager;
 
-    uint64_t startTimeNs = timeBaseSec * NS_PER_SEC;
-
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
     for (int i = 0; i < config.count_metric_size(); i++) {
@@ -314,7 +313,7 @@
         }
 
         sp<MetricProducer> countProducer =
-                new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
+                new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -384,7 +383,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
-                trackerIndices[2], nesting, wizard, internalDimensions, startTimeNs);
+                trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -420,7 +419,7 @@
         }
 
         sp<MetricProducer> eventMetric =
-                new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
+                new EventMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
 
         allMetricProducers.push_back(eventMetric);
     }
@@ -467,7 +466,8 @@
         }
 
         sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
-                                                                   wizard, pullTagId, startTimeNs);
+                                                                   wizard, pullTagId,
+                                                                   timeBaseTimeNs, currentTimeNs);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -526,7 +526,7 @@
         }
 
         sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, wizard, pullTagId, startTimeNs);
+                key, metric, conditionIndex, wizard, pullTagId, timeBaseTimeNs, currentTimeNs);
         allMetricProducers.push_back(gaugeProducer);
     }
     for (int i = 0; i < config.no_report_metric_size(); ++i) {
@@ -601,11 +601,11 @@
 
 bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
                 const sp<AlarmMonitor>& periodicAlarmMonitor,
-                const long timeBaseSec, const long currentTimeSec,
+                const int64_t timeBaseNs, const int64_t currentTimeNs,
                 vector<sp<AlarmTracker>>& allAlarmTrackers) {
     unordered_map<int64_t, int> alarmTrackerMap;
-    uint64_t startMillis = (uint64_t)timeBaseSec * MS_PER_SEC;
-    uint64_t currentTimeMillis = (uint64_t)currentTimeSec * MS_PER_SEC;
+    int64_t startMillis = timeBaseNs / 1000 / 1000;
+    int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
     for (int i = 0; i < config.alarm_size(); i++) {
         const Alarm& alarm = config.alarm(i);
         if (alarm.offset_millis() <= 0) {
@@ -646,8 +646,9 @@
 
 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 int64_t timeBaseNs, const int64_t currentTimeNs,
+                      set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -673,7 +674,8 @@
         return false;
     }
 
-    if (!initMetrics(key, config, timeBaseSec, uidMap, logTrackerMap, conditionTrackerMap,
+    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap,
+                     logTrackerMap, conditionTrackerMap,
                      allAtomMatchers, allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
                      noReportMetricIds)) {
@@ -686,7 +688,7 @@
         return false;
     }
     if (!initAlarms(config, key, periodicAlarmMonitor,
-                    timeBaseSec, currentTimeSec, allPeriodicAlarmTrackers)) {
+                    timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
         ALOGE("initAlarms failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 0ebdcf9..d749bf4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -81,7 +81,9 @@
 //                          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, UidMap& uidMap,
+        const ConfigKey& key, const StatsdConfig& config,
+        const int64_t timeBaseTimeNs, const int64_t currentTimeNs,
+        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,
@@ -96,8 +98,9 @@
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
 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 int64_t timeBaseNs, const int64_t currentTimeNs,
+                      std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 36b24c8..eaa7bf1 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -97,9 +97,9 @@
 }
 
 message GaugeBucketInfo {
-  optional int64 start_bucket_nanos = 1;
+  optional int64 start_bucket_elapsed_nanos = 1;
 
-  optional int64 end_bucket_nanos = 2;
+  optional int64 end_bucket_elapsed_nanos = 2;
 
   repeated Atom atom = 3;
 
@@ -305,4 +305,6 @@
         optional int64 elapsed_timestamp_nanos = 2;
     }
     repeated SkippedLogEventStats skipped_log_event_stats = 13;
+
+    repeated int64 log_loss_stats = 14;
 }
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index fb8877a..91a40e3 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -178,6 +178,128 @@
     EXPECT_EQ(2, report.annotation(0).field_int32());
 }
 
+TEST(StatsLogProcessorTest, TestOutOfOrderLogs) {
+    // Setup simple config key corresponding to empty config.
+    sp<UidMap> m = new UidMap();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> subscriberAlarmMonitor;
+    int broadcastCount = 0;
+    StatsLogProcessor p(m, anomalyAlarmMonitor, subscriberAlarmMonitor, 0,
+                        [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
+
+    LogEvent event1(0, 1 /*logd timestamp*/, 1001 /*elapsedRealtime*/);
+    event1.init();
+
+    LogEvent event2(0, 2, 1002);
+    event2.init();
+
+    LogEvent event3(0, 3, 1005);
+    event3.init();
+
+    LogEvent event4(0, 4, 1004);
+    event4.init();
+
+    // <----- Reconnection happens
+
+    LogEvent event5(0, 5, 999);
+    event5.init();
+
+    LogEvent event6(0, 6, 2000);
+    event6.init();
+
+    // <----- Reconnection happens
+
+    LogEvent event7(0, 7, 3000);
+    event7.init();
+
+    // first event ever
+    p.OnLogEvent(&event1, true);
+    EXPECT_EQ(1UL, p.mLogCount);
+    EXPECT_EQ(1001LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1001LL, p.mLastTimestampSeen);
+
+    p.OnLogEvent(&event2, false);
+    EXPECT_EQ(2UL, p.mLogCount);
+    EXPECT_EQ(1002LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1002LL, p.mLastTimestampSeen);
+
+    p.OnLogEvent(&event3, false);
+    EXPECT_EQ(3UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1005LL, p.mLastTimestampSeen);
+
+    p.OnLogEvent(&event4, false);
+    EXPECT_EQ(4UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+    EXPECT_FALSE(p.mInReconnection);
+
+    // Reconnect happens, event1 out of buffer. Read event2
+    p.OnLogEvent(&event2, true);
+    EXPECT_EQ(4UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+    EXPECT_TRUE(p.mInReconnection);
+
+    p.OnLogEvent(&event3, false);
+    EXPECT_EQ(4UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+    EXPECT_TRUE(p.mInReconnection);
+
+    p.OnLogEvent(&event4, false);
+    EXPECT_EQ(4UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(1004LL, p.mLastTimestampSeen);
+    EXPECT_FALSE(p.mInReconnection);
+
+    // Fresh event comes.
+    p.OnLogEvent(&event5, false);
+    EXPECT_EQ(5UL, p.mLogCount);
+    EXPECT_EQ(1005LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(999LL, p.mLastTimestampSeen);
+
+    p.OnLogEvent(&event6, false);
+    EXPECT_EQ(6UL, p.mLogCount);
+    EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+
+    // Reconnect happens, read from event4
+    p.OnLogEvent(&event4, true);
+    EXPECT_EQ(6UL, p.mLogCount);
+    EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+    EXPECT_TRUE(p.mInReconnection);
+
+    p.OnLogEvent(&event5, false);
+    EXPECT_EQ(6UL, p.mLogCount);
+    EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+    EXPECT_TRUE(p.mInReconnection);
+
+    // Before we get out of reconnection state, it reconnects again.
+    p.OnLogEvent(&event5, true);
+    EXPECT_EQ(6UL, p.mLogCount);
+    EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+    EXPECT_TRUE(p.mInReconnection);
+
+    p.OnLogEvent(&event6, false);
+    EXPECT_EQ(6UL, p.mLogCount);
+    EXPECT_EQ(2000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(2000LL, p.mLastTimestampSeen);
+    EXPECT_FALSE(p.mInReconnection);
+    EXPECT_EQ(0, p.mLogLossCount);
+
+    // it reconnects again. All old events are gone. We lose CP.
+    p.OnLogEvent(&event7, true);
+    EXPECT_EQ(7UL, p.mLogCount);
+    EXPECT_EQ(3000LL, p.mLargestTimestampSeen);
+    EXPECT_EQ(3000LL, p.mLastTimestampSeen);
+    EXPECT_EQ(1, p.mLogLossCount);
+    EXPECT_FALSE(p.mInReconnection);
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
index 73c4e7b..9ea0b81 100644
--- a/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Alarm_e2e_test.cpp
@@ -51,7 +51,7 @@
     int64_t bucketStartTimeNs = 10000000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(2u, processor->mMetricsManagers.begin()->second->mAllPeriodicAlarmTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
index 93ecde5..c78d99e 100644
--- a/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_count_e2e_test.cpp
@@ -65,7 +65,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -168,7 +168,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
index e924b03..50da9e2 100644
--- a/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Anomaly_duration_sum_e2e_test.cpp
@@ -101,7 +101,7 @@
         TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -278,7 +278,7 @@
         TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
@@ -391,7 +391,7 @@
     config.mutable_alert(0)->set_refractory_period_secs(refractory_period_sec);
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     EXPECT_EQ(1u, processor->mMetricsManagers.begin()->second->mAllAnomalyTrackers.size());
diff --git a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
index 4dd0da8..4f035be 100644
--- a/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/Attribution_e2e_test.cpp
@@ -62,7 +62,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -204,7 +204,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
diff --git a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
index bb3ad64..b98dc60 100644
--- a/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ConfigTtl_e2e_test.cpp
@@ -68,7 +68,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
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 eb57d470..0758fd0 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
@@ -90,7 +90,7 @@
                     TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
             auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
             EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
             EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -404,7 +404,7 @@
                     TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
             auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
             EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
             EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -648,7 +648,7 @@
                 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
         auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
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 9729a2e..e74be1e 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
@@ -75,7 +75,7 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -283,7 +283,7 @@
     int64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     std::vector<AttributionNodeInternal> attributions1 = {CreateAttribution(111, "App1"),
@@ -465,7 +465,8 @@
         int64_t bucketSizeNs =
                 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        auto processor = CreateStatsLogProcessor(
+                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -666,7 +667,8 @@
         int64_t bucketSizeNs =
                 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
-        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        auto processor = CreateStatsLogProcessor(
+                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
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 4e2c36e..c32048b 100644
--- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
+++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_simple_cond_test.cpp
@@ -80,7 +80,7 @@
                     TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
             auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
             EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
             EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -363,7 +363,7 @@
                     TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
             auto processor = CreateStatsLogProcessor(
-                    bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                    bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
             EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
             EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -582,7 +582,7 @@
                 TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
 
         auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
new file mode 100644
index 0000000..9561fcf4
--- /dev/null
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -0,0 +1,396 @@
+// 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/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig(const GaugeMetric::SamplingType sampling_type) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = temperatureAtomMatcher;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = screenIsOffPredicate;
+
+    auto gaugeMetric = config.add_gauge_metric();
+    gaugeMetric->set_id(123456);
+    gaugeMetric->set_what(temperatureAtomMatcher.id());
+    gaugeMetric->set_condition(screenIsOffPredicate.id());
+    gaugeMetric->set_sampling_type(sampling_type);
+    gaugeMetric->mutable_gauge_fields_filter()->set_include_all(true);
+    *gaugeMetric->mutable_dimensions_in_what() =
+        CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+    gaugeMetric->set_bucket(FIVE_MINUTES);
+
+    return config;
+}
+
+}  // namespace
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvents) {
+    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(
+        baseTimeNs, configAddedTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int startBucketNum = processor->mMetricsManagers.begin()->second->
+            mAllMetricProducers[0]->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+                    second.front().intervalNs);
+    int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+            second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                        configAddedTimeNs + 55);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, nextPullTimeNs);
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + bucketSizeNs + 10);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + bucketSizeNs + 100);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs,
+              nextPullTimeNs);
+
+    processor->informPullAlarmFired(nextPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, nextPullTimeNs);
+
+    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                  configAddedTimeNs + 3 * bucketSizeNs + 2);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 3);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                  configAddedTimeNs + 5 * bucketSizeNs + 1);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(nextPullTimeNs + 2);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, nextPullTimeNs);
+
+    processor->informPullAlarmFired(nextPullTimeNs + 2);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(2 /* sensor name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(6, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 1,
+              data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(2).atom_size());
+    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs + 1,
+              data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(3).atom_size());
+    EXPECT_EQ(1, data.bucket_info(3).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs + 1,
+              data.bucket_info(3).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(3).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(3).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(4).atom_size());
+    EXPECT_EQ(1, data.bucket_info(4).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
+              data.bucket_info(4).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(4).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(4).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(5).atom_size());
+    EXPECT_EQ(1, data.bucket_info(5).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs + 2,
+              data.bucket_info(5).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(5).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(5).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(5).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(5).atom(0).temperature().temperature_dc(), 0);
+}
+
+TEST(GaugeMetricE2eTest, TestAllConditionChangesSamplePulledEvents) {
+    auto config = CreateStatsdConfig(GaugeMetric::ALL_CONDITION_CHANGES);
+    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(
+        baseTimeNs, configAddedTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int startBucketNum = processor->mMetricsManagers.begin()->second->
+            mAllMetricProducers[0]->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                        configAddedTimeNs + 55);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + bucketSizeNs + 10);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + bucketSizeNs + 100);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                  configAddedTimeNs + 3 * bucketSizeNs + 2);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                  configAddedTimeNs + 5 * bucketSizeNs + 1);
+    processor->OnLogEvent(screenOffEvent.get());
+    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                  configAddedTimeNs + 5 * bucketSizeNs + 3);
+    processor->OnLogEvent(screenOnEvent.get());
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                  configAddedTimeNs + 5 * bucketSizeNs + 10);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 8 * bucketSizeNs + 10, false, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(2 /* sensor name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs + 100,
+              data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(2, data.bucket_info(2).atom_size());
+    EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 1,
+              data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs + 10,
+              data.bucket_info(2).elapsed_timestamp_nanos(1));
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+    EXPECT_FALSE(data.bucket_info(2).atom(1).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(1).temperature().temperature_dc(), 0);
+}
+
+
+TEST(GaugeMetricE2eTest, TestRandomSamplePulledEvent_LateAlarm) {
+    auto config = CreateStatsdConfig(GaugeMetric::RANDOM_ONE_SAMPLE);
+    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(
+        baseTimeNs, configAddedTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int startBucketNum = processor->mMetricsManagers.begin()->second->
+            mAllMetricProducers[0]->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+                    second.front().intervalNs);
+    int64_t& nextPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+            second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, nextPullTimeNs);
+
+    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                        configAddedTimeNs + 55);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + bucketSizeNs + 10);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    // Pulling alarm arrives one bucket size late.
+    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 3 * bucketSizeNs, nextPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + 3 * bucketSizeNs + 11);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives more than one bucket size late.
+    processor->informPullAlarmFired(nextPullTimeNs + bucketSizeNs + 12);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 5 * bucketSizeNs, nextPullTimeNs);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    EXPECT_GT((int)gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(2 /* sensor name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(0).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(0).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(configAddedTimeNs + 3 * bucketSizeNs + 11,
+              data.bucket_info(1).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(configAddedTimeNs + 55, data.bucket_info(0).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(1).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(1).atom(0).temperature().temperature_dc(), 0);
+
+    EXPECT_EQ(1, data.bucket_info(2).atom_size());
+    EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs + 12,
+              data.bucket_info(2).elapsed_timestamp_nanos(0));
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_FALSE(data.bucket_info(2).atom(0).temperature().sensor_name().empty());
+    EXPECT_GT(data.bucket_info(2).atom(0).temperature().temperature_dc(), 0);
+
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 18a0485..d79cb33 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -96,7 +96,8 @@
             TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
 
         ConfigKey cfgKey;
-        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        auto processor = CreateStatsLogProcessor(
+                bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
         EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -169,9 +170,11 @@
             EXPECT_EQ(2, data.bucket_info(0).atom_size());
             EXPECT_EQ(2, data.bucket_info(0).elapsed_timestamp_nanos_size());
             EXPECT_EQ(2, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
-            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
-            EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
+            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(0).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(0).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name2",
                       data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
@@ -186,8 +189,10 @@
             EXPECT_EQ(1, data.bucket_info(1).atom_size());
             EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
-            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
-            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(1).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(1).end_bucket_elapsed_nanos());
             EXPECT_EQ(AppStartOccurred::WARM,
                       data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
@@ -199,9 +204,9 @@
             EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
             EXPECT_EQ(2, data.bucket_info(2).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-                      data.bucket_info(2).start_bucket_nanos());
+                      data.bucket_info(2).start_bucket_elapsed_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-                      data.bucket_info(2).end_bucket_nanos());
+                      data.bucket_info(2).end_bucket_elapsed_nanos());
             EXPECT_EQ(AppStartOccurred::COLD,
                       data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
@@ -218,9 +223,11 @@
             EXPECT_EQ(1, data.bucket_info(0).atom_size());
             EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_nanos());
-            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
-            EXPECT_EQ(AppStartOccurred::HOT, data.bucket_info(0).atom(0).app_start_occurred().type());
+            EXPECT_EQ(bucketStartTimeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(0).end_bucket_elapsed_nanos());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(0).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name2",
                       data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
@@ -229,8 +236,10 @@
             EXPECT_EQ(1, data.bucket_info(1).atom_size());
             EXPECT_EQ(1, data.bucket_info(1).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(1).wall_clock_timestamp_nanos_size());
-            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, data.bucket_info(1).start_bucket_nanos());
-            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(1).end_bucket_nanos());
+            EXPECT_EQ(bucketStartTimeNs + bucketSizeNs,
+                      data.bucket_info(1).start_bucket_elapsed_nanos());
+            EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                      data.bucket_info(1).end_bucket_elapsed_nanos());
             EXPECT_EQ(AppStartOccurred::WARM,
                       data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
@@ -242,9 +251,9 @@
             EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
             EXPECT_EQ(1, data.bucket_info(2).wall_clock_timestamp_nanos_size());
             EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
-                      data.bucket_info(2).start_bucket_nanos());
+                      data.bucket_info(2).start_bucket_elapsed_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
-                      data.bucket_info(2).end_bucket_nanos());
+                      data.bucket_info(2).end_bucket_elapsed_nanos());
             EXPECT_EQ(AppStartOccurred::COLD,
                       data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
@@ -264,8 +273,10 @@
         EXPECT_EQ(1, data.bucket_info(0).atom_size());
         EXPECT_EQ(1, data.bucket_info(0).elapsed_timestamp_nanos_size());
         EXPECT_EQ(1, data.bucket_info(0).wall_clock_timestamp_nanos_size());
-        EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_nanos());
-        EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_nanos());
+        EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs,
+                  data.bucket_info(0).start_bucket_elapsed_nanos());
+        EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
+                  data.bucket_info(0).end_bucket_elapsed_nanos());
         EXPECT_EQ(AppStartOccurred::COLD, data.bucket_info(0).atom(0).app_start_occurred().type());
         EXPECT_EQ("activity_name7",
                   data.bucket_info(0).atom(0).app_start_occurred().activity_name());
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
index 1952a6f..4ace382 100644
--- a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -107,7 +107,7 @@
         TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
@@ -224,7 +224,8 @@
             TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
 
     ConfigKey cfgKey;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(
+            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
 
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index d4892ed..5499ee3 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -35,7 +35,7 @@
     config.SerializeToString(&str);
     std::vector<uint8_t> configAsVec(str.begin(), str.end());
     bool success;
-    service.addConfiguration(kConfigKey, configAsVec, &success);
+    service.addConfiguration(kConfigKey, configAsVec);
 }
 
 ConfigMetricsReport GetReports(StatsService& service) {
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
new file mode 100644
index 0000000..62b6fcc
--- /dev/null
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -0,0 +1,260 @@
+// 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/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateStatsdConfig() {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root.
+    auto temperatureAtomMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = temperatureAtomMatcher;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = screenIsOffPredicate;
+
+    auto valueMetric = config.add_value_metric();
+    valueMetric->set_id(123456);
+    valueMetric->set_what(temperatureAtomMatcher.id());
+    valueMetric->set_condition(screenIsOffPredicate.id());
+    *valueMetric->mutable_value_field() =
+        CreateDimensions(android::util::TEMPERATURE, {3/* temperature degree field */ });
+    *valueMetric->mutable_dimensions_in_what() =
+        CreateDimensions(android::util::TEMPERATURE, {2/* sensor name field */ });
+    valueMetric->set_bucket(FIVE_MINUTES);
+
+    return config;
+}
+
+}  // namespace
+
+TEST(ValueMetricE2eTest, TestPulledEvents) {
+    auto config = CreateStatsdConfig();
+    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(
+        baseTimeNs, configAddedTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int startBucketNum = processor->mMetricsManagers.begin()->second->
+            mAllMetricProducers[0]->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+                    second.front().intervalNs);
+    int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+            second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                        configAddedTimeNs + 55);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + 65);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + 75);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 2 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + 2 * bucketSizeNs + 15);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + 4 * bucketSizeNs + 11);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 7 * bucketSizeNs + 10, false, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::ValueMetricDataWrapper valueMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+    EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+    auto data = valueMetrics.data(0);
+    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(2 /* sensor name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(5, data.bucket_info_size());
+
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(0).has_value());
+
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(1).has_value());
+
+    EXPECT_EQ(baseTimeNs + 4 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 5 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(2).has_value());
+
+    EXPECT_EQ(baseTimeNs + 6 * bucketSizeNs, data.bucket_info(3).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(3).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(3).has_value());
+
+    EXPECT_EQ(baseTimeNs + 7 * bucketSizeNs, data.bucket_info(4).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(4).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(4).has_value());
+}
+
+TEST(ValueMetricE2eTest, TestPulledEvents_LateAlarm) {
+    auto config = CreateStatsdConfig();
+    int64_t baseTimeNs = 10 * NS_PER_SEC;
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(
+        baseTimeNs, configAddedTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int startBucketNum = processor->mMetricsManagers.begin()->second->
+            mAllMetricProducers[0]->getCurrentBucketNum();
+    EXPECT_GT(startBucketNum, (int64_t)0);
+
+    // When creating the config, the gauge metric producer should register the alarm at the
+    // end of the current bucket.
+    EXPECT_EQ((size_t)1, StatsPullerManagerImpl::GetInstance().mReceivers.size());
+    EXPECT_EQ(bucketSizeNs,
+              StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+                    second.front().intervalNs);
+    int64_t& expectedPullTimeNs = StatsPullerManagerImpl::GetInstance().mReceivers.begin()->
+            second.front().nextPullTimeNs;
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + bucketSizeNs, expectedPullTimeNs);
+
+    // Screen off/on/off events.
+    auto screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                        configAddedTimeNs + 55);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    auto screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + 65);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + 75);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives late by 2 buckets and 1 ns.
+    processor->informPullAlarmFired(expectedPullTimeNs + 2 * bucketSizeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 4 * bucketSizeNs, expectedPullTimeNs);
+
+    screenOnEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_ON,
+                                                       configAddedTimeNs + 4 * bucketSizeNs + 65);
+    processor->OnLogEvent(screenOnEvent.get());
+
+    // Pulling alarm arrives late by one bucket size + 21ns.
+    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 6 * bucketSizeNs, expectedPullTimeNs);
+
+    screenOffEvent = CreateScreenStateChangedEvent(android::view::DISPLAY_STATE_OFF,
+                                                   configAddedTimeNs + 6 * bucketSizeNs + 31);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    processor->informPullAlarmFired(expectedPullTimeNs + bucketSizeNs + 21);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 8 * bucketSizeNs, expectedPullTimeNs);
+
+    processor->informPullAlarmFired(expectedPullTimeNs + 1);
+    EXPECT_EQ(baseTimeNs + startBucketNum * bucketSizeNs + 9 * bucketSizeNs, expectedPullTimeNs);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 9 * bucketSizeNs + 10, false, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    EXPECT_EQ(1, reports.reports_size());
+    EXPECT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::ValueMetricDataWrapper valueMetrics;
+    sortMetricDataByDimensionsValue(
+            reports.reports(0).metrics(0).value_metrics(), &valueMetrics);
+    EXPECT_GT((int)valueMetrics.data_size(), 1);
+
+    auto data = valueMetrics.data(0);
+    EXPECT_EQ(android::util::TEMPERATURE, data.dimensions_in_what().field());
+    EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(2 /* sensor name field */,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+    EXPECT_FALSE(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str().empty());
+    EXPECT_EQ(3, data.bucket_info_size());
+
+    EXPECT_EQ(baseTimeNs + 2 * bucketSizeNs, data.bucket_info(0).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 3 * bucketSizeNs, data.bucket_info(0).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(0).has_value());
+
+    EXPECT_EQ(baseTimeNs + 8 * bucketSizeNs, data.bucket_info(1).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(1).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(1).has_value());
+
+    EXPECT_EQ(baseTimeNs + 9 * bucketSizeNs, data.bucket_info(2).start_bucket_elapsed_nanos());
+    EXPECT_EQ(baseTimeNs + 10 * bucketSizeNs, data.bucket_info(2).end_bucket_elapsed_nanos());
+    EXPECT_TRUE(data.bucket_info(2).has_value());
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
index f2d47c7..55bf4be 100644
--- a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -121,7 +121,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
@@ -154,7 +154,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
@@ -188,7 +188,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
@@ -231,7 +231,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
@@ -256,7 +256,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
@@ -284,7 +284,7 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs =
             TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
-    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     FeedEvents(config, processor);
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index a1f865d..698ce72 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -67,7 +67,7 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
@@ -144,7 +144,7 @@
             make_shared<StrictMock<MockStatsPullerManager>>();
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       -1 /* -1 means no pulling */, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
 
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -228,7 +228,7 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
@@ -297,7 +297,7 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId,
-                                      bucketStartTimeNs, pullerManager);
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
@@ -389,7 +389,7 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
@@ -433,7 +433,7 @@
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.setBucketSize(60 * NS_PER_SEC);
 
     Alert alert;
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a2bb734..81d8892 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -68,7 +68,7 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
@@ -90,8 +90,7 @@
     EXPECT_EQ(0, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(11, curInterval.start);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     allData.clear();
     event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -108,7 +107,7 @@
     EXPECT_EQ(0, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
     EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
 
     allData.clear();
@@ -125,7 +124,7 @@
     EXPECT_EQ(0, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
     EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
 }
 
@@ -169,7 +168,7 @@
             }));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
@@ -222,7 +221,7 @@
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -278,7 +277,7 @@
                 return true;
             }));
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
@@ -321,7 +320,7 @@
             make_shared<StrictMock<MockStatsPullerManager>>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
@@ -369,7 +368,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      -1 /*not pulled*/, bucketStartTimeNs);
+                                      -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -448,7 +447,7 @@
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      tagId, bucketStartTimeNs, pullerManager);
+                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     vector<shared_ptr<LogEvent>> allData;
@@ -464,15 +463,13 @@
     // has one slice
     EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
-    valueProducer.setBucketSize(60 * NS_PER_SEC);
 
     // startUpdated:true tainted:0 sum:0 start:11
     EXPECT_EQ(true, curInterval.startUpdated);
     EXPECT_EQ(0, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(11, curInterval.start);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // pull 2 at correct time
     allData.clear();
@@ -490,7 +487,7 @@
     EXPECT_EQ(0, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
     EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
 
     // pull 3 come late.
@@ -512,11 +509,8 @@
     EXPECT_EQ(36, curInterval.start);
     EXPECT_EQ(0, curInterval.sum);
     EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(4UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
-    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].mValue);
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[2].mValue);
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[3].mValue);
+    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
 }
 
 /*
@@ -562,7 +556,7 @@
             }));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
@@ -582,9 +576,7 @@
     EXPECT_EQ(false, curInterval.startUpdated);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered.
     // since the condition turned to off before this pull finish, it has no effect
@@ -601,9 +593,7 @@
     EXPECT_EQ(false, curInterval.startUpdated);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
 /*
@@ -660,7 +650,7 @@
             }));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
@@ -680,9 +670,7 @@
     EXPECT_EQ(false, curInterval.startUpdated);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // condition changed to true again, before the pull alarm is delivered
     valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
@@ -691,9 +679,7 @@
     EXPECT_EQ(130, curInterval.start);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Now the alarm is delivered, but it is considered late, it has no effect
     vector<shared_ptr<LogEvent>> allData;
@@ -710,9 +696,7 @@
     EXPECT_EQ(130, curInterval.start);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
 /*
@@ -758,7 +742,7 @@
             }));
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
-                                      pullerManager);
+                                      bucketStartTimeNs, pullerManager);
     valueProducer.setBucketSize(60 * NS_PER_SEC);
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
 
@@ -779,9 +763,7 @@
     EXPECT_EQ(false, curInterval.startUpdated);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 
     // Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
     // so this one comes even later
@@ -798,9 +780,7 @@
     EXPECT_EQ(false, curInterval.startUpdated);
     EXPECT_EQ(1, curInterval.tainted);
     EXPECT_EQ(0, curInterval.sum);
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
-    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 649c399..1264909 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -18,6 +18,7 @@
 namespace os {
 namespace statsd {
 
+
 AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
@@ -26,6 +27,10 @@
     return atom_matcher;
 }
 
+AtomMatcher CreateTemperatureAtomMatcher() {
+    return CreateSimpleAtomMatcher("TemperatureMatcher", android::util::TEMPERATURE);
+}
+
 AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
                                                       ScheduledJobStateChanged::State state) {
     AtomMatcher atom_matcher;
@@ -444,8 +449,8 @@
     return logEvent;
 }
 
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
-                                              const ConfigKey& key) {
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
+                                              const StatsdConfig& config, const ConfigKey& key) {
     sp<UidMap> uidMap = new UidMap();
     sp<AlarmMonitor> anomalyAlarmMonitor =
         new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
@@ -454,8 +459,8 @@
         new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
                 [](const sp<IStatsCompanionService>&){});
     sp<StatsLogProcessor> processor = new StatsLogProcessor(
-        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseSec, [](const ConfigKey&){});
-    processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
+        uidMap, anomalyAlarmMonitor, periodicAlarmMonitor, timeBaseNs, [](const ConfigKey&){});
+    processor->OnConfigUpdated(currentTimeNs, key, config);
     return processor;
 }
 
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 1ac630c..6ecca46 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -28,6 +28,9 @@
 // Create AtomMatcher proto to simply match a specific atom type.
 AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
 
+// Create AtomMatcher proto for temperature atom.
+AtomMatcher CreateTemperatureAtomMatcher();
+
 // Create AtomMatcher proto for scheduled job state changed.
 AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
 
@@ -172,8 +175,9 @@
 AttributionNodeInternal CreateAttribution(const int& uid, const string& tag);
 
 // Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
-                                              const ConfigKey& key);
+sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs,
+                                              const int64_t currentTimeNs,
+                                              const StatsdConfig& config, const ConfigKey& key);
 
 // Util function to sort the log events by timestamp.
 void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 0e6c933..2b7da6a 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -19,6 +19,7 @@
 import android.app.PendingIntent;
 import android.app.IntentService;
 import android.app.StatsManager;
+import android.app.StatsManager.StatsUnavailableException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -171,12 +172,16 @@
                     return;
                 }
                 if (mStatsManager != null) {
-                    byte[] data = mStatsManager.getData(CONFIG_ID);
-                    if (data != null) {
-                        displayData(data);
-                    } else {
-                        mReportText.setText("Failed!");
+                    try {
+                        byte[] data = mStatsManager.getReports(CONFIG_ID);
+                        if (data != null) {
+                            displayData(data);
+                            return;
+                        }
+                    } catch (StatsUnavailableException e) {
+                        Log.e(TAG, "Failed to get data from statsd", e);
                     }
+                    mReportText.setText("Failed!");
                 }
             }
         });
@@ -194,10 +199,11 @@
                     byte[] config = new byte[inputStream.available()];
                     inputStream.read(config);
                     if (mStatsManager != null) {
-                        if (mStatsManager.addConfiguration(CONFIG_ID, config)) {
+                        try {
+                            mStatsManager.addConfig(CONFIG_ID, config);
                             Toast.makeText(
                                     MainActivity.this, "Config pushed", Toast.LENGTH_LONG).show();
-                        } else {
+                        } catch (StatsUnavailableException | IllegalArgumentException e) {
                             Toast.makeText(MainActivity.this, "Config push FAILED!",
                                     Toast.LENGTH_LONG).show();
                         }
@@ -218,11 +224,12 @@
                         return;
                     }
                     if (mStatsManager != null) {
-                        if (mStatsManager.setDataFetchOperation(CONFIG_ID, pi)) {
+                        try {
+                            mStatsManager.setFetchReportsOperation(pi, CONFIG_ID);
                             Toast.makeText(MainActivity.this,
                                     "Receiver specified to pending intent", Toast.LENGTH_LONG)
                                     .show();
-                        } else {
+                        } catch (StatsUnavailableException e) {
                             Toast.makeText(MainActivity.this, "Statsd did not set receiver",
                                     Toast.LENGTH_LONG)
                                     .show();
@@ -241,10 +248,11 @@
                         return;
                     }
                     if (mStatsManager != null) {
-                        if (mStatsManager.setDataFetchOperation(CONFIG_ID, null)) {
+                        try {
+                            mStatsManager.setFetchReportsOperation(null, CONFIG_ID);
                             Toast.makeText(MainActivity.this, "Receiver remove", Toast.LENGTH_LONG)
                                     .show();
-                        } else {
+                        } catch (StatsUnavailableException e) {
                             Toast.makeText(MainActivity.this, "Statsd did not remove receiver",
                                     Toast.LENGTH_LONG)
                                     .show();
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
index bed4d98..769f78c 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/LoadtestActivity.java
@@ -355,7 +355,13 @@
             return null;
         }
         if (mStatsManager != null) {
-            byte[] data = mStatsManager.getMetadata();
+            byte[] data;
+            try {
+                data = mStatsManager.getStatsMetadata();
+            } catch (StatsManager.StatsUnavailableException e) {
+                Log.e(TAG, "Failed to get data from statsd", e);
+                return null;
+            }
             if (data != null) {
                 StatsdStatsReport report = null;
                 boolean good = false;
@@ -375,7 +381,13 @@
             return null;
         }
         if (mStatsManager != null) {
-            byte[] data = mStatsManager.getData(ConfigFactory.CONFIG_ID);
+            byte[] data;
+            try {
+                data = mStatsManager.getReports(ConfigFactory.CONFIG_ID);
+            } catch (StatsManager.StatsUnavailableException e) {
+                Log.e(TAG, "Failed to get data from statsd", e);
+                return null;
+            }
             if (data != null) {
                 ConfigMetricsReportList reports = null;
                 try {
@@ -563,10 +575,11 @@
         // TODO: Clear all configs instead of specific ones.
         if (mStatsManager != null) {
             if (mStarted) {
-                if (!mStatsManager.removeConfiguration(ConfigFactory.CONFIG_ID)) {
+                try {
+                    mStatsManager.removeConfig(ConfigFactory.CONFIG_ID);
                     Log.d(TAG, "Removed loadtest statsd configs.");
-                } else {
-                    Log.d(TAG, "Failed to remove loadtest configs.");
+                } catch (StatsManager.StatsUnavailableException e) {
+                    Log.e(TAG, "Failed to remove loadtest configs.", e);
                 }
             }
         }
@@ -574,12 +587,13 @@
 
     private boolean setConfig(ConfigFactory.ConfigMetadata configData) {
         if (mStatsManager != null) {
-            if (mStatsManager.addConfiguration(ConfigFactory.CONFIG_ID, configData.bytes)) {
+            try {
+                mStatsManager.addConfig(ConfigFactory.CONFIG_ID, configData.bytes);
                 mNumMetrics = configData.numMetrics;
                 Log.d(TAG, "Config pushed to statsd");
                 return true;
-            } else {
-                Log.d(TAG, "Failed to push config to statsd");
+            } catch (StatsManager.StatsUnavailableException | IllegalArgumentException e) {
+                Log.e(TAG, "Failed to push config to statsd", e);
             }
         }
         return false;
diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt
index 87fb998..7177336 100644
--- a/config/boot-image-profile.txt
+++ b/config/boot-image-profile.txt
@@ -32326,7 +32326,7 @@
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getDisplayFrame(Landroid/view/IWindow;Landroid/graphics/Rect;)V
 HSPLandroid/view/IWindowSession$Stub$Proxy;->getInTouchMode()Z
 HSPLandroid/view/IWindowSession$Stub$Proxy;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V
-HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 HSPLandroid/view/IWindowSession$Stub$Proxy;->setInsets(Landroid/view/IWindow;ILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Region;)V
 HSPLandroid/view/IWindowSession$Stub$Proxy;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V
 HSPLandroid/view/IWindowSession$Stub$Proxy;->setWallpaperPosition(Landroid/os/IBinder;FFFF)V
@@ -32349,7 +32349,7 @@
 HSPLandroid/view/IWindowSession;->pokeDrawLock(Landroid/os/IBinder;)V
 HSPLandroid/view/IWindowSession;->prepareDrag(Landroid/view/IWindow;IIILandroid/view/Surface;)Landroid/os/IBinder;
 HSPLandroid/view/IWindowSession;->prepareToReplaceWindows(Landroid/os/IBinder;Z)V
-HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 HSPLandroid/view/IWindowSession;->remove(Landroid/view/IWindow;)V
 HSPLandroid/view/IWindowSession;->reportDropResult(Landroid/view/IWindow;Z)V
 HSPLandroid/view/IWindowSession;->sendWallpaperCommand(Landroid/os/IBinder;Ljava/lang/String;IIILandroid/os/Bundle;Z)Landroid/os/Bundle;
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 44543a7..942a9dd 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -2,8 +2,19 @@
 Landroid/accounts/IAccountAuthenticatorResponse$Stub;-><init>()V
 Landroid/accounts/IAccountManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/accounts/IAccountManager;
 Landroid/accounts/IAccountManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/animation/KeyframeSet;-><init>([Landroid/animation/Keyframe;)V
+Landroid/animation/KeyframeSet;->ofFloat([F)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofInt([I)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofKeyframe([Landroid/animation/Keyframe;)Landroid/animation/KeyframeSet;
+Landroid/animation/KeyframeSet;->ofObject([Ljava/lang/Object;)Landroid/animation/KeyframeSet;
 Landroid/animation/LayoutTransition;->cancel(I)V
 Landroid/animation/LayoutTransition;->cancel()V
+Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;-><init>(Landroid/util/Property;[F)V
+Landroid/animation/PropertyValuesHolder$FloatPropertyValuesHolder;-><init>(Ljava/lang/String;[F)V
+Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;-><init>(Landroid/util/Property;[I)V
+Landroid/animation/PropertyValuesHolder$IntPropertyValuesHolder;-><init>(Ljava/lang/String;[I)V
+Landroid/animation/PropertyValuesHolder$MultiFloatValuesHolder;-><init>(Ljava/lang/String;Landroid/animation/TypeConverter;Landroid/animation/TypeEvaluator;[Ljava/lang/Object;)V
+Landroid/animation/PropertyValuesHolder$MultiIntValuesHolder;-><init>(Ljava/lang/String;Landroid/animation/TypeConverter;Landroid/animation/TypeEvaluator;[Ljava/lang/Object;)V
 Landroid/animation/ValueAnimator;->animateValue(F)V
 Landroid/animation/ValueAnimator;->sDurationScale:F
 Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
@@ -51,6 +62,7 @@
 Landroid/app/Activity;->mWindow:Landroid/view/Window;
 Landroid/app/Activity;->mWindowManager:Landroid/view/WindowManager;
 Landroid/app/ActivityOptions;->makeMultiThumbFutureAspectScaleAnimation(Landroid/content/Context;Landroid/os/Handler;Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/app/ActivityOptions$OnAnimationStartedListener;Z)Landroid/app/ActivityOptions;
+Landroid/app/ActivityOptions;->startSharedElementAnimation(Landroid/view/Window;[Landroid/util/Pair;)Landroid/app/ActivityOptions;
 Landroid/app/Activity;->setDisablePreviewScreenshots(Z)V
 Landroid/app/Activity;->setPersistent(Z)V
 Landroid/app/ActivityThread$ActivityClientRecord;->activityInfo:Landroid/content/pm/ActivityInfo;
@@ -124,6 +136,7 @@
 Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->performNewIntents(Landroid/os/IBinder;Ljava/util/List;Z)V
 Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V
+Landroid/app/ActivityThread;->printRow(Ljava/io/PrintWriter;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/app/ActivityThread$ProviderClientRecord;->mHolder:Landroid/app/ContentProviderHolder;
 Landroid/app/ActivityThread$ProviderClientRecord;->mLocalProvider:Landroid/content/ContentProvider;
 Landroid/app/ActivityThread$ProviderClientRecord;->mProvider:Landroid/content/IContentProvider;
@@ -136,6 +149,7 @@
 Landroid/app/ActivityThread$ServiceArgsData;->token:Landroid/os/IBinder;
 Landroid/app/ActivityThread;->sPackageManager:Landroid/content/pm/IPackageManager;
 Landroid/app/ActivityThread;->startActivityNow(Landroid/app/Activity;Ljava/lang/String;Landroid/content/Intent;Landroid/content/pm/ActivityInfo;Landroid/os/IBinder;Landroid/os/Bundle;Landroid/app/Activity$NonConfigurationInstances;)Landroid/app/Activity;
+Landroid/app/admin/DevicePolicyManager;->getProfileOwnerAsUser(I)Landroid/content/ComponentName;
 Landroid/app/admin/DevicePolicyManager;->getTrustAgentConfiguration(Landroid/content/ComponentName;Landroid/content/ComponentName;I)Ljava/util/List;
 Landroid/app/admin/DevicePolicyManager;->packageHasActiveAdmins(Ljava/lang/String;I)Z
 Landroid/app/admin/DevicePolicyManager;->setActiveAdmin(Landroid/content/ComponentName;ZI)V
@@ -145,6 +159,7 @@
 Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_packageHasActiveAdmins:I
 Landroid/app/admin/IDevicePolicyManager$Stub;->TRANSACTION_removeActiveAdmin:I
 Landroid/app/admin/SecurityLog$SecurityEvent;-><init>([B)V
+Landroid/app/admin/SecurityLog;->writeEvent(I[Ljava/lang/Object;)I
 Landroid/app/AlarmManager;->FLAG_ALLOW_WHILE_IDLE_UNRESTRICTED:I
 Landroid/app/AlarmManager;->FLAG_IDLE_UNTIL:I
 Landroid/app/AlarmManager;->FLAG_STANDALONE:I
@@ -153,6 +168,8 @@
 Landroid/app/AlarmManager;->WINDOW_EXACT:J
 Landroid/app/AlarmManager;->WINDOW_HEURISTIC:J
 Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
+Landroid/app/AlertDialog$Builder;->setRecycleOnMeasureEnabled(Z)Landroid/app/AlertDialog$Builder;
+Landroid/app/AlertDialog$Builder;->setView(Landroid/view/View;IIII)Landroid/app/AlertDialog$Builder;
 Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
 Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
 Landroid/app/AppGlobals;->getInitialPackage()Ljava/lang/String;
@@ -194,12 +211,14 @@
 Landroid/app/AppOpsManager;->OP_READ_CONTACTS:I
 Landroid/app/AppOpsManager;->OP_READ_PHONE_STATE:I
 Landroid/app/AppOpsManager;->OP_READ_SMS:I
+Landroid/app/AppOpsManager;->OP_RUN_IN_BACKGROUND:I
 Landroid/app/AppOpsManager;->OP_VIBRATE:I
 Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I
 Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I
 Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
 Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I
 Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
+Landroid/app/backup/AbsoluteFileBackupHelper;-><init>(Landroid/content/Context;[Ljava/lang/String;)V
 Landroid/app/backup/BackupDataInput$EntityHeader;->dataSize:I
 Landroid/app/backup/BackupDataInput$EntityHeader;->key:Ljava/lang/String;
 Landroid/app/backup/BackupDataInputStream;->dataSize:I
@@ -207,6 +226,8 @@
 Landroid/app/backup/BackupDataOutput;->mBackupWriter:J
 Landroid/app/backup/BackupHelperDispatcher$Header;->chunkSize:I
 Landroid/app/backup/BackupHelperDispatcher$Header;->keyPrefix:Ljava/lang/String;
+Landroid/app/backup/BlobBackupHelper;-><init>(I[Ljava/lang/String;)V
+Landroid/app/backup/FileBackupHelperBase;->writeNewStateDescription(Landroid/os/ParcelFileDescriptor;)V
 Landroid/app/backup/FullBackup;->backupToTar(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/backup/FullBackupDataOutput;)I
 Landroid/app/backup/FullBackupDataOutput;->addSize(J)V
 Landroid/app/backup/FullBackupDataOutput;-><init>(Landroid/os/ParcelFileDescriptor;)V
@@ -242,7 +263,10 @@
 Landroid/app/Dialog;->mListenersHandler:Landroid/os/Handler;
 Landroid/app/Dialog;->mOwnerActivity:Landroid/app/Activity;
 Landroid/app/Dialog;->mShowMessage:Landroid/os/Message;
+Landroid/app/DownloadManager;->forceDownload([J)V
+Landroid/app/DownloadManager;->markRowDeleted([J)I
 Landroid/app/DownloadManager$Request;->mUri:Landroid/net/Uri;
+Landroid/app/DownloadManager;->restartDownload([J)V
 Landroid/app/FragmentManagerImpl;->mAdded:Ljava/util/ArrayList;
 Landroid/app/FragmentManagerImpl;->noteStateNotSaved()V
 Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
@@ -260,6 +284,7 @@
 Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V
 Landroid/app/IActivityManager;->requestBugReport(I)V
 Landroid/app/IActivityManager;->resumeAppSwitches()V
+Landroid/app/IActivityManager;->setActivityController(Landroid/app/IActivityController;Z)V
 Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V
 Landroid/app/IActivityManager;->setTaskResizeable(II)V
 Landroid/app/IActivityManager;->stopService(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;I)I
@@ -329,6 +354,7 @@
 Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList;
 Landroid/app/Notification$Builder;->makePublicContentView()Landroid/widget/RemoteViews;
 Landroid/app/Notification$Builder;->setChannel(Ljava/lang/String;)Landroid/app/Notification$Builder;
+Landroid/app/Notification;-><init>(Landroid/content/Context;ILjava/lang/CharSequence;JLjava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/content/Intent;)V
 Landroid/app/Notification;->isGroupSummary()Z
 Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
 Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V
@@ -374,6 +400,7 @@
 Landroid/app/trust/ITrustManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/usage/UsageStatsManager;->mService:Landroid/app/usage/IUsageStatsManager;
 Landroid/app/usage/UsageStats;->mLastEvent:I
+Landroid/app/usage/UsageStats;->mLaunchCount:I
 Landroid/app/usage/UsageStats;->mTotalTimeInForeground:J
 Landroid/app/Vr2dDisplayProperties$Builder;->build()Landroid/app/Vr2dDisplayProperties;
 Landroid/app/Vr2dDisplayProperties$Builder;-><init>()V
@@ -414,15 +441,29 @@
 Landroid/bluetooth/BluetoothGatt;->mAuthRetryState:I
 Landroid/bluetooth/BluetoothGatt;->refresh()Z
 Landroid/bluetooth/BluetoothHeadset;->close()V
+Landroid/bluetooth/BluetoothHeadset;->connectAudio()Z
+Landroid/bluetooth/BluetoothHeadset;->disconnectAudio()Z
+Landroid/bluetooth/BluetoothHeadset;->startScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothHeadset;->stopScoUsingVirtualVoiceCall(Landroid/bluetooth/BluetoothDevice;)Z
 Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z
+Landroid/bluetooth/BluetoothPan;->close()V
+Landroid/bluetooth/BluetoothPan;->connect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->doBind()Z
+Landroid/bluetooth/BluetoothPan;-><init>(Landroid/content/Context;Landroid/bluetooth/BluetoothProfile$ServiceListener;)V
+Landroid/bluetooth/BluetoothPan;->isEnabled()Z
 Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z
+Landroid/bluetooth/BluetoothPan;->isValidDevice(Landroid/bluetooth/BluetoothDevice;)Z
+Landroid/bluetooth/BluetoothPan;->log(Ljava/lang/String;)V
 Landroid/bluetooth/BluetoothPan;->setBluetoothTethering(Z)V
+Landroid/bluetooth/BluetoothProfile;->PAN:I
 Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
 Landroid/bluetooth/IBluetooth;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/IBluetoothManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/bluetooth/IBluetooth$Stub;->asInterface(Landroid/os/IBinder;)Landroid/bluetooth/IBluetooth;
 Landroid/bluetooth/IBluetooth$Stub$Proxy;->getAddress()Ljava/lang/String;
 Landroid/bluetooth/le/ScanRecord;->parseFromBytes([B)Landroid/bluetooth/le/ScanRecord;
+Landroid/content/AsyncTaskLoader$LoadTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Object;
 Landroid/content/AsyncTaskLoader;->mExecutor:Ljava/util/concurrent/Executor;
 Landroid/content/BroadcastReceiver$PendingResult;-><init>(ILjava/lang/String;Landroid/os/Bundle;IZZLandroid/os/IBinder;II)V
 Landroid/content/BroadcastReceiver;->setPendingResult(Landroid/content/BroadcastReceiver$PendingResult;)V
@@ -450,6 +491,7 @@
 Landroid/content/ContentResolver;->mPackageName:Ljava/lang/String;
 Landroid/content/ContentResolver;->releaseProvider(Landroid/content/IContentProvider;)Z
 Landroid/content/ContentResolver;->releaseUnstableProvider(Landroid/content/IContentProvider;)Z
+Landroid/content/ContentResolver;->takePersistableUriPermission(Ljava/lang/String;Landroid/net/Uri;I)V
 Landroid/content/ContentResolver;->unstableProviderDied(Landroid/content/IContentProvider;)V
 Landroid/content/ContentValues;-><init>(Ljava/util/HashMap;)V
 Landroid/content/ContentValues;->mValues:Ljava/util/HashMap;
@@ -475,6 +517,7 @@
 Landroid/content/IntentFilter;->mActions:Ljava/util/ArrayList;
 Landroid/content/Intent;->mExtras:Landroid/os/Bundle;
 Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent;
+Landroid/content/Intent;->toInsecureString()Ljava/lang/String;
 Landroid/content/pm/ActivityInfo;->resizeMode:I
 Landroid/content/pm/ActivityInfo;->supportsPictureInPicture()Z
 Landroid/content/pm/ApplicationInfo;->enabledSetting:I
@@ -487,6 +530,7 @@
 Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String;
 Landroid/content/pm/ApplicationInfo;->secondaryCpuAbi:Ljava/lang/String;
 Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String;
+Landroid/content/pm/ApplicationInfo;->versionCode:I
 Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName;
 Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
 Landroid/content/pm/IPackageManager;->addPermissionAsync(Landroid/content/pm/PermissionInfo;)Z
@@ -515,6 +559,7 @@
 Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
 Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager;
 Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V
+Landroid/content/pm/PackageInfo;->INSTALL_LOCATION_UNSPECIFIED:I
 Landroid/content/pm/PackageItemInfo;->setForceSafeLabels(Z)V
 Landroid/content/pm/PackageManager;->buildRequestPermissionsIntent([Ljava/lang/String;)Landroid/content/Intent;
 Landroid/content/pm/PackageManager;->freeStorageAndNotify(JLandroid/content/pm/IPackageDataObserver;)V
@@ -584,6 +629,7 @@
 Landroid/content/res/AssetFileDescriptor;->mStartOffset:J
 Landroid/content/res/AssetManager;->addAssetPathAsSharedLibrary(Ljava/lang/String;)I
 Landroid/content/res/AssetManager;->addAssetPath(Ljava/lang/String;)I
+Landroid/content/res/AssetManager;->applyStyle(JIILandroid/content/res/XmlBlock$Parser;[IJJ)V
 Landroid/content/res/AssetManager;->getAssignedPackageIdentifiers()Landroid/util/SparseArray;
 Landroid/content/res/AssetManager;->getResourceBagText(II)Ljava/lang/CharSequence;
 Landroid/content/res/AssetManager;->getResourceEntryName(I)Ljava/lang/String;
@@ -599,12 +645,15 @@
 Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;I)Ljava/io/InputStream;
 Landroid/content/res/AssetManager;->openNonAsset(Ljava/lang/String;)Ljava/io/InputStream;
 Landroid/content/res/AssetManager;->resolveAttrs(JII[I[I[I[I)Z
+Landroid/content/res/AssetManager;->retrieveAttributes(Landroid/content/res/XmlBlock$Parser;[I[I[I)Z
 Landroid/content/res/AssetManager;->setConfiguration(IILjava/lang/String;IIIIIIIIIIIIIII)V
 Landroid/content/res/ColorStateList$ColorStateListFactory;-><init>(Landroid/content/res/ColorStateList;)V
 Landroid/content/res/ColorStateList;->getColors()[I
 Landroid/content/res/ColorStateList;->mColors:[I
 Landroid/content/res/ColorStateList;->mDefaultColor:I
 Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateList$ColorStateListFactory;
+Landroid/content/res/ColorStateList;->mStateSpecs:[[I
+Landroid/content/res/ColorStateList;->onColorsChanged()V
 Landroid/content/res/CompatibilityInfo;->applicationScale:F
 Landroid/content/res/CompatibilityInfo;->DEFAULT_COMPATIBILITY_INFO:Landroid/content/res/CompatibilityInfo;
 Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable;
@@ -662,6 +711,7 @@
 Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
 Landroid/database/AbstractCursor;->mRowIdColumnIndex:I
 Landroid/database/AbstractWindowedCursor;->clearOrCreateWindow(Ljava/lang/String;)V
+Landroid/database/CursorJoiner;->compareStrings([Ljava/lang/String;)I
 Landroid/database/CursorWindow;->mWindowPtr:J
 Landroid/database/CursorWindow;->sCursorWindowSize:I
 Landroid/database/CursorWindow;->sWindowToPidMap:Landroid/util/LongSparseArray;
@@ -673,12 +723,21 @@
 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/SQLiteDatabase;->reopenReadWrite()V
 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/filterfw/core/AsyncRunner$AsyncRunnerTask;->doInBackground([Landroid/filterfw/core/SyncRunner;)Landroid/filterfw/core/AsyncRunner$RunnerResult;
+Landroid/filterfw/core/FilterFunction;->executeWithArgList([Ljava/lang/Object;)Landroid/filterfw/core/Frame;
+Landroid/filterfw/core/Filter;->initWithAssignmentList([Ljava/lang/Object;)V
+Landroid/filterfw/core/KeyValueMap;->fromKeyValues([Ljava/lang/Object;)Landroid/filterfw/core/KeyValueMap;
+Landroid/filterfw/core/KeyValueMap;->setKeyValues([Ljava/lang/Object;)V
+Landroid/filterfw/FilterFunctionEnvironment;->createFunction(Ljava/lang/Class;[Ljava/lang/Object;)Landroid/filterfw/core/FilterFunction;
+Landroid/filterfw/GraphEnvironment;->addReferences([Ljava/lang/Object;)V
+Landroid/filterfw/io/GraphReader;->addReferencesByKeysAndValues([Ljava/lang/Object;)V
 Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
@@ -720,6 +779,7 @@
 Landroid/graphics/drawable/Drawable;->getOpticalInsets()Landroid/graphics/Insets;
 Landroid/graphics/drawable/Drawable;->inflateWithAttributes(Landroid/content/res/Resources;Lorg/xmlpull/v1/XmlPullParser;Landroid/content/res/TypedArray;I)V
 Landroid/graphics/drawable/Drawable;->mCallback:Ljava/lang/ref/WeakReference;
+Landroid/graphics/drawable/Drawable;->parseTintMode(ILandroid/graphics/PorterDuff$Mode;)Landroid/graphics/PorterDuff$Mode;
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mAngle:I
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradientColors:[I
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mGradient:I
@@ -732,12 +792,14 @@
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadiusArray:[F
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mRadius:F
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mShape:I
+Landroid/graphics/drawable/GradientDrawable$GradientState;->mSolidColors:Landroid/content/res/ColorStateList;
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashGap:F
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeDashWidth:F
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mStrokeWidth:I
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mThickness:I
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mThicknessRatio:F
 Landroid/graphics/drawable/GradientDrawable$GradientState;->mWidth:I
+Landroid/graphics/drawable/GradientDrawable;->mGradientState:Landroid/graphics/drawable/GradientDrawable$GradientState;
 Landroid/graphics/drawable/GradientDrawable;->mPadding:Landroid/graphics/Rect;
 Landroid/graphics/drawable/Icon;->getBitmap()Landroid/graphics/Bitmap;
 Landroid/graphics/drawable/Icon;->getDataBytes()[B
@@ -747,8 +809,13 @@
 Landroid/graphics/drawable/Icon;->hasTint()Z
 Landroid/graphics/drawable/Icon;->mType:I
 Landroid/graphics/drawable/InsetDrawable;->mState:Landroid/graphics/drawable/InsetDrawable$InsetState;
+Landroid/graphics/drawable/LayerDrawable$ChildDrawable;->mDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/graphics/drawable/LayerDrawable$LayerState;->mChildren:[Landroid/graphics/drawable/LayerDrawable$ChildDrawable;
+Landroid/graphics/drawable/LayerDrawable;->mLayerState:Landroid/graphics/drawable/LayerDrawable$LayerState;
 Landroid/graphics/drawable/NinePatchDrawable;->mNinePatchState:Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;
 Landroid/graphics/drawable/NinePatchDrawable$NinePatchState;->mNinePatch:Landroid/graphics/NinePatch;
+Landroid/graphics/drawable/RippleDrawable;->mState:Landroid/graphics/drawable/RippleDrawable$RippleState;
+Landroid/graphics/drawable/RippleDrawable$RippleState;->mColor:Landroid/content/res/ColorStateList;
 Landroid/graphics/drawable/StateListDrawable;->extractStateSet(Landroid/util/AttributeSet;)[I
 Landroid/graphics/drawable/StateListDrawable;->getStateCount()I
 Landroid/graphics/drawable/StateListDrawable;->getStateDrawable(I)Landroid/graphics/drawable/Drawable;
@@ -768,6 +835,8 @@
 Landroid/graphics/GraphicBuffer;-><init>(IIIIJ)V
 Landroid/graphics/GraphicBuffer;->mNativeObject:J
 Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
+Landroid/graphics/Insets;->left:I
+Landroid/graphics/Insets;->right:I
 Landroid/graphics/LinearGradient;->mColors:[I
 Landroid/graphics/Matrix;->native_instance:J
 Landroid/graphics/Movie;-><init>(J)V
@@ -776,8 +845,7 @@
 Landroid/graphics/NinePatch;->mBitmap:Landroid/graphics/Bitmap;
 Landroid/graphics/Picture;->mNativePicture:J
 Landroid/graphics/PixelXorXfermode;-><init>(I)V
-Landroid/graphics/PorterDuffColorFilter;->setColor(I)V
-Landroid/graphics/PorterDuffColorFilter;->setMode(Landroid/graphics/PorterDuff$Mode;)V
+Landroid/graphics/Rect;->scale(F)V
 Landroid/graphics/Region;-><init>(JI)V
 Landroid/graphics/Region;->mNativeRegion:J
 Landroid/graphics/SurfaceTexture;->mFrameAvailableListener:J
@@ -849,7 +917,11 @@
 Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_BLUE:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_GREEN:Landroid/hardware/camera2/CaptureResult$Key;
 Landroid/hardware/camera2/CaptureResult;->TONEMAP_CURVE_RED:Landroid/hardware/camera2/CaptureResult$Key;
+Landroid/hardware/camera2/impl/CameraMetadataNative;->areValuesAllNull([Ljava/lang/Object;)Z
 Landroid/hardware/camera2/impl/CameraMetadataNative;->mMetadataPtr:J
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([F)I
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCodeGeneric([Ljava/lang/Object;)I
+Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([I)I
 Landroid/hardware/Camera;->addCallbackBuffer([BI)V
 Landroid/hardware/Camera;->mNativeContext:J
 Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera;
@@ -916,15 +988,25 @@
 Landroid/hardware/usb/UsbRequest;->mLength:I
 Landroid/hardware/usb/UsbRequest;->mNativeContext:J
 Landroid/icu/impl/CurrencyData;-><init>()V
+Landroid/icu/impl/locale/XLocaleDistance$RegionMapper$Builder;->addParadigms([Ljava/lang/String;)Landroid/icu/impl/locale/XLocaleDistance$RegionMapper$Builder;
+Landroid/icu/impl/TimeZoneGenericNames;->formatPattern(Landroid/icu/impl/TimeZoneGenericNames$Pattern;[Ljava/lang/String;)Ljava/lang/String;
+Landroid/icu/impl/TimeZoneGenericNames$GenericNameType;-><init>([Ljava/lang/String;)V
 Landroid/icu/text/ArabicShaping;->isAlefMaksouraChar(C)Z
 Landroid/icu/text/ArabicShaping;->isSeenTailFamilyChar(C)I
 Landroid/icu/text/ArabicShaping;->isTailChar(C)Z
 Landroid/icu/text/ArabicShaping;->isYehHamzaChar(C)Z
+Landroid/icu/text/Collator;->getIntValue(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)I
 Landroid/icu/text/DateFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
 Landroid/icu/text/DateIntervalFormat;-><init>()V
 Landroid/icu/text/DateTimePatternGenerator$DistanceInfo;-><init>()V
 Landroid/icu/text/DecimalFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/text/DictionaryBreakEngine;-><init>([Ljava/lang/Integer;)V
+Landroid/icu/text/LocaleDisplayNames$LastResortLocaleDisplayNames;-><init>(Landroid/icu/util/ULocale;[Landroid/icu/text/DisplayContext;)V
+Landroid/icu/text/MeasureFormat;->formatMeasuresSlowTrack(Landroid/icu/text/ListFormatter;Ljava/lang/StringBuilder;Ljava/text/FieldPosition;[Landroid/icu/util/Measure;)Ljava/lang/StringBuilder;
 Landroid/icu/text/RuleBasedCollator;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/text/SimpleFormatter;->formatAndAppend(Ljava/lang/StringBuilder;[I[Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
+Landroid/icu/text/SimpleFormatter;->formatAndReplace(Ljava/lang/StringBuilder;[I[Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;
+Landroid/icu/text/SimpleFormatter;->format([Ljava/lang/CharSequence;)Ljava/lang/String;
 Landroid/icu/text/SpoofChecker$ScriptSet;->and(I)V
 Landroid/icu/text/SpoofChecker$ScriptSet;-><init>()V
 Landroid/icu/text/SpoofChecker$ScriptSet;->isFull()Z
@@ -934,6 +1016,11 @@
 Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
 Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
 Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
+Landroid/icu/util/Currency$EquivalenceRelation;->add([Ljava/lang/Object;)Landroid/icu/util/Currency$EquivalenceRelation;
+Landroid/icu/util/GenderInfo;->getListGender([Landroid/icu/util/GenderInfo$Gender;)Landroid/icu/util/GenderInfo$Gender;
+Landroid/icu/util/LocaleMatcher;->getBestMatch([Landroid/icu/util/ULocale;)Landroid/icu/util/ULocale;
+Landroid/icu/util/LocalePriorityList;->add([Landroid/icu/util/ULocale;)Landroid/icu/util/LocalePriorityList$Builder;
+Landroid/icu/util/LocalePriorityList$Builder;->add([Landroid/icu/util/ULocale;)Landroid/icu/util/LocalePriorityList$Builder;
 Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
 Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
 Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
@@ -960,6 +1047,7 @@
 Landroid/media/AudioFormat;->mEncoding:I
 Landroid/media/AudioFormat;->mSampleRate:I
 Landroid/media/audiofx/AudioEffect;->command(I[B[B)I
+Landroid/media/audiofx/AudioEffect;->concatArrays([[B)[B
 Landroid/media/audiofx/AudioEffect;->getParameter([I[B)I
 Landroid/media/audiofx/AudioEffect;->getParameter([I[I)I
 Landroid/media/audiofx/AudioEffect;-><init>(Ljava/util/UUID;Ljava/util/UUID;II)V
@@ -1035,6 +1123,8 @@
 Landroid/media/AudioTrack;->mStreamType:I
 Landroid/media/AudioTrack;->native_release()V
 Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
+Landroid/media/effect/SingleFilterEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/media/effect/SizeChangeEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/media/ExifInterface;->getDateTime()J
 Landroid/media/IAudioService;->getStreamMaxVolume(I)I
 Landroid/media/IAudioService;->getStreamVolume(I)I
@@ -1051,11 +1141,13 @@
 Landroid/media/MediaCodec;->getBuffers(Z)[Ljava/nio/ByteBuffer;
 Landroid/media/MediaCodec;->releaseOutputBuffer(IZZJ)V
 Landroid/media/MediaFile;->FIRST_AUDIO_FILE_TYPE:I
+Landroid/media/MediaFile;->getFileTypeForMimeType(Ljava/lang/String;)I
 Landroid/media/MediaFile;->getFileType(Ljava/lang/String;)Landroid/media/MediaFile$MediaFileType;
 Landroid/media/MediaFile;->getMimeTypeForFile(Ljava/lang/String;)Ljava/lang/String;
 Landroid/media/MediaFile;-><init>()V
 Landroid/media/MediaFile;->isAudioFileType(I)Z
 Landroid/media/MediaFile;->isImageFileType(I)Z
+Landroid/media/MediaFile;->isPlayListFileType(I)Z
 Landroid/media/MediaFile;->isVideoFileType(I)Z
 Landroid/media/MediaFile;->LAST_AUDIO_FILE_TYPE:I
 Landroid/media/MediaFile$MediaFileType;->fileType:I
@@ -1072,6 +1164,7 @@
 Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)V
 Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V
 Landroid/media/MediaPlayer;->setRetransmitEndpoint(Ljava/net/InetSocketAddress;)V
+Landroid/media/MediaRecorder;->setParameter(Ljava/lang/String;)V
 Landroid/media/MediaRouter$RouteInfo;->getStatusCode()I
 Landroid/media/MediaRouter$RouteInfo;->STATUS_CONNECTING:I
 Landroid/media/MediaRouter;->selectRouteInt(ILandroid/media/MediaRouter$RouteInfo;Z)V
@@ -1116,6 +1209,7 @@
 Landroid/media/SubtitleTrack$RenderingWidget;->setSize(II)V
 Landroid/media/ThumbnailUtils;->createImageThumbnail(Ljava/lang/String;I)Landroid/graphics/Bitmap;
 Landroid/media/ToneGenerator;->mNativeContext:J
+Landroid/media/tv/TvInputService$OverlayViewCleanUpTask;->doInBackground([Landroid/view/View;)Ljava/lang/Void;
 Landroid/media/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
 Landroid/media/VolumeShaper$Configuration;->mDurationMs:D
 Landroid/media/VolumeShaper$Configuration;->mId:I
@@ -1142,8 +1236,10 @@
 Landroid/net/ConnectivityManager;->isNetworkSupported(I)Z
 Landroid/net/ConnectivityManager;->isNetworkTypeMobile(I)Z
 Landroid/net/ConnectivityManager;->mService:Landroid/net/IConnectivityManager;
+Landroid/net/ConnectivityManager;->registerNetworkFactory(Landroid/os/Messenger;Ljava/lang/String;)V
 Landroid/net/ConnectivityManager;->requestRouteToHostAddress(ILjava/net/InetAddress;)Z
 Landroid/net/ConnectivityManager;->requestRouteToHost(II)Z
+Landroid/net/ConnectivityManager;->setBackgroundDataSetting(Z)V
 Landroid/net/ConnectivityManager;->TYPE_MOBILE_CBS:I
 Landroid/net/ConnectivityManager;->TYPE_MOBILE_EMERGENCY:I
 Landroid/net/ConnectivityManager;->TYPE_MOBILE_FOTA:I
@@ -1167,6 +1263,7 @@
 Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
 Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor;
 Landroid/net/LocalSocketImpl;->outboundFileDescriptors:[Ljava/io/FileDescriptor;
+Landroid/net/MacAddress;->addr([I)[B
 Landroid/net/NetworkCapabilities;->getCapabilities()[I
 Landroid/net/NetworkCapabilities;->getTransportTypes()[I
 Landroid/net/NetworkPolicyManager;->mService:Landroid/net/INetworkPolicyManager;
@@ -1211,6 +1308,7 @@
 Landroid/net/SSLCertificateSocketFactory;->setChannelIdPrivateKey(Ljava/security/PrivateKey;)V
 Landroid/net/SSLCertificateSocketFactory;->setSoWriteTimeout(Ljava/net/Socket;I)V
 Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String;
+Landroid/net/SSLCertificateSocketFactory;->toLengthPrefixedList([[B)[B
 Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V
 Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
 Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
@@ -1282,9 +1380,12 @@
 Landroid/net/wifi/WifiSsid;->NONE:Ljava/lang/String;
 Landroid/nfc/NfcAdapter;->getDefaultAdapter()Landroid/nfc/NfcAdapter;
 Landroid/nfc/NfcAdapter;->setNdefPushMessageCallback(Landroid/nfc/NfcAdapter$CreateNdefMessageCallback;Landroid/app/Activity;I)V
+Landroid/nfc/TechListParcel;-><init>([[Ljava/lang/String;)V
 Landroid/opengl/GLSurfaceView$EglHelper;->mEglContext:Ljavax/microedition/khronos/egl/EGLContext;
 Landroid/opengl/GLSurfaceView$GLThread;->mEglHelper:Landroid/opengl/GLSurfaceView$EglHelper;
 Landroid/opengl/GLSurfaceView;->mGLThread:Landroid/opengl/GLSurfaceView$GLThread;
+Landroid/opengl/GLSurfaceView;->mRenderer:Landroid/opengl/GLSurfaceView$Renderer;
+Landroid/os/AsyncTask$AsyncTaskResult;-><init>(Landroid/os/AsyncTask;[Ljava/lang/Object;)V
 Landroid/os/AsyncTask;->mFuture:Ljava/util/concurrent/FutureTask;
 Landroid/os/AsyncTask;->mStatus:Landroid/os/AsyncTask$Status;
 Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean;
@@ -1292,7 +1393,9 @@
 Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor;
 Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V
 Landroid/os/BatteryStats$Counter;->getCountLocked(I)I
+Landroid/os/BatteryStats;->dumpLine(Ljava/io/PrintWriter;ILjava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
 Landroid/os/BatteryStats;->getUidStats()Landroid/util/SparseArray;
+Landroid/os/BatteryStats$HistoryItem;->CMD_UPDATE:B
 Landroid/os/BatteryStats$HistoryItem;->states2:I
 Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
 Landroid/os/BatteryStats;->startIteratingHistoryLocked()Z
@@ -1320,6 +1423,7 @@
 Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
 Landroid/os/BatteryStats$Uid$Sensor;->getHandle()I
 Landroid/os/BatteryStats$Uid$Sensor;->getSensorTime()Landroid/os/BatteryStats$Timer;
+Landroid/os/BestClock;-><init>(Ljava/time/ZoneId;[Ljava/time/Clock;)V
 Landroid/os/Binder;->execTransact(IJJI)Z
 Landroid/os/Binder;->mObject:J
 Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String;
@@ -1360,6 +1464,7 @@
 Landroid/os/Debug$MemoryInfo;->otherSwappedOut:I
 Landroid/os/Debug$MemoryInfo;->otherSwappedOutPss:I
 Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File;
+Landroid/os/Environment;->buildPaths([Ljava/io/File;[Ljava/lang/String;)[Ljava/io/File;
 Landroid/os/Environment;->getVendorDirectory()Ljava/io/File;
 Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File;
 Landroid/os/FileObserver$ObserverThread;->onEvent(IILjava/lang/String;)V
@@ -1433,6 +1538,7 @@
 Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z
 Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V
 Landroid/os/Process;->setArgV0(Ljava/lang/String;)V
+Landroid/os/RecoverySystem;->bootCommand(Landroid/content/Context;[Ljava/lang/String;)V
 Landroid/os/SELinux;->isSELinuxEnabled()Z
 Landroid/os/SELinux;->isSELinuxEnforced()Z
 Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1452,6 +1558,7 @@
 Landroid/os/storage/StorageManager;->getBestVolumeDescription(Landroid/os/storage/VolumeInfo;)Ljava/lang/String;
 Landroid/os/storage/StorageManager;->getDisks()Ljava/util/List;
 Landroid/os/storage/StorageManager;->getStorageBytesUntilLow(Ljava/io/File;)J
+Landroid/os/storage/StorageManager;->getStorageFullBytes(Ljava/io/File;)J
 Landroid/os/storage/StorageManager;->getStorageLowBytes(Ljava/io/File;)J
 Landroid/os/storage/StorageManager;->getVolumeList(II)[Landroid/os/storage/StorageVolume;
 Landroid/os/storage/StorageManager;->getVolumeList()[Landroid/os/storage/StorageVolume;
@@ -1471,8 +1578,10 @@
 Landroid/os/storage/VolumeInfo;->isPrimary()Z
 Landroid/os/storage/VolumeInfo;->isVisible()Z
 Landroid/os/StrictMode;->disableDeathOnFileUriExposure()V
+Landroid/os/StrictMode;->enterCriticalSpan(Ljava/lang/String;)Landroid/os/StrictMode$Span;
 Landroid/os/StrictMode;->getThreadPolicyMask()I
 Landroid/os/StrictMode;->onBinderStrictModePolicyChange(I)V
+Landroid/os/StrictMode$Span;->finish()V
 Landroid/os/StrictMode$ThreadPolicy$Builder;->penaltyListener(Landroid/os/StrictMode$OnThreadViolationListener;Ljava/util/concurrent/Executor;)Landroid/os/StrictMode$ThreadPolicy$Builder;
 Landroid/os/StrictMode;->violationsBeingTimed:Ljava/lang/ThreadLocal;
 Landroid/os/SystemProperties;->addChangeCallback(Ljava/lang/Runnable;)V
@@ -1480,6 +1589,7 @@
 Landroid/os/SystemProperties;->native_get(Ljava/lang/String;)Ljava/lang/String;
 Landroid/os/SystemProperties;->PROP_NAME_MAX:I
 Landroid/os/SystemProperties;->set(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/SystemService;->waitForAnyStopped([Ljava/lang/String;)V
 Landroid/os/Trace;->asyncTraceBegin(JLjava/lang/String;I)V
 Landroid/os/Trace;->asyncTraceEnd(JLjava/lang/String;I)V
 Landroid/os/Trace;->isTagEnabled(J)Z
@@ -1591,6 +1701,7 @@
 Landroid/preference/Preference;->performClick(Landroid/preference/PreferenceScreen;)V
 Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter;
 Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
+Landroid/print/PrintFileDocumentAdapter$WriteFileAsyncTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Void;
 Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
 Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
 Landroid/provider/Browser$BookmarkColumns;->DATE:Ljava/lang/String;
@@ -1606,6 +1717,7 @@
 Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V
 Landroid/provider/CalendarContract$CalendarAlerts;->findNextAlarmTime(Landroid/content/ContentResolver;J)J
 Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V
+Landroid/provider/Downloads$Impl;->CONTENT_URI:Landroid/net/Uri;
 Landroid/provider/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider;
 Landroid/provider/Settings$Global;->ENABLE_ACCESSIBILITY_GLOBAL_GESTURE_ENABLED:Ljava/lang/String;
 Landroid/provider/Settings$Global;->PACKAGE_VERIFIER_ENABLE:Ljava/lang/String;
@@ -1831,6 +1943,8 @@
 Landroid/R$styleable;->Window:[I
 Landroid/R$styleable;->Window_windowBackground:I
 Landroid/R$styleable;->Window_windowFrame:I
+Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B
+Landroid/security/keymaster/KeymasterArguments;->addEnums(I[I)V
 Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
 Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
 Landroid/security/net/config/RootTrustManager;->checkServerTrusted([Ljava/security/cert/X509Certificate;Ljava/lang/String;Ljava/lang/String;)Ljava/util/List;
@@ -1843,10 +1957,13 @@
 Landroid/service/media/MediaBrowserService$Result;->mFlags:I
 Landroid/service/notification/NotificationListenerService;->registerAsSystemService(Landroid/content/Context;Landroid/content/ComponentName;I)V
 Landroid/service/notification/NotificationListenerService;->unregisterAsSystemService()V
+Landroid/service/notification/StatusBarNotification;->getUid()I
 Landroid/service/voice/AlwaysOnHotwordDetector$EventPayload;->getCaptureSession()Ljava/lang/Integer;
+Landroid/service/voice/AlwaysOnHotwordDetector$RefreshAvailabiltyTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Void;
 Landroid/service/voice/VoiceInteractionService;->isKeyphraseAndLocaleSupportedForHotword(Ljava/lang/String;Ljava/util/Locale;)Z
 Landroid/service/vr/IVrManager;->getVr2dDisplayId()I
 Landroid/service/wallpaper/WallpaperService$Engine;->setFixedSizeAllowed(Z)V
+Landroid/speech/tts/TextToSpeech$Connection$SetupConnectionAsyncTask;->doInBackground([Ljava/lang/Void;)Ljava/lang/Integer;
 Landroid/speech/tts/TextToSpeech;->getCurrentEngine()Ljava/lang/String;
 Landroid/system/Int32Ref;->value:I
 Landroid/system/OsConstants;->AF_NETLINK:I
@@ -1904,6 +2021,25 @@
 Landroid/system/OsConstants;->XATTR_CREATE:I
 Landroid/system/OsConstants;->XATTR_REPLACE:I
 Landroid/system/StructTimeval;->fromMillis(J)Landroid/system/StructTimeval;
+Landroid/telecom/AudioState;->isMuted:Z
+Landroid/telecom/Log;->addEvent(Landroid/telecom/Logging/EventManager$Loggable;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->buildMessage(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Landroid/telecom/Log;->d(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->d(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->e(Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->e(Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Logging/EventManager;->event(Landroid/telecom/Logging/EventManager$Loggable;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->i(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->v(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->v(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->w(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/Object;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Log;->wtf(Ljava/lang/String;Ljava/lang/Throwable;Ljava/lang/String;[Ljava/lang/Object;)V
+Landroid/telecom/Response;->onResult(Ljava/lang/Object;[Ljava/lang/Object;)V
 Landroid/telecom/TelecomManager;->EXTRA_IS_HANDOVER:Ljava/lang/String;
 Landroid/telecom/TelecomManager;->getUserSelectedOutgoingPhoneAccount()Landroid/telecom/PhoneAccountHandle;
 Landroid/telecom/TelecomManager;->setUserSelectedOutgoingPhoneAccount(Landroid/telecom/PhoneAccountHandle;)V
@@ -1938,6 +2074,8 @@
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_MODERATE:I
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_NONE_OR_UNKNOWN:I
 Landroid/telephony/SignalStrength;->SIGNAL_STRENGTH_POOR:I
+Landroid/telephony/SmsManager;->sendMultipartTextMessage(Ljava/lang/String;Ljava/lang/String;Ljava/util/ArrayList;Ljava/util/ArrayList;Ljava/util/ArrayList;IZI)V
+Landroid/telephony/SmsManager;->sendTextMessage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;IZI)V
 Landroid/telephony/SmsMessage;->getSubId()I
 Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telephony/SmsMessageBase;
 Landroid/telephony/SubscriptionManager;->getActiveSubscriptionIdList()[I
@@ -1950,6 +2088,7 @@
 Landroid/telephony/SubscriptionManager;->getPhoneId(I)I
 Landroid/telephony/SubscriptionManager;->getSlotIndex(I)I
 Landroid/telephony/SubscriptionManager;->getSubId(I)[I
+Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
 Landroid/telephony/SubscriptionManager;->setDefaultSmsSubId(I)V
 Landroid/telephony/TelephonyManager;->from(Landroid/content/Context;)Landroid/telephony/TelephonyManager;
 Landroid/telephony/TelephonyManager;->getCallState(I)I
@@ -1986,6 +2125,7 @@
 Landroid/telephony/TelephonyManager;->isWifiCallingAvailable()Z
 Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
 Landroid/text/AndroidBidi;->bidi(I[C[B)I
+Landroid/text/BoringLayout;->isBoring(Ljava/lang/CharSequence;Landroid/text/TextPaint;Landroid/text/TextDirectionHeuristic;Landroid/text/BoringLayout$Metrics;)Landroid/text/BoringLayout$Metrics;
 Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V
 Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout;
 Landroid/text/Html;->withinStyle(Ljava/lang/StringBuilder;Ljava/lang/CharSequence;II)V
@@ -2032,6 +2172,7 @@
 Landroid/text/SpannableStringInternal;->setSpan(Ljava/lang/Object;IIIZ)V
 Landroid/text/SpannableStringInternal;->START:I
 Landroid/text/SpanSet;->spans:[Ljava/lang/Object;
+Landroid/text/StaticLayout;->getHeight(Z)I
 Landroid/text/StaticLayout;-><init>(Ljava/lang/CharSequence;IILandroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZLandroid/text/TextUtils$TruncateAt;II)V
 Landroid/text/StaticLayout$LineBreaks;->ascents:[F
 Landroid/text/StaticLayout$LineBreaks;->breaks:[I
@@ -2050,9 +2191,11 @@
 Landroid/text/TextLine;->obtain()Landroid/text/TextLine;
 Landroid/text/TextLine;->sCached:[Landroid/text/TextLine;
 Landroid/text/TextPaint;->setUnderlineText(IF)V
+Landroid/text/TextUtils$TruncateAt;->END_SMALL:Landroid/text/TextUtils$TruncateAt;
 Landroid/transition/ChangeBounds;->BOTTOM_RIGHT_ONLY_PROPERTY:Landroid/util/Property;
 Landroid/transition/ChangeBounds;->POSITION_PROPERTY:Landroid/util/Property;
 Landroid/transition/TransitionManager;->sRunningTransitions:Ljava/lang/ThreadLocal;
+Landroid/transition/TransitionUtils;->mergeTransitions([Landroid/transition/Transition;)Landroid/transition/Transition;
 Landroid/util/ArrayMap;->append(Ljava/lang/Object;Ljava/lang/Object;)V
 Landroid/util/ArrayMap;->mBaseCacheSize:I
 Landroid/util/ArrayMap;->mTwiceBaseCacheSize:I
@@ -2063,6 +2206,7 @@
 Landroid/util/LongSparseLongArray;->mKeys:[J
 Landroid/util/LongSparseLongArray;->mSize:I
 Landroid/util/LongSparseLongArray;->mValues:[J
+Landroid/util/MathUtils;->constrain(III)I
 Landroid/util/NtpTrustedTime;->forceRefresh()Z
 Landroid/util/NtpTrustedTime;->getCachedNtpTime()J
 Landroid/util/NtpTrustedTime;->getCachedNtpTimeReference()J
@@ -2071,6 +2215,7 @@
 Landroid/util/Pools$SimplePool;->mPool:[Ljava/lang/Object;
 Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object;
 Landroid/util/Pools$SynchronizedPool;-><init>(I)V
+Landroid/util/Pools$SynchronizedPool;->release(Ljava/lang/Object;)Z
 Landroid/util/Rational;->mDenominator:I
 Landroid/util/Rational;->mNumerator:I
 Landroid/util/Singleton;->get()Ljava/lang/Object;
@@ -2091,18 +2236,22 @@
 Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager;
 Landroid/view/accessibility/AccessibilityManager;->sInstanceSync:Ljava/lang/Object;
 Landroid/view/accessibility/AccessibilityNodeInfo;->isSealed()Z
+Landroid/view/accessibility/AccessibilityNodeInfo;->mChildNodeIds:Landroid/util/LongArray;
 Landroid/view/accessibility/AccessibilityNodeInfo;->refresh(Landroid/os/Bundle;Z)Z
 Landroid/view/accessibility/AccessibilityNodeInfo;->setSealed(Z)V
 Landroid/view/accessibility/AccessibilityRecord;->getSourceNodeId()J
 Landroid/view/accessibility/IAccessibilityManager;->getEnabledAccessibilityServiceList(II)Ljava/util/List;
 Landroid/view/accessibility/IAccessibilityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/accessibility/IAccessibilityManager;
 Landroid/view/accessibility/IAccessibilityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/view/animation/Animation;->detach()V
+Landroid/view/animation/Animation;->initializeInvalidateRegion(IIII)V
 Landroid/view/animation/Animation;->mListener:Landroid/view/animation/Animation$AnimationListener;
 Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/view/Choreographer$CallbackQueue;->addCallbackLocked(JLjava/lang/Object;Ljava/lang/Object;)V
 Landroid/view/Choreographer;->doFrame(JI)V
 Landroid/view/Choreographer;->getFrameTime()J
 Landroid/view/Choreographer;->mCallbackQueues:[Landroid/view/Choreographer$CallbackQueue;
+Landroid/view/Choreographer;->mFrameIntervalNanos:J
 Landroid/view/Choreographer;->mLastFrameTimeNanos:J
 Landroid/view/Choreographer;->mLock:Ljava/lang/Object;
 Landroid/view/Choreographer;->scheduleVsyncLocked()V
@@ -2139,10 +2288,12 @@
 Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
 Landroid/view/inputmethod/InputMethodManager;->focusOut(Landroid/view/View;)V
 Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
+Landroid/view/inputmethod/InputMethodManager;->getInstance()Landroid/view/inputmethod/InputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
 Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
 Landroid/view/inputmethod/InputMethodManager;->mH:Landroid/view/inputmethod/InputMethodManager$H;
 Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/View;
+Landroid/view/inputmethod/InputMethodManager;->mServedInputConnectionWrapper:Landroid/view/inputmethod/InputMethodManager$ControlledInputConnectionWrapper;
 Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
 Landroid/view/inputmethod/InputMethodManager;->mService:Lcom/android/internal/view/IInputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
@@ -2164,10 +2315,11 @@
 Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I
 Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z
 Landroid/view/IWindowManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIILandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
+Landroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I
 Landroid/view/KeyCharacterMap$FallbackAction;->keyCode:I
 Landroid/view/KeyCharacterMap$FallbackAction;->metaState:I
 Landroid/view/KeyCharacterMap;-><init>(J)V
+Landroid/view/KeyEvent;->isConfirmKey(I)Z
 Landroid/view/KeyEvent;->mAction:I
 Landroid/view/KeyEvent;->mCharacters:Ljava/lang/String;
 Landroid/view/KeyEvent;->mDeviceId:I
@@ -2220,6 +2372,9 @@
 Landroid/view/RemoteAnimationTarget;->taskId:I
 Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
 Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
+Landroid/view/RenderNodeAnimator;-><init>(IF)V
+Landroid/view/RenderNodeAnimator;->mapViewPropertyToRenderProperty(I)I
+Landroid/view/RenderNodeAnimator;->setTarget(Landroid/view/View;)V
 Landroid/view/RenderNode;->discardDisplayList()V
 Landroid/view/RenderNode;->output()V
 Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
@@ -2238,10 +2393,13 @@
 Landroid/view/Surface;->mLock:Ljava/lang/Object;
 Landroid/view/Surface;->mNativeObject:J
 Landroid/view/SurfaceSession;->mNativeClient:J
+Landroid/view/Surface;->transferFrom(Landroid/view/Surface;)V
+Landroid/view/SurfaceView;->isFixedSize()Z
 Landroid/view/SurfaceView;->mCallbacks:Ljava/util/ArrayList;
 Landroid/view/SurfaceView;->mFormat:I
 Landroid/view/SurfaceView;->mRequestedFormat:I
 Landroid/view/SurfaceView;->mSurfaceHolder:Landroid/view/SurfaceHolder;
+Landroid/view/SurfaceView;->setFrame(IIII)Z
 Landroid/view/SurfaceView;->surfacePositionLost_uiRtSync(J)V
 Landroid/view/SurfaceView;->updateSurfacePosition_renderWorker(JIIII)V
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker;-><init>(Landroid/content/Context;I)V
@@ -2253,6 +2411,9 @@
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionModified(IILandroid/view/textclassifier/TextSelection;)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;->selectionStarted(I)Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;
 Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier;
+Landroid/view/textclassifier/TextClassifier;->classifyText(Ljava/lang/CharSequence;IILandroid/view/textclassifier/TextClassification$Options;)Landroid/view/textclassifier/TextClassification;
+Landroid/view/textclassifier/TextClassifier;->generateLinks(Ljava/lang/CharSequence;Landroid/view/textclassifier/TextLinks$Options;)Landroid/view/textclassifier/TextLinks;
+Landroid/view/textclassifier/TextClassifier;->suggestSelection(Ljava/lang/CharSequence;IILandroid/view/textclassifier/TextSelection$Options;)Landroid/view/textclassifier/TextSelection;
 Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
 Landroid/view/TextureView;->destroyHardwareLayer()V
 Landroid/view/TextureView;->destroyHardwareResources()V
@@ -2286,6 +2447,7 @@
 Landroid/view/View;->dispatchDetachedFromWindow()V
 Landroid/view/View;->fitsSystemWindows()Z
 Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate;
+Landroid/view/View;->getAccessibilityViewId()I
 Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V
 Landroid/view/View;->getHorizontalScrollFactor()F
 Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix;
@@ -2311,6 +2473,7 @@
 Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
 Landroid/view/ViewGroup;->mPersistentDrawingCache:I
 Landroid/view/ViewGroup;->offsetChildrenTopAndBottom(I)V
+Landroid/view/ViewGroup;->onChildVisibilityChanged(Landroid/view/View;II)V
 Landroid/view/ViewGroup;->resetResolvedDrawables()V
 Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V
 Landroid/view/ViewGroup;->resetResolvedPadding()V
@@ -2321,6 +2484,7 @@
 Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
 Landroid/view/View;->internalSetPadding(IIII)V
 Landroid/view/View;->isPaddingResolved()Z
+Landroid/view/View;->isRootNamespace()Z
 Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
 Landroid/view/View;->isVisibleToUser()Z
 Landroid/view/View$ListenerInfo;-><init>()V
@@ -2330,6 +2494,7 @@
 Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
 Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
 Landroid/view/View;->mAttachInfo:Landroid/view/View$AttachInfo;
+Landroid/view/View;->mBackground:Landroid/graphics/drawable/Drawable;
 Landroid/view/View;->mBottom:I
 Landroid/view/View;->mContext:Landroid/content/Context;
 Landroid/view/View;->mDrawingCache:Landroid/graphics/Bitmap;
@@ -2343,6 +2508,7 @@
 Landroid/view/View;->mPaddingRight:I
 Landroid/view/View;->mPaddingTop:I
 Landroid/view/View;->mParent:Landroid/view/ViewParent;
+Landroid/view/View;->mPrivateFlags2:I
 Landroid/view/View;->mPrivateFlags3:I
 Landroid/view/View;->mPrivateFlags:I
 Landroid/view/View;->mRecreateDisplayList:Z
@@ -2370,7 +2536,10 @@
 Landroid/view/View;->resetResolvedTextDirection()V
 Landroid/view/View;->resetRtlProperties()V
 Landroid/view/ViewRootImpl;->detachFunctor(J)V
+Landroid/view/ViewRootImpl;->dispatchInputEvent(Landroid/view/InputEvent;Landroid/view/InputEventReceiver;)V
+Landroid/view/ViewRootImpl;->dispatchInputEvent(Landroid/view/InputEvent;)V
 Landroid/view/ViewRootImpl;->enqueueInputEvent(Landroid/view/InputEvent;)V
+Landroid/view/ViewRootImpl;->getWindowFlags()I
 Landroid/view/ViewRootImpl;->invokeFunctor(JZ)V
 Landroid/view/ViewRootImpl;->mStopped:Z
 Landroid/view/ViewRootImpl;->mSurface:Landroid/view/Surface;
@@ -2423,8 +2592,31 @@
 Landroid/view/Window;->mContext:Landroid/content/Context;
 Landroid/view/Window;->mHardwareAccelerated:Z
 Landroid/view/Window;->mWindowStyle:Landroid/content/res/TypedArray;
+Landroid/webkit/CacheManager;->cacheDisabled()Z
+Landroid/webkit/CacheManager$CacheResult;->getContentDisposition()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getContentLength()J
+Landroid/webkit/CacheManager$CacheResult;->getEncoding()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getETag()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getExpires()J
+Landroid/webkit/CacheManager$CacheResult;->getExpiresString()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getHttpStatusCode()I
+Landroid/webkit/CacheManager$CacheResult;->getInputStream()Ljava/io/InputStream;
+Landroid/webkit/CacheManager$CacheResult;->getLastModified()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getLocalPath()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getLocation()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getMimeType()Ljava/lang/String;
+Landroid/webkit/CacheManager$CacheResult;->getOutputStream()Ljava/io/OutputStream;
+Landroid/webkit/CacheManager$CacheResult;-><init>()V
+Landroid/webkit/CacheManager$CacheResult;->setEncoding(Ljava/lang/String;)V
+Landroid/webkit/CacheManager$CacheResult;->setInputStream(Ljava/io/InputStream;)V
+Landroid/webkit/CacheManager;->endCacheTransaction()Z
+Landroid/webkit/CacheManager;->getCacheFileBaseDir()Ljava/io/File;
+Landroid/webkit/CacheManager;->getCacheFile(Ljava/lang/String;Ljava/util/Map;)Landroid/webkit/CacheManager$CacheResult;
+Landroid/webkit/CacheManager;->saveCacheFile(Ljava/lang/String;Landroid/webkit/CacheManager$CacheResult;)V
+Landroid/webkit/CacheManager;->startCacheTransaction()Z
 Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/webkit/WebResourceResponse;->mImmutable:Z
+Landroid/webkit/WebSettings$TextSize;->value:I
 Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
 Landroid/webkit/WebViewClient;->onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V
 Landroid/webkit/WebView;->debugDump()V
@@ -2435,11 +2627,13 @@
 Landroid/webkit/WebViewFactory;->getWebViewContextAndSetProvider()Landroid/content/Context;
 Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo;
 Landroid/webkit/WebViewFactory;->sProviderInstance:Landroid/webkit/WebViewFactoryProvider;
+Landroid/webkit/WebView;->getContentWidth()I
 Landroid/webkit/WebView;->getTouchIconUrl()Ljava/lang/String;
 Landroid/webkit/WebView;->getVisibleTitleHeight()I
 Landroid/webkit/WebView;->isPaused()Z
 Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
 Landroid/webkit/WebView;->notifyFindDialogDismissed()V
+Landroid/webkit/WebView;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
 Landroid/webkit/WebView;->restorePicture(Landroid/os/Bundle;Ljava/io/File;)Z
 Landroid/webkit/WebView;->savePicture(Landroid/os/Bundle;Ljava/io/File;)Z
 Landroid/webkit/WebView;->sEnforceThreadChecking:Z
@@ -2454,6 +2648,7 @@
 Landroid/widget/AbsListView;->mFastScroll:Landroid/widget/FastScroller;
 Landroid/widget/AbsListView;->mFlingRunnable:Landroid/widget/AbsListView$FlingRunnable;
 Landroid/widget/AbsListView;->mIsChildViewEnabled:Z
+Landroid/widget/AbsListView;->mLayoutMode:I
 Landroid/widget/AbsListView;->mMaximumVelocity:I
 Landroid/widget/AbsListView;->mMotionPosition:I
 Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
@@ -2480,9 +2675,12 @@
 Landroid/widget/AbsSeekBar;->mThumb:Landroid/graphics/drawable/Drawable;
 Landroid/widget/AbsSeekBar;->mTouchProgressOffset:F
 Landroid/widget/ActivityChooserModel;->get(Landroid/content/Context;Ljava/lang/String;)Landroid/widget/ActivityChooserModel;
+Landroid/widget/ActivityChooserModel$PersistHistoryAsyncTask;->doInBackground([Ljava/lang/Object;)Ljava/lang/Void;
 Landroid/widget/ActivityChooserView;->setExpandActivityOverflowButtonDrawable(Landroid/graphics/drawable/Drawable;)V
 Landroid/widget/AdapterView;->mDataChanged:Z
 Landroid/widget/AdapterView;->mFirstPosition:I
+Landroid/widget/AdapterView;->mNextSelectedPosition:I
+Landroid/widget/AdapterView;->mNextSelectedRowId:J
 Landroid/widget/AdapterView;->mOldSelectedPosition:I
 Landroid/widget/AdapterView;->setNextSelectedPositionInt(I)V
 Landroid/widget/AdapterView;->setSelectedPositionInt(I)V
@@ -2492,6 +2690,7 @@
 Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
 Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
 Landroid/widget/AutoCompleteTextView;->setForceIgnoreOutsideTouch(Z)V
+Landroid/widget/BaseAdapter;->mDataSetObservable:Landroid/database/DataSetObservable;
 Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
 Landroid/widget/CompoundButton;->mOnCheckedChangeListener:Landroid/widget/CompoundButton$OnCheckedChangeListener;
 Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver;
@@ -2505,6 +2704,7 @@
 Landroid/widget/Editor;->mShowSoftInputOnFocus:Z
 Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
 Landroid/widget/ExpandableListView;->mGroupIndicator:Landroid/graphics/drawable/Drawable;
+Landroid/widget/FastScroller;->groupAnimatorOfFloat(Landroid/util/Property;F[Landroid/view/View;)Landroid/animation/Animator;
 Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect;
 Landroid/widget/FastScroller;->mHeaderCount:I
 Landroid/widget/FastScroller;->mLongList:Z
@@ -2560,6 +2760,7 @@
 Landroid/widget/NumberPicker;->mInputText:Landroid/widget/EditText;
 Landroid/widget/NumberPicker;->mSelectionDivider:Landroid/graphics/drawable/Drawable;
 Landroid/widget/NumberPicker;->mSelectorWheelPaint:Landroid/graphics/Paint;
+Landroid/widget/OverScroller;->isScrollingInDirection(FF)Z
 Landroid/widget/OverScroller;->mScrollerY:Landroid/widget/OverScroller$SplineOverScroller;
 Landroid/widget/OverScroller$SplineOverScroller;->mCurrVelocity:F
 Landroid/widget/PopupMenu;->mPopup:Lcom/android/internal/view/menu/MenuPopupHelper;
@@ -2587,12 +2788,14 @@
 Landroid/widget/PopupWindow;->setLayoutInScreenEnabled(Z)V
 Landroid/widget/PopupWindow;->setLayoutInsetDecor(Z)V
 Landroid/widget/PopupWindow;->setTouchModal(Z)V
+Landroid/widget/PopupWindow;->showAtLocation(Landroid/os/IBinder;III)V
 Landroid/widget/ProgressBar;->mCurrentDrawable:Landroid/graphics/drawable/Drawable;
 Landroid/widget/ProgressBar;->mDuration:I
 Landroid/widget/ProgressBar;->mIndeterminate:Z
 Landroid/widget/ProgressBar;->mMaxHeight:I
 Landroid/widget/ProgressBar;->mMinHeight:I
 Landroid/widget/ProgressBar;->mOnlyIndeterminate:Z
+Landroid/widget/RelativeLayout$DependencyGraph;->getSortedViews([Landroid/view/View;[I)V
 Landroid/widget/RelativeLayout$LayoutParams;->mBottom:I
 Landroid/widget/RelativeLayout$LayoutParams;->mLeft:I
 Landroid/widget/RelativeLayout$LayoutParams;->mRight:I
@@ -2602,6 +2805,7 @@
 Landroid/widget/RemoteViews$Action;->viewId:I
 Landroid/widget/RemoteViewsAdapter;->mCache:Landroid/widget/RemoteViewsAdapter$FixedSizeRemoteViewsCache;
 Landroid/widget/RemoteViewsAdapter;->mWorkerThread:Landroid/os/HandlerThread;
+Landroid/widget/RemoteViews$AsyncApplyTask;->doInBackground([Ljava/lang/Void;)Landroid/widget/RemoteViews$ViewTree;
 Landroid/widget/RemoteViews$BitmapCache;->mBitmaps:Ljava/util/ArrayList;
 Landroid/widget/RemoteViews$BitmapReflectionAction;->bitmap:Landroid/graphics/Bitmap;
 Landroid/widget/RemoteViews$BitmapReflectionAction;->methodName:Ljava/lang/String;
@@ -2632,6 +2836,7 @@
 Landroid/widget/SearchView;->mSearchSrcTextView:Landroid/widget/SearchView$SearchAutoComplete;
 Landroid/widget/SearchView;->onCloseClicked()V
 Landroid/widget/SearchView;->setQuery(Ljava/lang/CharSequence;)V
+Landroid/widget/SelectionActionModeHelper$TextClassificationAsyncTask;->doInBackground([Ljava/lang/Void;)Landroid/widget/SelectionActionModeHelper$SelectionResult;
 Landroid/widget/SlidingDrawer;->mTopOffset:I
 Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
 Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
@@ -2741,6 +2946,10 @@
 Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setFeatureTag(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/presence/PresTupleInfo;->setTimestamp(Ljava/lang/String;)V
 Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V
+Lcom/android/internal/app/AlertController;->mCustomTitleView:Landroid/view/View;
+Lcom/android/internal/app/AlertController;->mForceInverseBackground:Z
+Lcom/android/internal/app/AlertController;->mTitle:Ljava/lang/CharSequence;
+Lcom/android/internal/app/AlertController;->mView:Landroid/view/View;
 Lcom/android/internal/app/AlertController$RecycleListView;-><init>(Landroid/content/Context;Landroid/util/AttributeSet;)V
 Lcom/android/internal/app/IAppOpsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IAppOpsService;
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I
@@ -2932,6 +3141,7 @@
 Lcom/android/internal/R$styleable;->DialogPreference:[I
 Lcom/android/internal/R$styleable;->EdgeEffect_colorEdgeEffect:I
 Lcom/android/internal/R$styleable;->EdgeEffect:[I
+Lcom/android/internal/R$styleable;->GridView:[I
 Lcom/android/internal/R$styleable;->IconMenuView:[I
 Lcom/android/internal/R$styleable;->ImageView:[I
 Lcom/android/internal/R$styleable;->ImageView_src:I
@@ -2975,6 +3185,7 @@
 Lcom/android/internal/R$styleable;->SyncAdapter_userVisible:I
 Lcom/android/internal/R$styleable;->TabWidget:[I
 Lcom/android/internal/R$styleable;->TextAppearance:[I
+Lcom/android/internal/R$styleable;->TextViewAppearance:[I
 Lcom/android/internal/R$styleable;->TextView_drawableBottom:I
 Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
 Lcom/android/internal/R$styleable;->TextView_drawableRight:I
@@ -3021,6 +3232,11 @@
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I
 Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I
+Lcom/android/internal/telephony/SmsHeader;->concatRef:Lcom/android/internal/telephony/SmsHeader$ConcatRef;
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->msgCount:I
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->refNumber:I
+Lcom/android/internal/telephony/SmsHeader$ConcatRef;->seqNumber:I
+Lcom/android/internal/telephony/SmsMessageBase;->mUserDataHeader:Lcom/android/internal/telephony/SmsHeader;
 Lcom/android/internal/telephony/SmsRawData;->CREATOR:Landroid/os/Parcelable$Creator;
 Lcom/android/internal/telephony/SmsRawData;-><init>([B)V
 Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -3029,6 +3245,8 @@
 Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap;
 Lcom/android/internal/util/XmlUtils;->skipCurrentTag(Lorg/xmlpull/v1/XmlPullParser;)V
 Lcom/android/internal/util/XmlUtils;->writeMapXml(Ljava/util/Map;Ljava/io/OutputStream;)V
+Lcom/android/internal/view/IInputConnectionWrapper;->mInputConnection:Landroid/view/inputmethod/InputConnection;
+Lcom/android/internal/view/IInputConnectionWrapper;->mLock:Ljava/lang/Object;
 Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager;
 Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List;
 Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -3039,11 +3257,19 @@
 Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
 Lcom/android/internal/view/menu/MenuItemImpl;->mIconResId:I
 Lcom/android/internal/view/menu/MenuItemImpl;->setMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
+Lcom/android/internal/view/menu/MenuPopupHelper;->mForceShowIcon:Z
 Lcom/android/internal/view/menu/MenuPopupHelper;->setForceShowIcon(Z)V
 Lcom/android/internal/view/menu/MenuView$ItemView;->getItemData()Lcom/android/internal/view/menu/MenuItemImpl;
+Lcom/android/okhttp/CertificatePinner$Builder;->add(Ljava/lang/String;[Ljava/lang/String;)Lcom/android/okhttp/CertificatePinner$Builder;
+Lcom/android/okhttp/CertificatePinner;->check(Ljava/lang/String;[Ljava/security/cert/Certificate;)V
 Lcom/android/okhttp/ConnectionPool;->keepAliveDurationNs:J
 Lcom/android/okhttp/ConnectionPool;->maxIdleConnections:I
 Lcom/android/okhttp/ConnectionPool;->systemDefault:Lcom/android/okhttp/ConnectionPool;
+Lcom/android/okhttp/ConnectionSpec$Builder;->cipherSuites([Lcom/android/okhttp/CipherSuite;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->cipherSuites([Ljava/lang/String;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->tlsVersions([Lcom/android/okhttp/TlsVersion;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/ConnectionSpec$Builder;->tlsVersions([Ljava/lang/String;)Lcom/android/okhttp/ConnectionSpec$Builder;
+Lcom/android/okhttp/Headers;->of([Ljava/lang/String;)Lcom/android/okhttp/Headers;
 Lcom/android/okhttp/HttpUrl;->encodedPath()Ljava/lang/String;
 Lcom/android/okhttp/HttpUrl;->query()Ljava/lang/String;
 Lcom/android/okhttp/internal/http/HttpEngine;->httpStream:Lcom/android/okhttp/internal/http/HttpStream;
@@ -3051,11 +3277,13 @@
 Lcom/android/okhttp/internal/http/HttpEngine;->networkRequest(Lcom/android/okhttp/Request;)Lcom/android/okhttp/Request;
 Lcom/android/okhttp/internal/http/HttpEngine;->priorResponse:Lcom/android/okhttp/Response;
 Lcom/android/okhttp/internal/http/HttpEngine;->userResponse:Lcom/android/okhttp/Response;
+Lcom/android/okhttp/internal/NamedRunnable;-><init>(Ljava/lang/String;[Ljava/lang/Object;)V
 Lcom/android/okhttp/OkHttpClient;->connectionPool:Lcom/android/okhttp/ConnectionPool;
 Lcom/android/okhttp/OkHttpClient;->DEFAULT_PROTOCOLS:Ljava/util/List;
 Lcom/android/okhttp/OkHttpClient;->dns:Lcom/android/okhttp/Dns;
 Lcom/android/okhttp/OkHttpClient;->setProtocols(Ljava/util/List;)Lcom/android/okhttp/OkHttpClient;
 Lcom/android/okhttp/OkHttpClient;->setRetryOnConnectionFailure(Z)V
+Lcom/android/okhttp/okio/ByteString;->of([B)Lcom/android/okhttp/okio/ByteString;
 Lcom/android/okhttp/Request;->headers:Lcom/android/okhttp/Headers;
 Lcom/android/okhttp/Request;->method:Ljava/lang/String;
 Lcom/android/okhttp/Request;->url:Lcom/android/okhttp/HttpUrl;
@@ -3176,6 +3404,7 @@
 Ljava/lang/AbstractStringBuilder;->value:[C
 Ljava/lang/Boolean;->value:Z
 Ljava/lang/Byte;->value:B
+Ljava/lang/Character$UnicodeBlock;-><init>(Ljava/lang/String;[Ljava/lang/String;)V
 Ljava/lang/Character;->value:C
 Ljava/lang/Class;->accessFlags:I
 Ljava/lang/Class;->dexCache:Ljava/lang/Object;
@@ -3204,16 +3433,22 @@
 Ljava/lang/ref/FinalizerReference;->next:Ljava/lang/ref/FinalizerReference;
 Ljava/lang/ref/FinalizerReference;->queue:Ljava/lang/ref/ReferenceQueue;
 Ljava/lang/ref/FinalizerReference;->remove(Ljava/lang/ref/FinalizerReference;)V
+Ljava/lang/reflect/Constructor;->newInstance0([Ljava/lang/Object;)Ljava/lang/Object;
 Ljava/lang/reflect/Executable;->artMethod:J
 Ljava/lang/reflect/Parameter;-><init>(Ljava/lang/String;ILjava/lang/reflect/Executable;I)V
+Ljava/lang/reflect/Proxy;->getProxyClass0(Ljava/lang/ClassLoader;[Ljava/lang/Class;)Ljava/lang/Class;
 Ljava/lang/reflect/Proxy;->invoke(Ljava/lang/reflect/Proxy;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;
+Ljava/lang/ref/Reference;->getReferent()Ljava/lang/Object;
 Ljava/lang/ref/ReferenceQueue;->add(Ljava/lang/ref/Reference;)V
 Ljava/lang/ref/Reference;->referent:Ljava/lang/Object;
 Ljava/lang/Runtime;->loadLibrary(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
+Ljava/lang/Runtime;->mLibPaths:[Ljava/lang/String;
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
 Ljava/lang/Short;->value:S
+Ljava/lang/String;->getCharsNoCheck(II[CI)V
 Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([CI[CII)V
 Ljava/lang/System;->arraycopy([II[III)V
 Ljava/lang/System;-><init>()V
 Ljava/lang/Thread;->daemon:Z
@@ -3232,7 +3467,9 @@
 Ljava/lang/Thread;->lock:Ljava/lang/Object;
 Ljava/lang/Thread;->name:Ljava/lang/String;
 Ljava/lang/Thread;->nativePeer:J
+Ljava/lang/Thread;->parkBlocker:Ljava/lang/Object;
 Ljava/lang/Thread;->priority:I
+Ljava/lang/Thread;->threadLocals:Ljava/lang/ThreadLocal$ThreadLocalMap;
 Ljava/lang/Throwable;->backtrace:Ljava/lang/Object;
 Ljava/lang/Throwable;->cause:Ljava/lang/Throwable;
 Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
@@ -3264,7 +3501,10 @@
 Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
 Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
 Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
+Ljava/net/URI;->fragment:Ljava/lang/String;
 Ljava/net/URI;->host:Ljava/lang/String;
+Ljava/net/URI;->port:I
+Ljava/net/URI;->string:Ljava/lang/String;
 Ljava/net/URL;->handler:Ljava/net/URLStreamHandler;
 Ljava/net/URL;->handlers:Ljava/util/Hashtable;
 Ljava/nio/Buffer;->address:J
@@ -3277,6 +3517,9 @@
 Ljava/nio/ByteBuffer;->offset:I
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
 Ljava/nio/DirectByteBuffer;-><init>(JI)V
+Ljava/nio/file/Files;->createAndCheckIsDirectory(Ljava/nio/file/Path;[Ljava/nio/file/attribute/FileAttribute;)V
+Ljava/nio/file/Files;->followLinks([Ljava/nio/file/LinkOption;)Z
+Ljava/nio/file/Files;->isAccessible(Ljava/nio/file/Path;[Ljava/nio/file/AccessMode;)Z
 Ljava/nio/NIOAccess;->getBaseArray(Ljava/nio/Buffer;)Ljava/lang/Object;
 Ljava/nio/NIOAccess;->getBaseArrayOffset(Ljava/nio/Buffer;)I
 Ljava/nio/NIOAccess;->getBasePointer(Ljava/nio/Buffer;)J
@@ -3285,6 +3528,9 @@
 Ljava/text/DateFormat;->is24Hour:Ljava/lang/Boolean;
 Ljava/time/Duration;->toSeconds()Ljava/math/BigDecimal;
 Ljava/time/OffsetDateTime;-><init>(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)V
+Ljava/util/ArrayDeque;->elements:[Ljava/lang/Object;
+Ljava/util/ArrayDeque;->head:I
+Ljava/util/ArrayDeque;->tail:I
 Ljava/util/ArrayList;->elementData:[Ljava/lang/Object;
 Ljava/util/ArrayList;->size:I
 Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
@@ -3297,22 +3543,40 @@
 Ljava/util/Collections$SynchronizedMap;->m:Ljava/util/Map;
 Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection;
 Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map;
+Ljava/util/concurrent/atomic/AtomicInteger;->value:I
 Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z
+Ljava/util/concurrent/CopyOnWriteArraySet;->al:Ljava/util/concurrent/CopyOnWriteArrayList;
 Ljava/util/concurrent/Executors$RunnableAdapter;->task:Ljava/lang/Runnable;
 Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
 Ljava/util/concurrent/FutureTask;->EXCEPTIONAL:I
 Ljava/util/concurrent/FutureTask;->outcome:Ljava/lang/Object;
 Ljava/util/concurrent/FutureTask;->state:I
+Ljava/util/concurrent/LinkedBlockingDeque;->first:Ljava/util/concurrent/LinkedBlockingDeque$Node;
+Ljava/util/concurrent/LinkedBlockingDeque;->lock:Ljava/util/concurrent/locks/ReentrantLock;
 Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I
+Ljava/util/concurrent/LinkedBlockingQueue;->head:Ljava/util/concurrent/LinkedBlockingQueue$Node;
+Ljava/util/concurrent/LinkedBlockingQueue;->putLock:Ljava/util/concurrent/locks/ReentrantLock;
+Ljava/util/concurrent/LinkedBlockingQueue;->takeLock:Ljava/util/concurrent/locks/ReentrantLock;
+Ljava/util/concurrent/ThreadPoolExecutor;->allowCoreThreadTimeOut:Z
 Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
 Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
+Ljava/util/Formatter$FormatSpecifier;->checkBadFlags([Ljava/util/Formatter$Flags;)V
 Ljava/util/HashMap$HashIterator;->hasNext()Z
+Ljava/util/HashMap;->modCount:I
+Ljava/util/HashMap;->table:[Ljava/util/HashMap$Node;
+Ljava/util/HashSet;->map:Ljava/util/HashMap;
 Ljava/util/jar/JarFile;->manifest:Ljava/util/jar/Manifest;
 Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry;
 Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z
+Ljava/util/LinkedList;->size:I
 Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
+Ljava/util/logging/LogManager$Beans;->getConstructor(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
+Ljava/util/logging/LogManager$Beans;->getMethod(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;
+Ljava/util/PriorityQueue;->modCount:I
+Ljava/util/PriorityQueue;->size:I
 Ljava/util/Random;->seedUniquifier()J
 Ljava/util/regex/Matcher;->appendPos:I
+Ljava/util/Vector;->elementData(I)Ljava/lang/Object;
 Ljava/util/zip/Deflater;->buf:[B
 Ljava/util/zip/Deflater;->finished:Z
 Ljava/util/zip/Deflater;->finish:Z
@@ -3342,3 +3606,8 @@
 Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V
 Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node;
 Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe;
+Lsun/security/x509/AlgorithmId;->oid([I)Lsun/security/util/ObjectIdentifier;
+Lsun/util/logging/PlatformLogger$DefaultLoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
+Lsun/util/logging/PlatformLogger$DefaultLoggerProxy;->formatMessage(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
+Lsun/util/logging/PlatformLogger$JavaLoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
+Lsun/util/logging/PlatformLogger$LoggerProxy;->doLog(Lsun/util/logging/PlatformLogger$Level;Ljava/lang/String;[Ljava/lang/Object;)V
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index e3d2e77..88f8794 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -501,7 +501,6 @@
 Landroid/telephony/SubscriptionManager;->isValidSubscriptionId(I)Z
 Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;II)V
 Landroid/telephony/SubscriptionManager;->putPhoneIdAndSubIdExtra(Landroid/content/Intent;I)V
-Landroid/telephony/SubscriptionManager;->setDefaultDataSubId(I)V
 Landroid/telephony/SubscriptionManager;->setDisplayName(Ljava/lang/String;IJ)I
 Landroid/telephony/SubscriptionManager;->setIconTint(II)I
 Landroid/telephony/TelephonyManager;->getIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;I)I
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 27bbc4b..1084b42 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2165,30 +2165,18 @@
     }
 
     @Override
-    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+    public Bundle getSuspendedPackageAppExtras() {
+        final PersistableBundle extras;
         try {
-            return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId());
+            extras = mPM.getSuspendedPackageAppExtras(mContext.getOpPackageName(),
+                    mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
-    }
-
-    @Override
-    public Bundle getSuspendedPackageAppExtras() {
-        final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName());
         return extras != null ? new Bundle(extras.deepCopy()) : null;
     }
 
     @Override
-    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras) {
-        try {
-            mPM.setSuspendedPackageAppExtras(packageName, appExtras, mContext.getUserId());
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-        }
-    }
-
-    @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         try {
             return mPM.isPackageSuspendedForUser(packageName, userId);
@@ -2199,8 +2187,12 @@
 
     /** @hide */
     @Override
-    public boolean isPackageSuspended(String packageName) {
-        return isPackageSuspendedForUser(packageName, mContext.getUserId());
+    public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
+        try {
+            return isPackageSuspendedForUser(packageName, mContext.getUserId());
+        } catch (IllegalArgumentException ie) {
+            throw new NameNotFoundException(packageName);
+        }
     }
 
     @Override
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 4a3fcaf..b512e5c 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -33,7 +33,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.persistentdata.IPersistentDataBlockService;
 import android.util.Log;
@@ -48,8 +47,8 @@
 import java.util.List;
 
 /**
- * Class that can be used to lock and unlock the keyboard. The
- * actual class to control the keyboard locking is
+ * Class that can be used to lock and unlock the keyguard. The
+ * actual class to control the keyguard locking is
  * {@link android.app.KeyguardManager.KeyguardLock}.
  */
 @SystemService(Context.KEYGUARD_SERVICE)
@@ -343,11 +342,11 @@
      * moves in and out of the foreground and does not require that any special
      * permissions be requested.
      *
-     * Enables you to lock or unlock the keyboard. Get an instance of this class by
+     * Enables you to lock or unlock the keyguard. Get an instance of this class by
      * calling {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
      * This class is wrapped by {@link android.app.KeyguardManager KeyguardManager}.
      * @param tag A tag that informally identifies who you are (for debugging who
-     *   is disabling he keyguard).
+     *   is disabling the keyguard).
      *
      * @return A {@link KeyguardLock} handle to use to disable and reenable the
      *   keyguard.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4ab6724..2b4f420 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4588,10 +4588,18 @@
                 bindHeaderChronometerAndTime(contentView);
                 bindProfileBadge(contentView);
             }
+            bindActivePermissions(contentView);
             bindExpandButton(contentView);
             mN.mUsesStandardHeader = true;
         }
 
+        private void bindActivePermissions(RemoteViews contentView) {
+            int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
+            contentView.setDrawableTint(R.id.camera, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.mic, false, color, PorterDuff.Mode.SRC_ATOP);
+            contentView.setDrawableTint(R.id.overlay, false, color, PorterDuff.Mode.SRC_ATOP);
+        }
+
         private void bindExpandButton(RemoteViews contentView) {
             int color = isColorized() ? getPrimaryTextColor() : getSecondaryTextColor();
             contentView.setDrawableTint(R.id.expand_button, false, color,
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 4a7cf62..9e47ced 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -328,7 +328,8 @@
      * Group information is only used for presentation, not for behavior.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the
+     * channel is not currently part of a group.
      *
      * @param groupId the id of a group created by
      * {@link NotificationManager#createNotificationChannelGroup(NotificationChannelGroup)}.
@@ -341,6 +342,9 @@
      * Sets whether notifications posted to this channel can appear as application icon badges
      * in a Launcher.
      *
+     * Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
+     *
      * @param showBadge true if badges should be allowed to be shown.
      */
     public void setShowBadge(boolean showBadge) {
@@ -353,7 +357,7 @@
      * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setSound(Uri sound, AudioAttributes audioAttributes) {
         this.mSound = sound;
@@ -365,7 +369,7 @@
      * on devices that support that feature.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void enableLights(boolean lights) {
         this.mLights = lights;
@@ -376,7 +380,7 @@
      * {@link #enableLights(boolean) enabled} on this channel and the device supports that feature.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setLightColor(int argb) {
         this.mLightColor = argb;
@@ -387,7 +391,7 @@
      * be set with {@link #setVibrationPattern(long[])}.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void enableVibration(boolean vibration) {
         this.mVibrationEnabled = vibration;
@@ -399,7 +403,7 @@
      * vibration} as well. Otherwise, vibration will be disabled.
      *
      * Only modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      */
     public void setVibrationPattern(long[] vibrationPattern) {
         this.mVibrationEnabled = vibrationPattern != null && vibrationPattern.length > 0;
@@ -407,9 +411,10 @@
     }
 
     /**
-     * Sets the level of interruption of this notification channel. Only
-     * modifiable before the channel is submitted to
-     * {@link NotificationManager#notify(String, int, Notification)}.
+     * Sets the level of interruption of this notification channel.
+     *
+     * Only modifiable before the channel is submitted to
+     * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.
      *
      * @param importance the amount the user should be interrupted by
      *            notifications from this channel.
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 5113fd0..757fc64 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1338,6 +1338,32 @@
             return false;
         }
 
+        /**
+         * @hide
+         */
+        public static int toggleScreenOffEffectsSuppressed(int currentEffects, boolean suppress) {
+            return toggleEffects(currentEffects, SCREEN_OFF_SUPPRESSED_EFFECTS, suppress);
+        }
+
+        /**
+         * @hide
+         */
+        public static int toggleScreenOnEffectsSuppressed(int currentEffects, boolean suppress) {
+            return toggleEffects(currentEffects, SCREEN_ON_SUPPRESSED_EFFECTS, suppress);
+        }
+
+        private static int toggleEffects(int currentEffects, int[] effects, boolean suppress) {
+            for (int i = 0; i < effects.length; i++) {
+                final int effect = effects[i];
+                if (suppress) {
+                    currentEffects |= effect;
+                } else {
+                    currentEffects &= ~effect;
+                }
+            }
+            return currentEffects;
+        }
+
         public static String suppressedEffectsToString(int effects) {
             if (effects <= 0) return "";
             final StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 4a6fa8c..8783d94 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -23,6 +23,7 @@
 import android.os.IStatsManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.util.AndroidException;
 import android.util.Slog;
 
 /**
@@ -82,55 +83,74 @@
     }
 
     /**
-     * Clients can send a configuration and simultaneously registers the name of a broadcast
-     * receiver that listens for when it should request data.
+     * Adds the given configuration and associates it with the given configKey. If a config with the
+     * given configKey already exists for the caller's uid, it is replaced with the new one.
      *
      * @param configKey An arbitrary integer that allows clients to track the configuration.
-     * @param config    Wire-encoded StatsDConfig proto that specifies metrics (and all
+     * @param config    Wire-encoded StatsdConfig proto that specifies metrics (and all
      *                  dependencies eg, conditions and matchers).
-     * @return true if successful
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     * @throws IllegalArgumentException if config is not a wire-encoded StatsdConfig proto
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean addConfiguration(long configKey, byte[] config) {
+    public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when adding configuration");
-                    return false;
-                }
-                return service.addConfiguration(configKey, config);
+                service.addConfiguration(configKey, config); // can throw IllegalArgumentException
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when adding configuration");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean addConfiguration(long configKey, byte[] config) {
+        try {
+            addConfig(configKey, config);
+            return true;
+        } catch (StatsUnavailableException | IllegalArgumentException e) {
+            return false;
+        }
+    }
+
+    /**
      * Remove a configuration from logging.
      *
      * @param configKey Configuration key to remove.
-     * @return true if successful
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean removeConfiguration(long configKey) {
+    public void removeConfig(long configKey) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when removing configuration");
-                    return false;
-                }
-                return service.removeConfiguration(configKey);
+                service.removeConfiguration(configKey);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when removing configuration");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public boolean removeConfiguration(long configKey) {
+        try {
+            removeConfig(configKey);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
+        }
+    }
+
+    /**
      * Set the PendingIntent to be used when broadcasting subscriber information to the given
      * subscriberId within the given config.
      * <p>
@@ -150,123 +170,165 @@
      * {@link #EXTRA_STATS_DIMENSIONS_VALUE}.
      * <p>
      * This function can only be called by the owner (uid) of the config. It must be called each
-     * time statsd starts. The config must have been added first (via addConfiguration()).
+     * time statsd starts. The config must have been added first (via {@link #addConfig}).
      *
-     * @param configKey     The integer naming the config to which this subscriber is attached.
-     * @param subscriberId  ID of the subscriber, as used in the config.
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it undoes any previous setting of this subscriberId.
-     * @return true if successful
+     * @param configKey     The integer naming the config to which this subscriber is attached.
+     * @param subscriberId  ID of the subscriber, as used in the config.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public void setBroadcastSubscriber(
+            PendingIntent pendingIntent, long configKey, long subscriberId)
+            throws StatsUnavailableException {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                if (pendingIntent != null) {
+                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
+                    IBinder intentSender = pendingIntent.getTarget().asBinder();
+                    service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
+                } else {
+                    service.unsetBroadcastSubscriber(configKey, subscriberId);
+                }
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
+                throw new StatsUnavailableException("could not connect", e);
+            }
+        }
+    }
+
+    /**
+     * TODO: Temporary for backwards compatibility. Remove.
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public boolean setBroadcastSubscriber(
             long configKey, long subscriberId, PendingIntent pendingIntent) {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when adding broadcast subscriber");
-                    return false;
-                }
-                if (pendingIntent != null) {
-                    // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
-                    IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    return service.setBroadcastSubscriber(configKey, subscriberId, intentSender);
-                } else {
-                    return service.unsetBroadcastSubscriber(configKey, subscriberId);
-                }
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when adding broadcast subscriber", e);
-                return false;
-            }
+        try {
+            setBroadcastSubscriber(pendingIntent, configKey, subscriberId);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
         }
     }
 
     /**
      * Registers the operation that is called to retrieve the metrics data. This must be called
-     * each time statsd starts. The config must have been added first (via addConfiguration(),
-     * although addConfiguration could have been called on a previous boot). This operation allows
+     * each time statsd starts. The config must have been added first (via {@link #addConfig},
+     * although addConfig could have been called on a previous boot). This operation allows
      * statsd to send metrics data whenever statsd determines that the metrics in memory are
-     * approaching the memory limits. The fetch operation should call {@link #getData} to fetch the
-     * data, which also deletes the retrieved metrics from statsd's memory.
+     * approaching the memory limits. The fetch operation should call {@link #getReports} to fetch
+     * the data, which also deletes the retrieved metrics from statsd's memory.
      *
-     * @param configKey     The integer naming the config to which this operation is attached.
      * @param pendingIntent the PendingIntent to use when broadcasting info to the subscriber
      *                      associated with the given subscriberId. May be null, in which case
      *                      it removes any associated pending intent with this configKey.
-     * @return true if successful
+     * @param configKey     The integer naming the config to which this operation is attached.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+    public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
+            throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when registering data listener.");
-                    return false;
-                }
                 if (pendingIntent == null) {
-                    return service.removeDataFetchOperation(configKey);
+                    service.removeDataFetchOperation(configKey);
                 } else {
                     // Extracts IIntentSender from the PendingIntent and turns it into an IBinder.
                     IBinder intentSender = pendingIntent.getTarget().asBinder();
-                    return service.setDataFetchOperation(configKey, intentSender);
+                    service.setDataFetchOperation(configKey, intentSender);
                 }
 
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when registering data listener.");
-                return false;
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
-     * Clients can request data with a binder call. This getter is destructive and also clears
-     * the retrieved metrics from statsd memory.
-     *
-     * @param configKey Configuration key to retrieve data from.
-     * @return Serialized ConfigMetricsReportList proto. Returns null on failure (eg, if statsd
-     * crashed).
+     * TODO: Temporary for backwards compatibility. Remove.
      */
     @RequiresPermission(Manifest.permission.DUMP)
-    public @Nullable byte[] getData(long configKey) {
+    public boolean setDataFetchOperation(long configKey, PendingIntent pendingIntent) {
+        try {
+            setFetchReportsOperation(pendingIntent, configKey);
+            return true;
+        } catch (StatsUnavailableException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Request the data collected for the given configKey.
+     * This getter is destructive - it also clears the retrieved metrics from statsd's memory.
+     *
+     * @param configKey Configuration key to retrieve data from.
+     * @return Serialized ConfigMetricsReportList proto.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getReports(long configKey) throws StatsUnavailableException {
         synchronized (this) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when getting data");
-                    return null;
-                }
                 return service.getData(configKey);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to connect to statsd when getting data");
-                return null;
+                throw new StatsUnavailableException("could not connect", e);
+            }
+        }
+    }
+
+    /**
+     * TODO: Temporary for backwards compatibility. Remove.
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public @Nullable byte[] getData(long configKey) {
+        try {
+            return getReports(configKey);
+        } catch (StatsUnavailableException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Clients can request metadata for statsd. Will contain stats across all configurations but not
+     * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
+     * This getter is not destructive and will not reset any metrics/counters.
+     *
+     * @return Serialized StatsdStatsReport proto.
+     * @throws StatsUnavailableException if unsuccessful due to failing to connect to stats service
+     */
+    @RequiresPermission(Manifest.permission.DUMP)
+    public byte[] getStatsMetadata() throws StatsUnavailableException {
+        synchronized (this) {
+            try {
+                IStatsManager service = getIStatsManagerLocked();
+                return service.getMetadata();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
+                throw new StatsUnavailableException("could not connect", e);
             }
         }
     }
 
     /**
      * Clients can request metadata for statsd. Will contain stats across all configurations but not
-     * the actual metrics themselves (metrics must be collected via {@link #getData(String)}.
+     * the actual metrics themselves (metrics must be collected via {@link #getReports(long)}.
      * This getter is not destructive and will not reset any metrics/counters.
      *
      * @return Serialized StatsdStatsReport proto. Returns null on failure (eg, if statsd crashed).
      */
     @RequiresPermission(Manifest.permission.DUMP)
     public @Nullable byte[] getMetadata() {
-        synchronized (this) {
-            try {
-                IStatsManager service = getIStatsManagerLocked();
-                if (service == null) {
-                    Slog.e(TAG, "Failed to find statsd when getting metadata");
-                    return null;
-                }
-                return service.getMetadata();
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to connect to statsd when getting metadata");
-                return null;
-            }
+        try {
+            return getStatsMetadata();
+        } catch (StatsUnavailableException e) {
+            return null;
         }
     }
 
@@ -279,14 +341,33 @@
         }
     }
 
-    private IStatsManager getIStatsManagerLocked() throws RemoteException {
+    private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
         if (mService != null) {
             return mService;
         }
         mService = IStatsManager.Stub.asInterface(ServiceManager.getService("stats"));
-        if (mService != null) {
+        if (mService == null) {
+            throw new StatsUnavailableException("could not be found");
+        }
+        try {
             mService.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
+        } catch (RemoteException e) {
+            throw new StatsUnavailableException("could not connect when linkToDeath", e);
         }
         return mService;
     }
+
+    /**
+     * Exception thrown when communication with the stats service fails (eg if it is not available).
+     * This might be thrown early during boot before the stats service has started or if it crashed.
+     */
+    public static class StatsUnavailableException extends AndroidException {
+        public StatsUnavailableException(String reason) {
+            super("Failed to connect to statsd: " + reason);
+        }
+
+        public StatsUnavailableException(String reason, Throwable e) {
+            super("Failed to connect to statsd: " + reason, e);
+        }
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 990147b..c491dcc 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1169,6 +1169,7 @@
      * Constant to indicate the feature of mandatory backups. Used as argument to
      * {@link #createAdminSupportIntent(String)}.
      * @see #setMandatoryBackupTransport(ComponentName, ComponentName)
+     * @hide
      */
     public static final String POLICY_MANDATORY_BACKUPS = "policy_mandatory_backups";
 
@@ -6843,8 +6844,7 @@
      * @param restriction Indicates for which feature the dialog should be displayed. Can be a
      *            user restriction from {@link UserManager}, e.g.
      *            {@link UserManager#DISALLOW_ADJUST_VOLUME}, or one of the constants
-     *            {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE} or
-     *            {@link #POLICY_MANDATORY_BACKUPS}.
+     *            {@link #POLICY_DISABLE_CAMERA}, {@link #POLICY_DISABLE_SCREEN_CAPTURE}.
      * @return Intent An intent to be used to start the dialog-activity if the restriction is
      *            set by an admin, or null if the restriction does not exist or no admin set it.
      */
@@ -8791,13 +8791,6 @@
      *
      * <p> Backup service is off by default when device owner is present.
      *
-     * <p> If backups are made mandatory by specifying a non-null mandatory backup transport using
-     * the {@link DevicePolicyManager#setMandatoryBackupTransport} method, the backup service is
-     * automatically enabled.
-     *
-     * <p> If the backup service is disabled using this method after the mandatory backup transport
-     * has been set, the mandatory backup transport is cleared.
-     *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param enabled {@code true} to enable the backup service, {@code false} to disable it.
      * @throws SecurityException if {@code admin} is not a device owner.
@@ -8835,6 +8828,8 @@
      * <p>Only device owner can call this method.
      * <p>If backups were disabled and a non-null backup transport {@link ComponentName} is
      * specified, backups will be enabled.
+     * <p> If the backup service is disabled after the mandatory backup transport has been set, the
+     * mandatory backup transport is cleared.
      *
      * <p>NOTE: The method shouldn't be called on the main thread.
      *
@@ -8842,6 +8837,7 @@
      * @param backupTransportComponent The backup transport layer to be used for mandatory backups.
      * @return {@code true} if the backup transport was successfully set; {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not a device owner.
+     * @hide
      */
     @WorkerThread
     public boolean setMandatoryBackupTransport(
@@ -8861,6 +8857,7 @@
      *
      * @return a {@link ComponentName} of the backup transport layer to be used if backups are
      *         mandatory or {@code null} if backups are not mandatory.
+     * @hide
      */
     public ComponentName getMandatoryBackupTransport() {
         throwIfParentInstance("getMandatoryBackupTransport");
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index a2aaf12..69852f3 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,11 +25,15 @@
     void unpinSlice(String pkg, in Uri uri, in IBinder token);
     boolean hasSliceAccess(String pkg);
     SliceSpec[] getPinnedSpecs(in Uri uri, String pkg);
-    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
-            in String[] autoGrantPermissions);
-    void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
     Uri[] getPinnedSlices(String pkg);
 
     byte[] getBackupPayload(int user);
     void applyRestore(in byte[] payload, int user);
+
+    // Perms.
+    void grantSlicePermission(String callingPkg, String toPkg, in Uri uri);
+    void revokeSlicePermission(String callingPkg, String toPkg, in Uri uri);
+    int checkSlicePermission(in Uri uri, String pkg, int pid, int uid,
+            in String[] autoGrantPermissions);
+    void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices);
 }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index bf3398a..4336f18 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -21,19 +21,13 @@
 import android.annotation.StringDef;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentProvider;
-import android.content.Intent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -575,45 +569,4 @@
         }
         return sb.toString();
     }
-
-    /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public static @Nullable Slice bindSlice(ContentResolver resolver,
-            @NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
-        Preconditions.checkNotNull(uri, "uri");
-        IContentProvider provider = resolver.acquireProvider(uri);
-        if (provider == null) {
-            throw new IllegalArgumentException("Unknown URI " + uri);
-        }
-        try {
-            Bundle extras = new Bundle();
-            extras.putParcelable(SliceProvider.EXTRA_BIND_URI, uri);
-            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
-                    new ArrayList<>(supportedSpecs));
-            final Bundle res = provider.call(resolver.getPackageName(), SliceProvider.METHOD_SLICE,
-                    null, extras);
-            Bundle.setDefusable(res, true);
-            if (res == null) {
-                return null;
-            }
-            return res.getParcelable(SliceProvider.EXTRA_SLICE);
-        } catch (RemoteException e) {
-            // Arbitrary and not worth documenting, as Activity
-            // Manager will kill this process shortly anyway.
-            return null;
-        } finally {
-            resolver.releaseProvider(provider);
-        }
-    }
-
-    /**
-     * @deprecated TO BE REMOVED.
-     */
-    @Deprecated
-    public static @Nullable Slice bindSlice(Context context, @NonNull Intent intent,
-            @NonNull List<SliceSpec> supportedSpecs) {
-        return context.getSystemService(SliceManager.class).bindSlice(intent, supportedSpecs);
-    }
 }
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 0285e9f..ad49437 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -16,6 +16,8 @@
 
 package android.app.slice;
 
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SdkConstant;
@@ -38,6 +40,7 @@
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.os.UserHandle;
+import android.util.ArraySet;
 import android.util.Log;
 
 import com.android.internal.util.Preconditions;
@@ -47,6 +50,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Class to handle interactions with {@link Slice}s.
@@ -101,22 +105,6 @@
     private final IBinder mToken = new Binder();
 
     /**
-     * Permission denied.
-     * @hide
-     */
-    public static final int PERMISSION_DENIED = -1;
-    /**
-     * Permission granted.
-     * @hide
-     */
-    public static final int PERMISSION_GRANTED = 0;
-    /**
-     * Permission just granted by the user, and should be granted uri permission as well.
-     * @hide
-     */
-    public static final int PERMISSION_USER_GRANTED = 1;
-
-    /**
      * @hide
      */
     public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
@@ -140,7 +128,7 @@
      * @see Intent#ACTION_ASSIST
      * @see Intent#CATEGORY_HOME
      */
-    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+    public void pinSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> specs) {
         try {
             mService.pinSlice(mContext.getPackageName(), uri,
                     specs.toArray(new SliceSpec[specs.size()]), mToken);
@@ -150,6 +138,14 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
+        pinSlice(uri, new ArraySet<>(specs));
+    }
+
+    /**
      * Remove a pin for a slice.
      * <p>
      * If the slice has no other pins/callbacks then the slice will be unpinned.
@@ -189,9 +185,10 @@
      * into account all clients and returns only specs supported by all.
      * @see SliceSpec
      */
-    public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
+    public @NonNull Set<SliceSpec> getPinnedSpecs(Uri uri) {
         try {
-            return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
+            return new ArraySet<>(Arrays.asList(mService.getPinnedSpecs(uri,
+                    mContext.getPackageName())));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -240,7 +237,7 @@
      * @return The Slice provided by the app or null if none is given.
      * @see Slice
      */
-    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull Set<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(uri, "uri");
         ContentResolver resolver = mContext.getContentResolver();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -265,6 +262,14 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
+    public @Nullable Slice bindSlice(@NonNull Uri uri, @NonNull List<SliceSpec> supportedSpecs) {
+        return bindSlice(uri, new ArraySet<>(supportedSpecs));
+    }
+
+    /**
      * Turns a slice intent into a slice uri. Expects an explicit intent.
      * <p>
      * This goes through a several stage resolution process to determine if any slice
@@ -272,12 +277,12 @@
      * <ol>
      *  <li> If the intent contains data that {@link ContentResolver#getType} is
      *  {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li>
-     *  <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
-     *  the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
-     *  will be returned.</li>
-     *  <li>Lastly, if the intent explicitly points at an activity, and that activity has
+     *  <li>If the intent explicitly points at an activity, and that activity has
      *  meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be
      *  returned.</li>
+     *  <li>Lastly, if the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
+     *  the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result
+     *  will be returned.</li>
      *  <li>If no slice is found, then {@code null} is returned.</li>
      * </ol>
      * @param intent The intent associated with a slice.
@@ -287,37 +292,12 @@
      * @see Intent
      */
     public @Nullable Uri mapIntentToUri(@NonNull Intent intent) {
-        Preconditions.checkNotNull(intent, "intent");
-        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
-                || intent.getData() != null,
-                "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
-
-        // Check if the intent has data for the slice uri on it and use that
-        final Uri intentData = intent.getData();
-        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return intentData;
-        }
+        final Uri staticUri = resolveStatic(intent, resolver);
+        if (staticUri != null) return staticUri;
         // Otherwise ask the app
-        Intent queryIntent = new Intent(intent);
-        if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
-            queryIntent.addCategory(CATEGORY_SLICE);
-        }
-        List<ResolveInfo> providers =
-                mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
-        if (providers == null || providers.isEmpty()) {
-            // There are no providers, see if this activity has a direct link.
-            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
-                    PackageManager.GET_META_DATA);
-            if (resolve != null && resolve.activityInfo != null
-                    && resolve.activityInfo.metaData != null
-                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
-                return Uri.parse(
-                        resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
-            }
-            return null;
-        }
-        String authority = providers.get(0).providerInfo.authority;
+        String authority = getAuthority(intent);
+        if (authority == null) return null;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -338,10 +318,43 @@
         }
     }
 
+    private String getAuthority(Intent intent) {
+        Intent queryIntent = new Intent(intent);
+        if (!queryIntent.hasCategory(CATEGORY_SLICE)) {
+            queryIntent.addCategory(CATEGORY_SLICE);
+        }
+        List<ResolveInfo> providers =
+                mContext.getPackageManager().queryIntentContentProviders(queryIntent, 0);
+        return providers != null && !providers.isEmpty() ? providers.get(0).providerInfo.authority
+                : null;
+    }
+
+    private Uri resolveStatic(@NonNull Intent intent, ContentResolver resolver) {
+        Preconditions.checkNotNull(intent, "intent");
+        Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
+                || intent.getData() != null,
+                "Slice intent must be explicit %s", intent);
+
+        // Check if the intent has data for the slice uri on it and use that
+        final Uri intentData = intent.getData();
+        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
+            return intentData;
+        }
+        // There are no providers, see if this activity has a direct link.
+        ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
+                PackageManager.GET_META_DATA);
+        if (resolve != null && resolve.activityInfo != null
+                && resolve.activityInfo.metaData != null
+                && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
+            return Uri.parse(
+                    resolve.activityInfo.metaData.getString(SLICE_METADATA_KEY));
+        }
+        return null;
+    }
+
     /**
-     * Turns a slice intent into slice content. Expects an explicit intent. If there is no
-     * {@link android.content.ContentProvider} associated with the given intent this will throw
-     * {@link IllegalArgumentException}.
+     * Turns a slice intent into slice content. Is a shortcut to perform the action
+     * of both {@link #mapIntentToUri(Intent)} and {@link #bindSlice(Uri, List)} at once.
      *
      * @param intent The intent associated with a slice.
      * @param supportedSpecs List of supported specs.
@@ -351,34 +364,17 @@
      * @see Intent
      */
     public @Nullable Slice bindSlice(@NonNull Intent intent,
-            @NonNull List<SliceSpec> supportedSpecs) {
+            @NonNull Set<SliceSpec> supportedSpecs) {
         Preconditions.checkNotNull(intent, "intent");
         Preconditions.checkArgument(intent.getComponent() != null || intent.getPackage() != null
                 || intent.getData() != null,
                 "Slice intent must be explicit %s", intent);
         ContentResolver resolver = mContext.getContentResolver();
-
-        // Check if the intent has data for the slice uri on it and use that
-        final Uri intentData = intent.getData();
-        if (intentData != null && SliceProvider.SLICE_TYPE.equals(resolver.getType(intentData))) {
-            return bindSlice(intentData, supportedSpecs);
-        }
+        final Uri staticUri = resolveStatic(intent, resolver);
+        if (staticUri != null) return bindSlice(staticUri, supportedSpecs);
         // Otherwise ask the app
-        List<ResolveInfo> providers =
-                mContext.getPackageManager().queryIntentContentProviders(intent, 0);
-        if (providers == null || providers.isEmpty()) {
-            // There are no providers, see if this activity has a direct link.
-            ResolveInfo resolve = mContext.getPackageManager().resolveActivity(intent,
-                    PackageManager.GET_META_DATA);
-            if (resolve != null && resolve.activityInfo != null
-                    && resolve.activityInfo.metaData != null
-                    && resolve.activityInfo.metaData.containsKey(SLICE_METADATA_KEY)) {
-                return bindSlice(Uri.parse(resolve.activityInfo.metaData
-                        .getString(SLICE_METADATA_KEY)), supportedSpecs);
-            }
-            return null;
-        }
-        String authority = providers.get(0).providerInfo.authority;
+        String authority = getAuthority(intent);
+        if (authority == null) return null;
         Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
                 .authority(authority).build();
         try (ContentProviderClient provider = resolver.acquireContentProviderClient(uri)) {
@@ -387,8 +383,6 @@
             }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
-            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
-                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
@@ -402,6 +396,16 @@
     }
 
     /**
+     * @deprecated TO BE REMOVED.
+     */
+    @Deprecated
+    @Nullable
+    public Slice bindSlice(@NonNull Intent intent,
+            @NonNull List<SliceSpec> supportedSpecs) {
+        return bindSlice(intent, new ArraySet<>(supportedSpecs));
+    }
+
+    /**
      * Determine whether a particular process and user ID has been granted
      * permission to access a specific slice URI.
      *
@@ -417,9 +421,11 @@
      * @see #grantSlicePermission(String, Uri)
      */
     public @PermissionResult int checkSlicePermission(@NonNull Uri uri, int pid, int uid) {
-        // TODO: Switch off Uri permissions.
-        return mContext.checkUriPermission(uri, pid, uid,
-                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        try {
+            return mService.checkSlicePermission(uri, null, pid, uid, null);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -431,11 +437,11 @@
      * @see #revokeSlicePermission
      */
     public void grantSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
-        // TODO: Switch off Uri permissions.
-        mContext.grantUriPermission(toPackage, uri,
-                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        try {
+            mService.grantSlicePermission(mContext.getPackageName(), toPackage, uri);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -453,11 +459,11 @@
      * @see #grantSlicePermission
      */
     public void revokeSlicePermission(@NonNull String toPackage, @NonNull Uri uri) {
-        // TODO: Switch off Uri permissions.
-        mContext.revokeUriPermission(toPackage, uri,
-                Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                        | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        try {
+            mService.revokeSlicePermission(mContext.getPackageName(), toPackage, uri);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
@@ -478,16 +484,6 @@
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
             }
-            if (result == PERMISSION_USER_GRANTED) {
-                // We just had a user grant of this permission and need to grant this to the app
-                // permanently.
-                mContext.grantUriPermission(pkg, uri.buildUpon().path("").build(),
-                        Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
-                                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
-                                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
-                // Notify a change has happened because we just granted a permission.
-                mContext.getContentResolver().notifyChange(uri, null);
-            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index fe5742d..d369272 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -44,6 +44,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * A SliceProvider allows an app to provide content to be displayed in system spaces. This content
@@ -197,6 +198,14 @@
      * @see {@link Slice}.
      * @see {@link Slice#HINT_PARTIAL}
      */
+    public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
+        return onBindSlice(sliceUri, new ArrayList<>(supportedSpecs));
+    }
+
+    /**
+     * @deprecated TO BE REMOVED
+     */
+    @Deprecated
     public Slice onBindSlice(Uri sliceUri, List<SliceSpec> supportedSpecs) {
         return null;
     }
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index 7252f02..216a4a0 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -237,20 +237,26 @@
                 DEFAULT_NETWORK_YES
         })
         @Retention(RetentionPolicy.SOURCE)
-        public @interface DefaultNetwork {}
+        public @interface DefaultNetworkStatus {}
 
         /**
-         * Combined usage for this network regardless of whether it was the active default network.
+         * Combined usage for this network regardless of default network status.
          */
         public static final int DEFAULT_NETWORK_ALL = -1;
 
         /**
-         * Usage that occurs while this network is not the active default network.
+         * Usage that occurs while this network is not a default network.
+         *
+         * <p>This implies that the app responsible for this usage requested that it occur on a
+         * specific network different from the one(s) the system would have selected for it.
          */
         public static final int DEFAULT_NETWORK_NO = 0x1;
 
         /**
-         * Usage that occurs while this network is the active default network.
+         * Usage that occurs while this network is a default network.
+         *
+         * <p>This implies that the app either did not select a specific network for this usage,
+         * or it selected a network that the system could have selected for app traffic.
          */
         public static final int DEFAULT_NETWORK_YES = 0x2;
 
@@ -262,7 +268,7 @@
         private int mUid;
         private int mTag;
         private int mState;
-        private int mDefaultNetwork;
+        private int mDefaultNetworkStatus;
         private int mMetered;
         private int mRoaming;
         private long mBeginTimeStamp;
@@ -323,8 +329,9 @@
             return 0;
         }
 
-        private static @DefaultNetwork int convertDefaultNetwork(int defaultNetwork) {
-            switch (defaultNetwork) {
+        private static @DefaultNetworkStatus int convertDefaultNetworkStatus(
+                int defaultNetworkStatus) {
+            switch (defaultNetworkStatus) {
                 case android.net.NetworkStats.DEFAULT_NETWORK_ALL : return DEFAULT_NETWORK_ALL;
                 case android.net.NetworkStats.DEFAULT_NETWORK_NO: return DEFAULT_NETWORK_NO;
                 case android.net.NetworkStats.DEFAULT_NETWORK_YES: return DEFAULT_NETWORK_YES;
@@ -397,18 +404,15 @@
         }
 
         /**
-         * Default network state. One of the following values:<p/>
+         * Default network status. One of the following values:<p/>
          * <ul>
          * <li>{@link #DEFAULT_NETWORK_ALL}</li>
          * <li>{@link #DEFAULT_NETWORK_NO}</li>
          * <li>{@link #DEFAULT_NETWORK_YES}</li>
          * </ul>
-         * <p>Indicates whether the network usage occurred on the system default network for this
-         * type of traffic, or whether the application chose to send this traffic on a network that
-         * was not the one selected by the system.
          */
-        public @DefaultNetwork int getDefaultNetwork() {
-            return mDefaultNetwork;
+        public @DefaultNetworkStatus int getDefaultNetworkStatus() {
+            return mDefaultNetworkStatus;
         }
 
         /**
@@ -605,7 +609,7 @@
         bucketOut.mUid = Bucket.convertUid(mRecycledSummaryEntry.uid);
         bucketOut.mTag = Bucket.convertTag(mRecycledSummaryEntry.tag);
         bucketOut.mState = Bucket.convertState(mRecycledSummaryEntry.set);
-        bucketOut.mDefaultNetwork = Bucket.convertDefaultNetwork(
+        bucketOut.mDefaultNetworkStatus = Bucket.convertDefaultNetworkStatus(
                 mRecycledSummaryEntry.defaultNetwork);
         bucketOut.mMetered = Bucket.convertMetered(mRecycledSummaryEntry.metered);
         bucketOut.mRoaming = Bucket.convertRoaming(mRecycledSummaryEntry.roaming);
@@ -657,7 +661,7 @@
                 bucketOut.mUid = Bucket.convertUid(getUid());
                 bucketOut.mTag = Bucket.convertTag(mTag);
                 bucketOut.mState = mState;
-                bucketOut.mDefaultNetwork = Bucket.DEFAULT_NETWORK_ALL;
+                bucketOut.mDefaultNetworkStatus = Bucket.DEFAULT_NETWORK_ALL;
                 bucketOut.mMetered = Bucket.METERED_ALL;
                 bucketOut.mRoaming = Bucket.ROAMING_ALL;
                 bucketOut.mBeginTimeStamp = mRecycledHistoryEntry.bucketStart;
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 85f4efc..0b21196 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -35,6 +35,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.DataUnit;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -95,6 +96,15 @@
     /** @hide */
     public static final int CALLBACK_RELEASED = 1;
 
+    /**
+     * Minimum data usage threshold for registering usage callbacks.
+     *
+     * Requests registered with a threshold lower than this will only be triggered once this minimum
+     * is reached.
+     * @hide
+     */
+    public static final long MIN_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(2);
+
     private final Context mContext;
     private final INetworkStatsService mService;
 
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index 84f57a3..503ca6c 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -111,7 +111,7 @@
 
         /**
          * An event type denoting a change in App Standby Bucket. The new bucket can be
-         * retrieved by calling {@link #getStandbyBucket()}.
+         * retrieved by calling {@link #getAppStandbyBucket()}.
          *
          * @see UsageStatsManager#getAppStandbyBucket()
          */
@@ -326,13 +326,23 @@
          * Returns the standby bucket of the app, if the event is of type
          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
          * @return the standby bucket associated with the event.
-         *
+         * @hide
          */
         public int getStandbyBucket() {
             return (mBucketAndReason & 0xFFFF0000) >>> 16;
         }
 
         /**
+         * Returns the standby bucket of the app, if the event is of type
+         * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0.
+         * @return the standby bucket associated with the event.
+         *
+         */
+        public int getAppStandbyBucket() {
+            return (mBucketAndReason & 0xFFFF0000) >>> 16;
+        }
+
+        /**
          * Returns the reason for the bucketing, if the event is of type
          * {@link #STANDBY_BUCKET_CHANGED}, otherwise returns 0. Reason values include
          * the main reason which is one of REASON_MAIN_*, OR'ed with REASON_SUB_*, if there
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 01ee671..f608fcb 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -40,6 +40,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.ResultReceiver;
 import android.os.ShellCommand;
@@ -1814,8 +1815,12 @@
     public static final String EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME";
 
     /**
-     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent with
-     * {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     * Intent extra: A {@link Bundle} of extras for a package being suspended. Will be sent as an
+     * extra with {@link #ACTION_MY_PACKAGE_SUSPENDED}.
+     *
+     * <p>The contents of this {@link Bundle} are a contract between the suspended app and the
+     * suspending app, i.e. any app with the permission {@code android.permission.SUSPEND_APPS}.
+     * This is meant to enable the suspended app to better handle the state of being suspended.
      *
      * @see #ACTION_MY_PACKAGE_SUSPENDED
      * @see #ACTION_MY_PACKAGE_UNSUSPENDED
@@ -2284,6 +2289,10 @@
     /**
      * Activity Action: Started to show more details about why an application was suspended.
      *
+     * <p>Whenever the system detects an activity launch for a suspended app, it shows a dialog to
+     * the user to inform them of the state and present them an affordance to start this activity
+     * action to show more details about the reason for suspension.
+     *
      * <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}.
@@ -2293,6 +2302,8 @@
      * <p class="note">This is a protected intent that can only be sent
      * by the system.
      *
+     * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
+     * PersistableBundle, String)
      * @see PackageManager#isPackageSuspended()
      * @see #ACTION_PACKAGES_SUSPENDED
      *
diff --git a/core/java/android/content/QuickViewConstants.java b/core/java/android/content/QuickViewConstants.java
index a25513d..132d43f 100644
--- a/core/java/android/content/QuickViewConstants.java
+++ b/core/java/android/content/QuickViewConstants.java
@@ -45,8 +45,8 @@
      * Feature to delete an individual document. Quick viewer implementations must use
      * Storage Access Framework to both verify delete permission and to delete content.
      *
-     * @see DocumentsContract#Document#FLAG_SUPPORTS_DELETE
-     * @see DocumentsContract#deleteDocument(ContentResolver resolver, Uri documentUri)
+     * @see DocumentsContract.Document#FLAG_SUPPORTS_DELETE
+     * @see DocumentsContract#deleteDocument(ContentResolver, Uri)
      */
     public static final String FEATURE_DELETE = "android:delete";
 
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 49b1efd..c5a39f4 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -26,6 +26,7 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1166,7 +1167,8 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface HiddenApiEnforcementPolicy {}
 
-    private boolean isValidHiddenApiEnforcementPolicy(int policy) {
+    /** @hide */
+    public static boolean isValidHiddenApiEnforcementPolicy(int policy) {
         return policy >= HIDDEN_API_ENFORCEMENT_DEFAULT && policy <= HIDDEN_API_ENFORCEMENT_MAX;
     }
 
@@ -1686,13 +1688,17 @@
      * @hide
      */
     public @HiddenApiEnforcementPolicy int getHiddenApiEnforcementPolicy() {
-        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
-            return mHiddenApiPolicy;
-        }
         if (isAllowedToUseHiddenApis()) {
             return HIDDEN_API_ENFORCEMENT_NONE;
         }
-        return HIDDEN_API_ENFORCEMENT_BLACK;
+        if (mHiddenApiPolicy != HIDDEN_API_ENFORCEMENT_DEFAULT) {
+            return mHiddenApiPolicy;
+        }
+        if (targetSdkVersion <= Build.VERSION_CODES.O_MR1) {
+            return HIDDEN_API_ENFORCEMENT_BLACK;
+        } else {
+            return HIDDEN_API_ENFORCEMENT_DARK_GREY_AND_BLACK;
+        }
     }
 
     /**
@@ -1706,6 +1712,31 @@
     }
 
     /**
+     * Updates the hidden API enforcement policy for this app from the given values, if appropriate.
+     *
+     * This will have no effect if this app is not subject to hidden API enforcement, i.e. if it
+     * is on the package whitelist.
+     *
+     * @param policyPreP configured policy for pre-P apps, or {@link
+     *        #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+     * @param policyP configured policy for apps targeting P or later, or {@link
+     *        #HIDDEN_API_ENFORCEMENT_DEFAULT} if nothing configured.
+     * @hide
+     */
+    public void maybeUpdateHiddenApiEnforcementPolicy(
+            @HiddenApiEnforcementPolicy int policyPreP, @HiddenApiEnforcementPolicy int policyP) {
+        if (isPackageWhitelistedForHiddenApis()) {
+            return;
+        }
+        if (targetSdkVersion <= Build.VERSION_CODES.O_MR1) {
+            setHiddenApiEnforcementPolicy(policyPreP);
+        } else if (targetSdkVersion > Build.VERSION_CODES.O_MR1) {
+            setHiddenApiEnforcementPolicy(policyP);
+        }
+
+    }
+
+    /**
      * @hide
      */
     public void setVersionCode(long newVersionCode) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 02ce47b8..2be33e9 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -280,9 +280,6 @@
 
     PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
 
-    void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
-            int userId);
-
     /**
      * Backup/restore support - only the system uid may use these.
      */
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 9aace2e..8223363 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -212,7 +212,7 @@
          * an applicaton.
          *
          * <p>Note: On devices running {@link android.os.Build.VERSION_CODES#P Android P} or higher,
-         * any apps that override {@link #onPackagesSuspended(String[], Bundle, UserHandle)} will
+         * any apps that override {@link #onPackagesSuspended(String[], UserHandle, Bundle)} will
          * not receive this callback.
          *
          * @param packageNames The names of the packages that have just been
@@ -226,15 +226,20 @@
          * Indicates that one or more packages have been suspended. A device administrator or an app
          * with {@code android.permission.SUSPEND_APPS} can do this.
          *
-         * @param packageNames The names of the packages that have just been suspended.
-         * @param launcherExtras A {@link Bundle} of extras for the launcher.
-         * @param user the user for which the given packages were suspended.
+         * <p>A suspending app with the permission {@code android.permission.SUSPEND_APPS} can
+         * optionally provide a {@link Bundle} of extra information that it deems helpful for the
+         * launcher to handle the suspended state of these packages. The contents of this
+         * {@link Bundle} supposed to be a contract between the suspending app and the launcher.
          *
+         * @param packageNames The names of the packages that have just been suspended.
+         * @param user the user for which the given packages were suspended.
+         * @param launcherExtras A {@link Bundle} of extras for the launcher, if provided to the
+         *                      system, {@code null} otherwise.
          * @see PackageManager#isPackageSuspended()
          * @see #getSuspendedPackageLauncherExtras(String, UserHandle)
          */
-        public void onPackagesSuspended(String[] packageNames, @Nullable Bundle launcherExtras,
-                UserHandle user) {
+        public void onPackagesSuspended(String[] packageNames, UserHandle user,
+                @Nullable Bundle launcherExtras) {
             onPackagesSuspended(packageNames, user);
         }
 
@@ -662,6 +667,9 @@
      * {@code PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
      * PersistableBundle, String)}.
      *
+     * <p>The contents of this {@link Bundle} are supposed to be a contract between the suspending
+     * app and the launcher.
+     *
      * <p>Note: This just returns whatever extras were provided to the system, <em>which might
      * even be {@code null}.</em>
      *
@@ -670,7 +678,7 @@
      * @return A {@link Bundle} of launcher extras. Or {@code null} if the package is not currently
      *         suspended.
      *
-     * @see Callback#onPackagesSuspended(String[], Bundle, UserHandle)
+     * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle)
      * @see PackageManager#isPackageSuspended()
      */
     public @Nullable Bundle getSuspendedPackageLauncherExtras(String packageName, UserHandle user) {
@@ -1298,8 +1306,8 @@
                     mCallback.onPackagesUnavailable(info.packageNames, info.user, info.replacing);
                     break;
                 case MSG_SUSPENDED:
-                    mCallback.onPackagesSuspended(info.packageNames, info.launcherExtras,
-                            info.user);
+                    mCallback.onPackagesSuspended(info.packageNames, info.user, info.launcherExtras
+                    );
                     break;
                 case MSG_UNSUSPENDED:
                     mCallback.onPackagesUnsuspended(info.packageNames, info.user);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 627ceb7..5f9f8f1 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -244,7 +244,7 @@
      * the first position to be the same across updates.
      *
      * <strong>Deprecated</strong> This has been replaced by the
-     * {@link PackageInfo#signingCertificateHistory} field, which takes into
+     * {@link PackageInfo#signingInfo} field, which takes into
      * account signing certificate rotation.  For backwards compatibility in
      * the event of signing certificate rotation, this will return the oldest
      * reported signing certificate, so that an application will appear to
@@ -256,29 +256,15 @@
     public Signature[] signatures;
 
     /**
-     * Array of all signatures arrays read from the package file, potentially
+     * Signing information read from the package file, potentially
      * including past signing certificates no longer used after signing
-     * certificate rotation.  Though signing certificate rotation is only
-     * available for apps with a single signing certificate, this provides an
-     * array of arrays so that packages signed with multiple signing
-     * certificates can still return all signers.  This is only filled in if
+     * certificate rotation.  This is only filled in if
      * the flag {@link PackageManager#GET_SIGNING_CERTIFICATES} was set.
      *
-     * A package must be singed with at least one certificate, which is at
-     * position zero in the array.  An application may be signed by multiple
-     * certificates, which would be in the array at position zero in an
-     * indeterminate order.  A package may also have a history of certificates
-     * due to signing certificate rotation.  In this case, the array will be
-     * populated by a series of single-entry arrays corresponding to a signing
-     * certificate of the package.
-     *
-     * <strong>Note:</strong> Signature ordering is not guaranteed to be
-     * stable which means that a package signed with certificates A and B is
-     * equivalent to being signed with certificates B and A. This means that
-     * in case multiple signatures are reported you cannot assume the one at
-     * the first position will be the same across updates.
+     * Use this field instead of the deprecated {@code signatures} field.
+     * See {@link SigningInfo} for more information on its contents.
      */
-    public Signature[][] signingCertificateHistory;
+    public SigningInfo signingInfo;
 
     /**
      * Application specified preferred configuration
@@ -476,17 +462,11 @@
         dest.writeBoolean(mOverlayIsStatic);
         dest.writeInt(compileSdkVersion);
         dest.writeString(compileSdkVersionCodename);
-        writeSigningCertificateHistoryToParcel(dest, parcelableFlags);
-    }
-
-    private void writeSigningCertificateHistoryToParcel(Parcel dest, int parcelableFlags) {
-        if (signingCertificateHistory != null) {
-            dest.writeInt(signingCertificateHistory.length);
-            for (int i = 0; i < signingCertificateHistory.length; i++) {
-                dest.writeTypedArray(signingCertificateHistory[i], parcelableFlags);
-            }
+        if (signingInfo != null) {
+            dest.writeInt(1);
+            signingInfo.writeToParcel(dest, parcelableFlags);
         } else {
-            dest.writeInt(-1);
+            dest.writeInt(0);
         }
     }
 
@@ -544,7 +524,10 @@
         mOverlayIsStatic = source.readBoolean();
         compileSdkVersion = source.readInt();
         compileSdkVersionCodename = source.readString();
-        readSigningCertificateHistoryFromParcel(source);
+        int hasSigningInfo = source.readInt();
+        if (hasSigningInfo != 0) {
+            signingInfo = SigningInfo.CREATOR.createFromParcel(source);
+        }
 
         // The component lists were flattened with the redundant ApplicationInfo
         // instances omitted.  Distribute the canonical one here as appropriate.
@@ -556,16 +539,6 @@
         }
     }
 
-    private void readSigningCertificateHistoryFromParcel(Parcel source) {
-        int len = source.readInt();
-        if (len != -1) {
-            signingCertificateHistory = new Signature[len][];
-            for (int i = 0; i < len; i++) {
-                signingCertificateHistory[i] = source.createTypedArray(Signature.CREATOR);
-            }
-        }
-    }
-
     private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
         if (components != null) {
             for (ComponentInfo ci : components) {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index d734aa3..c68f253 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5577,7 +5577,8 @@
      * @param packageName The name of the package to get the suspended status of.
      * @param userId The user id.
      * @return {@code true} if the package is suspended or {@code false} if the package is not
-     * suspended or could not be found.
+     * suspended.
+     * @throws IllegalArgumentException if the package was not found.
      * @hide
      */
     public abstract boolean isPackageSuspendedForUser(String packageName, int userId);
@@ -5586,12 +5587,13 @@
      * Query if an app is currently suspended.
      *
      * @return {@code true} if the given package is suspended, {@code false} otherwise
+     * @throws NameNotFoundException if the package could not be found.
      *
      * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
      * @hide
      */
     @SystemApi
-    public boolean isPackageSuspended(String packageName) {
+    public boolean isPackageSuspended(String packageName) throws NameNotFoundException {
         throw new UnsupportedOperationException("isPackageSuspended not implemented");
     }
 
@@ -5622,51 +5624,16 @@
     }
 
     /**
-     * Retrieve the {@link PersistableBundle} that was passed as {@code appExtras} when the given
-     * package was suspended.
+     * Returns a {@link Bundle} of extras that was meant to be sent to the calling app when it was
+     * suspended. An app with the permission {@code android.permission.SUSPEND_APPS} can supply this
+     * to the system at the time of suspending an app.
      *
-     * <p> The caller must hold permission {@link Manifest.permission#SUSPEND_APPS} to use this
-     * api.</p>
+     * <p>This is the same {@link Bundle} that is sent along with the broadcast
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED}, whenever the app is suspended. The contents of
+     * this {@link Bundle} are a contract between the suspended app and the suspending app.
      *
-     * @param packageName The package to retrieve extras for.
-     * @return The {@code appExtras} for the suspended package.
-     *
-     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
-        throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
-    }
-
-    /**
-     * Set the app extras for a suspended package. This method can be used to update the appExtras
-     * for a package that was earlier suspended using
-     * {@link #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
-     * String)}
-     * Does nothing if the given package is not already in a suspended state.
-     *
-     * @param packageName The package for which the appExtras need to be updated
-     * @param appExtras The new appExtras for the given package
-     *
-     * @see #setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle, String)
-     *
-     * @hide
-     */
-    @SystemApi
-    @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public void setSuspendedPackageAppExtras(String packageName,
-            @Nullable PersistableBundle appExtras) {
-        throw new UnsupportedOperationException("setSuspendedPackageAppExtras not implemented");
-    }
-
-    /**
-     * Returns any extra information supplied as {@code appExtras} to the system when the calling
-     * app was suspended.
-     *
-     * <p>Note: If no extras were supplied to the system, this method will return {@code null}, even
-     * when the calling app has been suspended.</p>
+     * <p>Note: These extras are optional, so if no extras were supplied to the system, this method
+     * will return {@code null}, even when the calling app has been suspended.
      *
      * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
      * package is not currently suspended.
@@ -5674,6 +5641,7 @@
      * @see #isPackageSuspended()
      * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
      * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#EXTRA_SUSPENDED_PACKAGE_EXTRAS
      */
     public @Nullable Bundle getSuspendedPackageAppExtras() {
         throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
@@ -6137,7 +6105,9 @@
      * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
      * since it takes into account the possibility of signing certificate rotation, except in the
      * case of packages that are signed by multiple certificates, for which signing certificate
-     * rotation is not supported.
+     * rotation is not supported.  This method is analogous to using {@code getPackageInfo} with
+     * {@code GET_SIGNING_CERTIFICATES} and then searching through the resulting {@code
+     * signingCertificateHistory} field to see if the desired certificate is present.
      *
      * @param packageName package whose signing certificates to check
      * @param certificate signing certificate for which to search
@@ -6151,13 +6121,19 @@
     }
 
     /**
-     * Searches the set of signing certificates by which the given uid has proven to have been
-     * signed.  This should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
+     * Searches the set of signing certificates by which the package(s) for the given uid has proven
+     * to have been signed.  For multiple packages sharing the same uid, this will return the
+     * signing certificates found in the signing history of the "newest" package, where "newest"
+     * indicates the package with the newest signing certificate in the shared uid group.  This
+     * method should be used instead of {@code getPackageInfo} with {@code GET_SIGNATURES}
      * since it takes into account the possibility of signing certificate rotation, except in the
      * case of packages that are signed by multiple certificates, for which signing certificate
-     * rotation is not supported.
+     * rotation is not supported. This method is analogous to using {@code getPackagesForUid}
+     * followed by {@code getPackageInfo} with {@code GET_SIGNING_CERTIFICATES}, selecting the
+     * {@code PackageInfo} of the newest-signed bpackage , and finally searching through the
+     * resulting {@code signingCertificateHistory} field to see if the desired certificate is there.
      *
-     * @param uid package whose signing certificates to check
+     * @param uid uid whose signing certificates to check
      * @param certificate signing certificate for which to search
      * @param type representation of the {@code certificate}
      * @return true if this package was or is signed by exactly the certificate {@code certificate}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3e0db60..7159f77 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -810,21 +810,11 @@
 
         // replacement for GET_SIGNATURES
         if ((flags & PackageManager.GET_SIGNING_CERTIFICATES) != 0) {
-            if (p.mSigningDetails.hasPastSigningCertificates()) {
-                // Package has included signing certificate rotation information.  Convert each
-                // entry to an array
-                int numberOfSigs = p.mSigningDetails.pastSigningCertificates.length;
-                pi.signingCertificateHistory = new Signature[numberOfSigs][];
-                for (int i = 0; i < numberOfSigs; i++) {
-                    pi.signingCertificateHistory[i] =
-                            new Signature[] { p.mSigningDetails.pastSigningCertificates[i] };
-                }
-            } else if (p.mSigningDetails.hasSignatures()) {
-                // otherwise keep old behavior
-                int numberOfSigs = p.mSigningDetails.signatures.length;
-                pi.signingCertificateHistory = new Signature[1][numberOfSigs];
-                System.arraycopy(p.mSigningDetails.signatures, 0,
-                        pi.signingCertificateHistory[0], 0, numberOfSigs);
+            if (p.mSigningDetails != SigningDetails.UNKNOWN) {
+                // only return a valid SigningInfo if there is signing information to report
+                pi.signingInfo = new SigningInfo(p.mSigningDetails);
+            } else {
+                pi.signingInfo = null;
             }
         }
         return pi;
diff --git a/core/java/android/content/pm/SigningInfo.java b/core/java/android/content/pm/SigningInfo.java
new file mode 100644
index 0000000..ef87403
--- /dev/null
+++ b/core/java/android/content/pm/SigningInfo.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information pertaining to the signing certificates used to sign a package.
+ */
+public final class SigningInfo implements Parcelable {
+
+    @NonNull
+    private final PackageParser.SigningDetails mSigningDetails;
+
+    public SigningInfo() {
+        mSigningDetails = PackageParser.SigningDetails.UNKNOWN;
+    }
+
+    /**
+     * @hide only packagemanager should be populating this
+     */
+    public SigningInfo(PackageParser.SigningDetails signingDetails) {
+        mSigningDetails = new PackageParser.SigningDetails(signingDetails);
+    }
+
+    public SigningInfo(SigningInfo orig) {
+        mSigningDetails = new PackageParser.SigningDetails(orig.mSigningDetails);
+    }
+
+    private SigningInfo(Parcel source) {
+        mSigningDetails = PackageParser.SigningDetails.CREATOR.createFromParcel(source);
+    }
+
+    /**
+     * Although relatively uncommon, packages may be signed by more than one signer, in which case
+     * their identity is viewed as being the set of all signers, not just any one.
+     */
+    public boolean hasMultipleSigners() {
+        return mSigningDetails.signatures != null && mSigningDetails.signatures.length > 1;
+    }
+
+    /**
+     * APK Signature Scheme v3 enables packages to provide a proof-of-rotation record that the
+     * platform verifies, and uses, to allow the use of new signing certificates.  This is only
+     * available to packages that are not signed by multiple signers.  In the event of a change to a
+     * new signing certificate, the package's past signing certificates are presented as well.  Any
+     * check of a package's signing certificate should also include a search through its entire
+     * signing history, since it could change to a new signing certificate at any time.
+     */
+    public boolean hasPastSigningCertificates() {
+        return mSigningDetails.signatures != null
+                && mSigningDetails.pastSigningCertificates != null;
+    }
+
+    /**
+     * Returns the signing certificates this package has proven it is authorized to use. This
+     * includes both the signing certificate associated with the signer of the package and the past
+     * signing certificates it included as its proof of signing certificate rotation.  This method
+     * is the preferred replacement for the {@code GET_SIGNATURES} flag used with {@link
+     * PackageManager#getPackageInfo(String, int)}.  When determining if a package is signed by a
+     * desired certificate, the returned array should be checked to determine if it is one of the
+     * entries.
+     *
+     * <note>
+     *     This method returns null if the package is signed by multiple signing certificates, as
+     *     opposed to being signed by one current signer and also providing the history of past
+     *     signing certificates.  {@link #hasMultipleSigners()} may be used to determine if this
+     *     package is signed by multiple signers.  Packages which are signed by multiple signers
+     *     cannot change their signing certificates and their {@code Signature} array should be
+     *     checked to make sure that every entry matches the looked-for signing certificates.
+     * </note>
+     */
+    public Signature[] getSigningCertificateHistory() {
+        if (hasMultipleSigners()) {
+            return null;
+        } else if (!hasPastSigningCertificates()) {
+
+            // this package is only signed by one signer with no history, return it
+            return mSigningDetails.signatures;
+        } else {
+
+            // this package has provided proof of past signing certificates, include them
+            return mSigningDetails.pastSigningCertificates;
+        }
+    }
+
+    /**
+     * Returns the signing certificates used to sign the APK contents of this application.  Not
+     * including any past signing certificates the package proved it is authorized to use.
+     * <note>
+     *     This method should not be used unless {@link #hasMultipleSigners()} returns true,
+     *     indicating that {@link #getSigningCertificateHistory()} cannot be used, otherwise {@link
+     *     #getSigningCertificateHistory()} should be preferred.
+     * </note>
+     */
+    public Signature[] getApkContentsSigners() {
+        return mSigningDetails.signatures;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        mSigningDetails.writeToParcel(dest, parcelableFlags);
+    }
+
+    public static final Parcelable.Creator<SigningInfo> CREATOR =
+            new Parcelable.Creator<SigningInfo>() {
+        @Override
+        public SigningInfo createFromParcel(Parcel source) {
+            return new SigningInfo(source);
+        }
+
+        @Override
+        public SigningInfo[] newArray(int size) {
+            return new SigningInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java
index 67e97bf..6d9ba77 100644
--- a/core/java/android/hardware/display/BrightnessConfiguration.java
+++ b/core/java/android/hardware/display/BrightnessConfiguration.java
@@ -86,7 +86,9 @@
             sb.append("(").append(mLux[i]).append(", ").append(mNits[i]).append(")");
         }
         sb.append("], '");
-        sb.append(mDescription);
+        if (mDescription != null) {
+            sb.append(mDescription);
+        }
         sb.append("'}");
         return sb.toString();
     }
@@ -96,7 +98,9 @@
         int result = 1;
         result = result * 31 + Arrays.hashCode(mLux);
         result = result * 31 + Arrays.hashCode(mNits);
-        result = result * 31 + mDescription.hashCode();
+        if (mDescription != null) {
+            result = result * 31 + mDescription.hashCode();
+        }
         return result;
     }
 
diff --git a/core/java/android/hardware/display/Curve.aidl b/core/java/android/hardware/display/Curve.aidl
new file mode 100644
index 0000000..796493e
--- /dev/null
+++ b/core/java/android/hardware/display/Curve.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+parcelable Curve;
diff --git a/core/java/android/hardware/display/Curve.java b/core/java/android/hardware/display/Curve.java
new file mode 100644
index 0000000..ac28fdd
--- /dev/null
+++ b/core/java/android/hardware/display/Curve.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public final class Curve implements Parcelable {
+    private final float[] mX;
+    private final float[] mY;
+
+    public Curve(float[] x, float[] y) {
+        mX = x;
+        mY = y;
+    }
+
+    public float[] getX() {
+        return mX;
+    }
+
+    public float[] getY() {
+        return mY;
+    }
+
+    public static final Creator<Curve> CREATOR = new Creator<Curve>() {
+        public Curve createFromParcel(Parcel in) {
+            float[] x = in.createFloatArray();
+            float[] y = in.createFloatArray();
+            return new Curve(x, y);
+        }
+
+        public Curve[] newArray(int size) {
+            return new Curve[size];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeFloatArray(mX);
+        out.writeFloatArray(mY);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index efb9517..b182fa2 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -28,6 +28,7 @@
 import android.graphics.Point;
 import android.media.projection.MediaProjection;
 import android.os.Handler;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.Surface;
@@ -748,6 +749,22 @@
     }
 
     /**
+     * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
+     * below it is rejected by the system.
+     * This prevent auto-brightness from setting the screen so dark as to prevent the user from
+     * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
+     * in that ambient brightness.
+     *
+     * @return The minimum brightness curve (as lux values and their corresponding nits values).
+     *
+     * @hide
+     */
+    @SystemApi
+    public Pair<float[], float[]> getMinimumBrightnessCurve() {
+        return mGlobal.getMinimumBrightnessCurve();
+    }
+
+    /**
      * Listens for changes in available display devices.
      */
     public interface DisplayListener {
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 2d0ef2f..d968a3e 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -31,6 +31,7 @@
 import android.os.ServiceManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
@@ -563,6 +564,24 @@
     }
 
     /**
+     * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
+     * below it is rejected by the system.
+     * This prevent auto-brightness from setting the screen so dark as to prevent the user from
+     * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
+     * in that ambient brightness.
+     *
+     * @return The minimum brightness curve (as lux values and their corresponding nits values).
+     */
+    public Pair<float[], float[]> getMinimumBrightnessCurve() {
+        try {
+            Curve curve = mDm.getMinimumBrightnessCurve();
+            return Pair.create(curve.getX(), curve.getY());
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Retrieves ambient brightness stats.
      */
     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index b77de748..b575997 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -19,6 +19,7 @@
 import android.content.pm.ParceledListSlice;
 import android.graphics.Point;
 import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.Curve;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.display.WifiDisplay;
@@ -112,4 +113,7 @@
 
     // Temporarily sets the auto brightness adjustment factor.
     void setTemporaryAutoBrightnessAdjustment(float adjustment);
+
+    // Get the minimum brightness curve.
+    Curve getMinimumBrightnessCurve();
 }
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 1a28732..e84c85e 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -19,7 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.BackupUtils;
-import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 
 import com.android.internal.util.Preconditions;
@@ -136,7 +136,7 @@
         return 0;
     }
 
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         return cycleRule.cycleIterator();
     }
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index bf6b7e0..6546c39 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@
 import android.os.UserHandle;
 import android.util.DebugUtils;
 import android.util.Pair;
+import android.util.Range;
 
 import com.google.android.collect.Sets;
 
@@ -258,8 +259,21 @@
     }
 
     /** {@hide} */
+    @Deprecated
     public static Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator(NetworkPolicy policy) {
-        return policy.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = policy.cycleIterator();
+        return new Iterator<Pair<ZonedDateTime, ZonedDateTime>>() {
+            @Override
+            public boolean hasNext() {
+                return it.hasNext();
+            }
+
+            @Override
+            public Pair<ZonedDateTime, ZonedDateTime> next() {
+                final Range<ZonedDateTime> r = it.next();
+                return Pair.create(r.getLower(), r.getUpper());
+            }
+        };
     }
 
     /**
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 6f812ac..bd4a27c 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -168,11 +168,6 @@
          * the requested network's required capabilities.  Note that when searching
          * for a network to satisfy a request, all capabilities requested must be
          * satisfied.
-         * <p>
-         * If the given capability was previously added to the list of unwanted capabilities
-         * then the capability will also be removed from the list of unwanted capabilities.
-         *
-         * @see #addUnwantedCapability(int)
          *
          * @param capability The capability to add.
          * @return The builder to facilitate chaining
@@ -184,8 +179,7 @@
         }
 
         /**
-         * Removes (if found) the given capability from this builder instance from both required
-         * and unwanted capabilities lists.
+         * Removes (if found) the given capability from this builder instance.
          *
          * @param capability The capability to remove.
          * @return The builder to facilitate chaining.
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
index b00cb48..321f971 100644
--- a/core/java/android/net/NetworkState.java
+++ b/core/java/android/net/NetworkState.java
@@ -26,6 +26,8 @@
  * @hide
  */
 public class NetworkState implements Parcelable {
+    private static final boolean SANITY_CHECK_ROAMING = false;
+
     public static final NetworkState EMPTY = new NetworkState(null, null, null, null, null, null);
 
     public final NetworkInfo networkInfo;
@@ -47,7 +49,7 @@
 
         // This object is an atomic view of a network, so the various components
         // should always agree on roaming state.
-        if (networkInfo != null && networkCapabilities != null) {
+        if (SANITY_CHECK_ROAMING && networkInfo != null && networkCapabilities != null) {
             if (networkInfo.isRoaming() == networkCapabilities
                     .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)) {
                 Slog.wtf("NetworkState", "Roaming state disagreement between " + networkInfo
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index e0fa63a..f9b6dfc 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -21,7 +21,6 @@
 
 import com.android.org.conscrypt.TrustManagerImpl;
 
-import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.security.cert.CertificateException;
@@ -133,8 +132,6 @@
     /**
      * Returns {@code true} if the TrustManager uses the same trust configuration for the provided
      * hostnames.
-     *
-     * @hide
      */
     @SystemApi
     public boolean isSameTrustConfiguration(String hostname1, String hostname2) {
diff --git a/core/java/android/os/IStatsManager.aidl b/core/java/android/os/IStatsManager.aidl
index 2a68714..3b32c52 100644
--- a/core/java/android/os/IStatsManager.aidl
+++ b/core/java/android/os/IStatsManager.aidl
@@ -77,45 +77,51 @@
     /**
      * Fetches data for the specified configuration key. Returns a byte array representing proto
      * wire-encoded of ConfigMetricsReportList.
+     *
+     * Requires Manifest.permission.DUMP.
      */
     byte[] getData(in long key);
 
     /**
      * Fetches metadata across statsd. Returns byte array representing wire-encoded proto.
+     *
+     * Requires Manifest.permission.DUMP.
      */
     byte[] getMetadata();
 
     /**
      * Sets a configuration with the specified config key and subscribes to updates for this
      * configuration key. Broadcasts will be sent if this configuration needs to be collected.
-     * The configuration must be a wire-encoded StatsDConfig. The receiver for this data is
+     * The configuration must be a wire-encoded StatsdConfig. The receiver for this data is
      * registered in a separate function.
      *
-     * Returns if this configuration was correctly registered.
+     * Requires Manifest.permission.DUMP.
      */
-    boolean addConfiguration(in long configKey, in byte[] config);
+    void addConfiguration(in long configKey, in byte[] config);
 
     /**
      * Registers the given pending intent for this config key. This intent is invoked when the
      * memory consumed by the metrics for this configuration approach the pre-defined limits. There
      * can be at most one listener per config key.
      *
-     * Returns if this listener was correctly registered.
+     * Requires Manifest.permission.DUMP.
      */
-    boolean setDataFetchOperation(long configKey, in IBinder intentSender);
+    void setDataFetchOperation(long configKey, in IBinder intentSender);
 
     /**
      * Removes the data fetch operation for the specified configuration.
+     *
+     * Requires Manifest.permission.DUMP.
      */
-    boolean removeDataFetchOperation(long configKey);
+    void removeDataFetchOperation(long configKey);
 
     /**
      * Removes the configuration with the matching config key. No-op if this config key does not
      * exist.
      *
-     * Returns if this configuration key was removed.
+     * Requires Manifest.permission.DUMP.
      */
-    boolean removeConfiguration(in long configKey);
+    void removeConfiguration(in long configKey);
 
     /**
      * Set the IIntentSender (i.e. PendingIntent) to be used when broadcasting subscriber
@@ -133,16 +139,16 @@
      * intentSender must be convertible into an IntentSender using IntentSender(IBinder)
      * and cannot be null.
      *
-     * Returns true if successful.
+     * Requires Manifest.permission.DUMP.
      */
-    boolean setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
+    void setBroadcastSubscriber(long configKey, long subscriberId, in IBinder intentSender);
 
     /**
      * Undoes setBroadcastSubscriber() for the (configKey, subscriberId) pair.
      * Any broadcasts associated with subscriberId will henceforth not be sent.
      * No-op if this (configKey, subsriberId) pair was not associated with an IntentSender.
      *
-     * Returns true if successful.
+     * Requires Manifest.permission.DUMP.
      */
-    boolean unsetBroadcastSubscriber(long configKey, long subscriberId);
+    void unsetBroadcastSubscriber(long configKey, long subscriberId);
 }
diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java
index 17d83db..3270719 100644
--- a/core/java/android/os/WorkSource.java
+++ b/core/java/android/os/WorkSource.java
@@ -924,13 +924,17 @@
         /** @hide */
         @VisibleForTesting
         public int[] getUids() {
-            return mUids;
+            int[] uids = new int[mSize];
+            System.arraycopy(mUids, 0, uids, 0, mSize);
+            return uids;
         }
 
         /** @hide */
         @VisibleForTesting
         public String[] getTags() {
-            return mTags;
+            String[] tags = new String[mSize];
+            System.arraycopy(mTags, 0, tags, 0, mSize);
+            return tags;
         }
 
         /** @hide */
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index d1d5d8e..673da50 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -166,6 +166,11 @@
     private List<String> mApiBlacklistExemptions = Collections.emptyList();
 
     /**
+     * Proportion of hidden API accesses that should be logged to the event log; 0 - 0x10000.
+     */
+    private int mHiddenApiAccessLogSampleRate;
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -478,6 +483,21 @@
         }
     }
 
+    /**
+     * Set the precentage of detected hidden API accesses that are logged to the event log.
+     *
+     * <p>This rate will take affect for all new processes forked from the zygote after this call.
+     *
+     * @param rate An integer between 0 and 0x10000 inclusive. 0 means no event logging.
+     */
+    public void setHiddenApiAccessLogSampleRate(int rate) {
+        synchronized (mLock) {
+            mHiddenApiAccessLogSampleRate = rate;
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
+        }
+    }
+
     @GuardedBy("mLock")
     private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
         if (state == null || state.isClosed()) {
@@ -505,6 +525,29 @@
         }
     }
 
+    private void maybeSetHiddenApiAccessLogSampleRate(ZygoteState state) {
+        if (state == null || state.isClosed()) {
+            return;
+        }
+        if (mHiddenApiAccessLogSampleRate == -1) {
+            return;
+        }
+        try {
+            state.writer.write(Integer.toString(1));
+            state.writer.newLine();
+            state.writer.write("--hidden-api-log-sampling-rate="
+                    + Integer.toString(mHiddenApiAccessLogSampleRate));
+            state.writer.newLine();
+            state.writer.flush();
+            int status = state.inputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set hidden API log sampling rate", ioe);
+        }
+    }
+
     /**
      * Tries to open socket to Zygote process if not already open. If
      * already open, does nothing.  May block and retry.  Requires that mLock be held.
@@ -520,6 +563,7 @@
                 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
             }
             maybeSetApiBlacklistExemptions(primaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
         }
         if (primaryZygoteState.matches(abi)) {
             return primaryZygoteState;
@@ -533,6 +577,7 @@
                 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
             }
             maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
+            maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
         }
 
         if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 9114107..d493cce 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -17,6 +17,7 @@
 package android.os.storage;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -93,7 +94,7 @@
         return true;
     }
 
-    public String getDescription() {
+    public @Nullable String getDescription() {
         final Resources res = Resources.getSystem();
         if ((flags & FLAG_SD) != 0) {
             if (isInteresting(label)) {
@@ -112,6 +113,17 @@
         }
     }
 
+    public @Nullable String getShortDescription() {
+        final Resources res = Resources.getSystem();
+        if (isSd()) {
+            return res.getString(com.android.internal.R.string.storage_sd_card);
+        } else if (isUsb()) {
+            return res.getString(com.android.internal.R.string.storage_usb_drive);
+        } else {
+            return null;
+        }
+    }
+
     public boolean isAdoptable() {
         return (flags & FLAG_ADOPTABLE) != 0;
     }
diff --git a/core/java/android/preference/DialogPreference.java b/core/java/android/preference/DialogPreference.java
index 3d57b4d..ed29f74 100644
--- a/core/java/android/preference/DialogPreference.java
+++ b/core/java/android/preference/DialogPreference.java
@@ -42,7 +42,7 @@
  * A base class for {@link Preference} objects that are
  * dialog-based. These preferences will, when clicked, open a dialog showing the
  * actual preference controls.
- * 
+ *
  * @attr ref android.R.styleable#DialogPreference_dialogTitle
  * @attr ref android.R.styleable#DialogPreference_dialogMessage
  * @attr ref android.R.styleable#DialogPreference_dialogIcon
@@ -54,7 +54,7 @@
         DialogInterface.OnClickListener, DialogInterface.OnDismissListener,
         PreferenceManager.OnActivityDestroyListener {
     private AlertDialog.Builder mBuilder;
-    
+
     private CharSequence mDialogTitle;
     private CharSequence mDialogMessage;
     private Drawable mDialogIcon;
@@ -68,6 +68,14 @@
     /** Which button was clicked. */
     private int mWhichButtonClicked;
 
+    /** Dismiss the dialog on the UI thread, but not inline with handlers */
+    private final Runnable mDismissRunnable = new Runnable() {
+        @Override
+        public void run() {
+            mDialog.dismiss();
+        }
+    };
+
     public DialogPreference(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
@@ -103,7 +111,7 @@
 
     /**
      * Sets the title of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogTitle The title.
      */
     public void setDialogTitle(CharSequence dialogTitle) {
@@ -117,7 +125,7 @@
     public void setDialogTitle(int dialogTitleResId) {
         setDialogTitle(getContext().getString(dialogTitleResId));
     }
-    
+
     /**
      * Returns the title to be shown on subsequent dialogs.
      * @return The title.
@@ -125,7 +133,7 @@
     public CharSequence getDialogTitle() {
         return mDialogTitle;
     }
-    
+
     /**
      * Sets the message of the dialog. This will be shown on subsequent dialogs.
      * <p>
@@ -133,7 +141,7 @@
      * list-based dialogs, for example. If setting a custom View on a dialog via
      * {@link #setDialogLayoutResource(int)}, include a text View with ID
      * {@link android.R.id#message} and it will be populated with this message.
-     * 
+     *
      * @param dialogMessage The message.
      */
     public void setDialogMessage(CharSequence dialogMessage) {
@@ -147,7 +155,7 @@
     public void setDialogMessage(int dialogMessageResId) {
         setDialogMessage(getContext().getString(dialogMessageResId));
     }
-    
+
     /**
      * Returns the message to be shown on subsequent dialogs.
      * @return The message.
@@ -155,26 +163,26 @@
     public CharSequence getDialogMessage() {
         return mDialogMessage;
     }
-    
+
     /**
      * Sets the icon of the dialog. This will be shown on subsequent dialogs.
-     * 
+     *
      * @param dialogIcon The icon, as a {@link Drawable}.
      */
     public void setDialogIcon(Drawable dialogIcon) {
         mDialogIcon = dialogIcon;
     }
-    
+
     /**
      * Sets the icon (resource ID) of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param dialogIconRes The icon, as a resource ID.
      */
     public void setDialogIcon(@DrawableRes int dialogIconRes) {
         mDialogIcon = getContext().getDrawable(dialogIconRes);
     }
-    
+
     /**
      * Returns the icon to be shown on subsequent dialogs.
      * @return The icon, as a {@link Drawable}.
@@ -182,11 +190,11 @@
     public Drawable getDialogIcon() {
         return mDialogIcon;
     }
-    
+
     /**
      * Sets the text of the positive button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param positiveButtonText The text of the positive button.
      */
     public void setPositiveButtonText(CharSequence positiveButtonText) {
@@ -200,27 +208,27 @@
     public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
         setPositiveButtonText(getContext().getString(positiveButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the positive button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the positive button.
      */
     public CharSequence getPositiveButtonText() {
         return mPositiveButtonText;
     }
-    
+
     /**
      * Sets the text of the negative button of the dialog. This will be shown on
      * subsequent dialogs.
-     * 
+     *
      * @param negativeButtonText The text of the negative button.
      */
     public void setNegativeButtonText(CharSequence negativeButtonText) {
         mNegativeButtonText = negativeButtonText;
     }
-    
+
     /**
      * @see #setNegativeButtonText(CharSequence)
      * @param negativeButtonTextResId The negative button text as a resource.
@@ -228,38 +236,38 @@
     public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
         setNegativeButtonText(getContext().getString(negativeButtonTextResId));
     }
-    
+
     /**
      * Returns the text of the negative button to be shown on subsequent
      * dialogs.
-     * 
+     *
      * @return The text of the negative button.
      */
     public CharSequence getNegativeButtonText() {
         return mNegativeButtonText;
     }
-    
+
     /**
      * Sets the layout resource that is inflated as the {@link View} to be shown
      * as the content View of subsequent dialogs.
-     * 
+     *
      * @param dialogLayoutResId The layout resource ID to be inflated.
      * @see #setDialogMessage(CharSequence)
      */
     public void setDialogLayoutResource(int dialogLayoutResId) {
         mDialogLayoutResId = dialogLayoutResId;
     }
-    
+
     /**
      * Returns the layout resource that is used as the content View for
      * subsequent dialogs.
-     * 
+     *
      * @return The layout resource.
      */
     public int getDialogLayoutResource() {
         return mDialogLayoutResId;
     }
-    
+
     /**
      * Prepares the dialog builder to be shown when the preference is clicked.
      * Use this to set custom properties on the dialog.
@@ -269,7 +277,7 @@
      */
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
     }
-    
+
     @Override
     protected void onClick() {
         if (mDialog != null && mDialog.isShowing()) return;
@@ -281,14 +289,14 @@
      * Shows the dialog associated with this Preference. This is normally initiated
      * automatically on clicking on the preference. Call this method if you need to
      * show the dialog on some other event.
-     * 
+     *
      * @param state Optional instance state to restore on the dialog
      */
     protected void showDialog(Bundle state) {
         Context context = getContext();
 
         mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
-        
+
         mBuilder = new AlertDialog.Builder(context)
             .setTitle(mDialogTitle)
             .setIcon(mDialogIcon)
@@ -302,11 +310,11 @@
         } else {
             mBuilder.setMessage(mDialogMessage);
         }
-        
+
         onPrepareDialogBuilder(mBuilder);
-        
+
         getPreferenceManager().registerOnActivityDestroyListener(this);
-        
+
         // Create the dialog
         final Dialog dialog = mDialog = mBuilder.create();
         if (state != null) {
@@ -315,10 +323,29 @@
         if (needInputMethod()) {
             requestInputMethod(dialog);
         }
+        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
+            @Override
+            public void onShow(DialogInterface dialog) {
+                removeDismissCallbacks();
+            }
+        });
         dialog.setOnDismissListener(this);
         dialog.show();
     }
 
+    void postDismiss() {
+        removeDismissCallbacks();
+        View decorView = mDialog.getWindow().getDecorView();
+        decorView.post(mDismissRunnable);
+    }
+
+    private void removeDismissCallbacks() {
+        if (mDialog != null && mDialog.getWindow() != null
+                && mDialog.getWindow().getDecorView() != null) {
+            mDialog.getWindow().getDecorView().removeCallbacks(mDismissRunnable);
+        }
+    }
+
     /**
      * Returns whether the preference needs to display a soft input method when the dialog
      * is displayed. Default is false. Subclasses should override this method if they need
@@ -341,7 +368,7 @@
      * Creates the content view for the dialog (if a custom content view is
      * required). By default, it inflates the dialog layout resource if it is
      * set.
-     * 
+     *
      * @return The content View for the dialog.
      * @see #setLayoutResource(int)
      */
@@ -349,48 +376,49 @@
         if (mDialogLayoutResId == 0) {
             return null;
         }
-        
+
         LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
         return inflater.inflate(mDialogLayoutResId, null);
     }
-    
+
     /**
      * Binds views in the content View of the dialog to data.
      * <p>
      * Make sure to call through to the superclass implementation.
-     * 
+     *
      * @param view The content View of the dialog, if it is custom.
      */
     @CallSuper
     protected void onBindDialogView(View view) {
         View dialogMessageView = view.findViewById(com.android.internal.R.id.message);
-        
+
         if (dialogMessageView != null) {
             final CharSequence message = getDialogMessage();
             int newVisibility = View.GONE;
-            
+
             if (!TextUtils.isEmpty(message)) {
                 if (dialogMessageView instanceof TextView) {
                     ((TextView) dialogMessageView).setText(message);
                 }
-                
+
                 newVisibility = View.VISIBLE;
             }
-            
+
             if (dialogMessageView.getVisibility() != newVisibility) {
                 dialogMessageView.setVisibility(newVisibility);
             }
         }
     }
-    
+
     public void onClick(DialogInterface dialog, int which) {
         mWhichButtonClicked = which;
     }
-    
+
+    @Override
     public void onDismiss(DialogInterface dialog) {
-        
+        removeDismissCallbacks();
         getPreferenceManager().unregisterOnActivityDestroyListener(this);
-        
+
         mDialog = null;
         onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
     }
@@ -398,7 +426,7 @@
     /**
      * Called when the dialog is dismissed and should be used to save data to
      * the {@link SharedPreferences}.
-     * 
+     *
      * @param positiveResult Whether the positive button was clicked (true), or
      *            the negative button was clicked or the dialog was canceled (false).
      */
@@ -407,7 +435,7 @@
 
     /**
      * Gets the dialog that is shown by this preference.
-     * 
+     *
      * @return The dialog, or null if a dialog is not being shown.
      */
     public Dialog getDialog() {
@@ -418,11 +446,11 @@
      * {@inheritDoc}
      */
     public void onActivityDestroy() {
-        
+
         if (mDialog == null || !mDialog.isShowing()) {
             return;
         }
-        
+
         mDialog.dismiss();
     }
 
@@ -457,7 +485,7 @@
     private static class SavedState extends BaseSavedState {
         boolean isDialogShowing;
         Bundle dialogBundle;
-        
+
         public SavedState(Parcel source) {
             super(source);
             isDialogShowing = source.readInt() == 1;
@@ -486,5 +514,5 @@
             }
         };
     }
-    
+
 }
diff --git a/core/java/android/preference/ListPreference.java b/core/java/android/preference/ListPreference.java
index 2700373..f9323ed 100644
--- a/core/java/android/preference/ListPreference.java
+++ b/core/java/android/preference/ListPreference.java
@@ -32,7 +32,7 @@
  * <p>
  * This preference will store a string into the SharedPreferences. This string will be the value
  * from the {@link #setEntryValues(CharSequence[])} array.
- * 
+ *
  * @attr ref android.R.styleable#ListPreference_entries
  * @attr ref android.R.styleable#ListPreference_entryValues
  */
@@ -191,7 +191,7 @@
 
     /**
      * Sets the value to the given index from the entry values.
-     * 
+     *
      * @param index The index of the value to set.
      */
     public void setValueIndex(int index) {
@@ -199,30 +199,30 @@
             setValue(mEntryValues[index].toString());
         }
     }
-    
+
     /**
      * Returns the value of the key. This should be one of the entries in
      * {@link #getEntryValues()}.
-     * 
+     *
      * @return The value of the key.
      */
     public String getValue() {
-        return mValue; 
+        return mValue;
     }
-    
+
     /**
      * Returns the entry corresponding to the current value.
-     * 
+     *
      * @return The entry corresponding to the current value, or null.
      */
     public CharSequence getEntry() {
         int index = getValueIndex();
         return index >= 0 && mEntries != null ? mEntries[index] : null;
     }
-    
+
     /**
      * Returns the index of the given value (in the entry values array).
-     * 
+     *
      * @param value The value whose index should be returned.
      * @return The index of the value, or -1 if not found.
      */
@@ -236,22 +236,22 @@
         }
         return -1;
     }
-    
+
     private int getValueIndex() {
         return findIndexOfValue(mValue);
     }
-    
+
     @Override
     protected void onPrepareDialogBuilder(Builder builder) {
         super.onPrepareDialogBuilder(builder);
-        
+
         if (mEntries == null || mEntryValues == null) {
             throw new IllegalStateException(
                     "ListPreference requires an entries array and an entryValues array.");
         }
 
         mClickedDialogEntryIndex = getValueIndex();
-        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex, 
+        builder.setSingleChoiceItems(mEntries, mClickedDialogEntryIndex,
                 new DialogInterface.OnClickListener() {
                     public void onClick(DialogInterface dialog, int which) {
                         mClickedDialogEntryIndex = which;
@@ -261,10 +261,10 @@
                          * click, and dismisses the dialog.
                          */
                         ListPreference.this.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
-                        dialog.dismiss();
+                        postDismiss();
                     }
         });
-        
+
         /*
          * The typical interaction for list-based dialogs is to have
          * click-on-an-item dismiss the dialog instead of the user having to
@@ -276,7 +276,7 @@
     @Override
     protected void onDialogClosed(boolean positiveResult) {
         super.onDialogClosed(positiveResult);
-        
+
         if (positiveResult && mClickedDialogEntryIndex >= 0 && mEntryValues != null) {
             String value = mEntryValues[mClickedDialogEntryIndex].toString();
             if (callChangeListener(value)) {
@@ -294,7 +294,7 @@
     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
         setValue(restoreValue ? getPersistedString(mValue) : (String) defaultValue);
     }
-    
+
     @Override
     protected Parcelable onSaveInstanceState() {
         final Parcelable superState = super.onSaveInstanceState();
@@ -302,7 +302,7 @@
             // No need to save instance state since it's persistent
             return superState;
         }
-        
+
         final SavedState myState = new SavedState(superState);
         myState.value = getValue();
         return myState;
@@ -315,15 +315,15 @@
             super.onRestoreInstanceState(state);
             return;
         }
-         
+
         SavedState myState = (SavedState) state;
         super.onRestoreInstanceState(myState.getSuperState());
         setValue(myState.value);
     }
-    
+
     private static class SavedState extends BaseSavedState {
         String value;
-        
+
         public SavedState(Parcel source) {
             super(source);
             value = source.readString();
@@ -350,5 +350,5 @@
             }
         };
     }
-    
+
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b644c1d..ef87752 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1179,6 +1179,23 @@
     public static final String ACTION_ZEN_MODE_SETTINGS = "android.settings.ZEN_MODE_SETTINGS";
 
     /**
+     * Activity Action: Show Zen Mode visual effects configuration settings.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ZEN_MODE_BLOCKED_EFFECTS_SETTINGS =
+            "android.settings.ZEN_MODE_BLOCKED_EFFECTS_SETTINGS";
+
+    /**
+     * Activity Action: Show Zen Mode onboarding activity.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ZEN_MODE_ONBOARDING = "android.settings.ZEN_MODE_ONBOARDING";
+
+    /**
      * Activity Action: Show Zen Mode (aka Do Not Disturb) priority configuration settings.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -3113,6 +3130,9 @@
          */
         public static final String DISPLAY_COLOR_MODE = "display_color_mode";
 
+        private static final Validator DISPLAY_COLOR_MODE_VALIDATOR =
+                new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+
         /**
          * The amount of time in milliseconds before the device goes to sleep or begins
          * to dream after a period of inactivity.  This value is also known as the
@@ -4100,6 +4120,7 @@
             SHOW_BATTERY_PERCENT,
             NOTIFICATION_VIBRATION_INTENSITY,
             HAPTIC_FEEDBACK_INTENSITY,
+            DISPLAY_COLOR_MODE
         };
 
         /**
@@ -4212,6 +4233,7 @@
             PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED);
             PRIVATE_SETTINGS.add(EGG_MODE);
             PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT);
+            PRIVATE_SETTINGS.add(DISPLAY_COLOR_MODE);
         }
 
         /**
@@ -4233,6 +4255,7 @@
             VALIDATORS.put(NEXT_ALARM_FORMATTED, NEXT_ALARM_FORMATTED_VALIDATOR);
             VALIDATORS.put(FONT_SCALE, FONT_SCALE_VALIDATOR);
             VALIDATORS.put(DIM_SCREEN, DIM_SCREEN_VALIDATOR);
+            VALIDATORS.put(DISPLAY_COLOR_MODE, DISPLAY_COLOR_MODE_VALIDATOR);
             VALIDATORS.put(SCREEN_OFF_TIMEOUT, SCREEN_OFF_TIMEOUT_VALIDATOR);
             VALIDATORS.put(SCREEN_BRIGHTNESS_FOR_VR, SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR);
             VALIDATORS.put(SCREEN_BRIGHTNESS_MODE, SCREEN_BRIGHTNESS_MODE_VALIDATOR);
@@ -11125,6 +11148,14 @@
         public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities";
 
         /**
+         * If nonzero, all system error dialogs will be hidden.  For example, the
+         * crash and ANR dialogs will not be shown, and the system will just proceed
+         * as if they had been accepted by the user.
+         * @hide
+         */
+        public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs";
+
+        /**
          * Use Dock audio output for media:
          *      0 = disabled
          *      1 = enabled
@@ -11743,6 +11774,38 @@
                 "hidden_api_blacklist_exemptions";
 
         /**
+         * Sampling rate for hidden API access event logs, as an integer in the range 0 to 0x10000
+         * inclusive.
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_ACCESS_LOG_SAMPLING_RATE =
+                "hidden_api_access_log_sampling_rate";
+
+        /**
+         * Hidden API enforcement policy for apps targeting SDK versions prior to the latest
+         * version.
+         *
+         * Values correspond to @{@link
+         * android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_POLICY_PRE_P_APPS =
+                "hidden_api_policy_pre_p_apps";
+
+        /**
+         * Hidden API enforcement policy for apps targeting the current SDK version.
+         *
+         * Values correspond to @{@link
+         * android.content.pm.ApplicationInfo.HiddenApiEnforcementPolicy}
+         *
+         * @hide
+         */
+        public static final String HIDDEN_API_POLICY_P_APPS =
+                "hidden_api_policy_p_apps";
+
+        /**
          * Timeout for a single {@link android.media.soundtrigger.SoundTriggerDetectionService}
          * operation (in ms).
          *
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index f351c5a..b84843b 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -288,6 +289,18 @@
     }
 
     /**
+     * Checks whether the recoverable key store is currently available.
+     *
+     * <p>If it returns true, the device must currently be using a screen lock that is supported for
+     * use with the recoverable key store, i.e. AOSP PIN, pattern or password.
+     */
+    @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
+    public static boolean isRecoverableKeyStoreEnabled(@NonNull Context context) {
+        KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+        return keyguardManager != null && keyguardManager.isDeviceSecure();
+    }
+
+    /**
      * @deprecated Use {@link #initRecoveryService(String, byte[], byte[])} instead.
      */
     @Deprecated
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index daecea7..7b01f7a 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -83,7 +83,8 @@
     private static final int DAY_MINUTES = 24 * 60;
     private static final int ZERO_VALUE_MS = 10 * SECONDS_MS;
 
-    // Default allow categories set in readXml() from default_zen_mode_config.xml, fallback values:
+    // Default allow categories set in readXml() from default_zen_mode_config.xml,
+    // fallback/upgrade values:
     private static final boolean DEFAULT_ALLOW_ALARMS = true;
     private static final boolean DEFAULT_ALLOW_MEDIA = true;
     private static final boolean DEFAULT_ALLOW_SYSTEM = false;
@@ -97,7 +98,7 @@
     private static final int DEFAULT_SUPPRESSED_VISUAL_EFFECTS =
             Policy.getAllSuppressedVisualEffects();
 
-    public static final int XML_VERSION = 6;
+    public static final int XML_VERSION = 7;
     public static final String ZEN_TAG = "zen";
     private static final String ZEN_ATT_VERSION = "version";
     private static final String ZEN_ATT_USER = "user";
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index f1fd812..c48b6d1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -813,7 +813,7 @@
                     }
                     final int relayoutResult = mSession.relayout(
                         mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
-                            View.VISIBLE, 0, mWinFrame, mOverscanInsets, mContentInsets,
+                            View.VISIBLE, 0, -1, mWinFrame, mOverscanInsets, mContentInsets,
                             mVisibleInsets, mStableInsets, mOutsets, mBackdropFrame,
                             mDisplayCutout, mMergedConfiguration, mSurfaceHolder.mSurface);
 
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 96edfa3..c2c3182 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -303,10 +303,9 @@
      *
      * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
      */
-    public void getBounds(@NonNull Paint paint, @IntRange(from = 0) int start,
-            @IntRange(from = 0) int end, @NonNull Rect bounds) {
-        nGetBounds(mNativePtr, mCopiedBuffer, paint.getNativeInstance(), start, end,
-                paint.getBidiFlags(), bounds);
+    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
+            @NonNull Rect bounds) {
+        nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
     }
 
     /**
@@ -743,6 +742,6 @@
     @CriticalNative
     private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
 
-    private static native void nGetBounds(long nativePtr, char[] buf, long paintPtr, int start,
-            int end, int bidiFlag, Rect rect);
+    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+            Rect rect);
 }
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 44789d6..369f357 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -515,8 +515,7 @@
                 + "para: (" + paraStart + ", " + paraEnd + "), "
                 + "request: (" + start + ", " + end + ")");
         }
-        getMeasuredParagraph(paraIndex).getBounds(mParams.mPaint,
-                start - paraStart, end - paraStart, bounds);
+        getMeasuredParagraph(paraIndex).getBounds(start - paraStart, end - paraStart, bounds);
     }
 
     /**
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index ad3b4b6..de86a66 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -40,6 +40,10 @@
     public static final int FLAG_SHORTER = 1 << 0;
     /** {@hide} */
     public static final int FLAG_CALCULATE_ROUNDED = 1 << 1;
+    /** {@hide} */
+    public static final int FLAG_SI_UNITS = 1 << 2;
+    /** {@hide} */
+    public static final int FLAG_IEC_UNITS = 1 << 3;
 
     /** {@hide} */
     public static class BytesResult {
@@ -90,7 +94,7 @@
         if (context == null) {
             return "";
         }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, 0);
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SI_UNITS);
         return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
                 res.value, res.units));
     }
@@ -103,41 +107,43 @@
         if (context == null) {
             return "";
         }
-        final BytesResult res = formatBytes(context.getResources(), sizeBytes, FLAG_SHORTER);
+        final BytesResult res = formatBytes(context.getResources(), sizeBytes,
+                FLAG_SI_UNITS | FLAG_SHORTER);
         return bidiWrap(context, context.getString(com.android.internal.R.string.fileSizeSuffix,
                 res.value, res.units));
     }
 
     /** {@hide} */
     public static BytesResult formatBytes(Resources res, long sizeBytes, int flags) {
+        final int unit = ((flags & FLAG_IEC_UNITS) != 0) ? 1024 : 1000;
         final boolean isNegative = (sizeBytes < 0);
         float result = isNegative ? -sizeBytes : sizeBytes;
         int suffix = com.android.internal.R.string.byteShort;
         long mult = 1;
         if (result > 900) {
             suffix = com.android.internal.R.string.kilobyteShort;
-            mult = 1000;
-            result = result / 1000;
+            mult = unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.megabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.gigabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.terabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         if (result > 900) {
             suffix = com.android.internal.R.string.petabyteShort;
-            mult *= 1000;
-            result = result / 1000;
+            mult *= unit;
+            result = result / unit;
         }
         // Note we calculate the rounded long by ourselves, but still let String.format()
         // compute the rounded value. String.format("%f", 0.1) might not return "0.1" due to
diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java
index 9f115eb..975ad48 100644
--- a/core/java/android/util/RecurrenceRule.java
+++ b/core/java/android/util/RecurrenceRule.java
@@ -158,7 +158,7 @@
                 && period.getDays() == 0;
     }
 
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         if (period != null) {
             return new RecurringIterator();
         } else {
@@ -166,7 +166,7 @@
         }
     }
 
-    private class NonrecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+    private class NonrecurringIterator implements Iterator<Range<ZonedDateTime>> {
         boolean hasNext;
 
         public NonrecurringIterator() {
@@ -179,13 +179,13 @@
         }
 
         @Override
-        public Pair<ZonedDateTime, ZonedDateTime> next() {
+        public Range<ZonedDateTime> next() {
             hasNext = false;
-            return new Pair<>(start, end);
+            return new Range<>(start, end);
         }
     }
 
-    private class RecurringIterator implements Iterator<Pair<ZonedDateTime, ZonedDateTime>> {
+    private class RecurringIterator implements Iterator<Range<ZonedDateTime>> {
         int i;
         ZonedDateTime cycleStart;
         ZonedDateTime cycleEnd;
@@ -231,12 +231,12 @@
         }
 
         @Override
-        public Pair<ZonedDateTime, ZonedDateTime> next() {
+        public Range<ZonedDateTime> next() {
             if (LOGD) Log.d(TAG, "Cycle " + i + " from " + cycleStart + " to " + cycleEnd);
-            Pair<ZonedDateTime, ZonedDateTime> p = new Pair<>(cycleStart, cycleEnd);
+            Range<ZonedDateTime> r = new Range<>(cycleStart, cycleEnd);
             i--;
             updateCycle();
-            return p;
+            return r;
         }
     }
 
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 66a9c6c..f59c0b5 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -31,6 +31,7 @@
 import android.os.Parcelable;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.PathParser;
 import android.util.proto.ProtoOutputStream;
 
@@ -75,15 +76,19 @@
             false /* copyArguments */);
 
 
+    private static final Pair<Path, DisplayCutout> NULL_PAIR = new Pair<>(null, null);
     private static final Object CACHE_LOCK = new Object();
+
     @GuardedBy("CACHE_LOCK")
     private static String sCachedSpec;
     @GuardedBy("CACHE_LOCK")
     private static int sCachedDisplayWidth;
     @GuardedBy("CACHE_LOCK")
+    private static int sCachedDisplayHeight;
+    @GuardedBy("CACHE_LOCK")
     private static float sCachedDensity;
     @GuardedBy("CACHE_LOCK")
-    private static DisplayCutout sCachedCutout;
+    private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
 
     private final Rect mSafeInsets;
     private final Region mBounds;
@@ -347,7 +352,7 @@
     }
 
     /**
-     * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+     * Creates the bounding path according to @android:string/config_mainBuiltInDisplayCutout.
      *
      * @hide
      */
@@ -357,6 +362,16 @@
     }
 
     /**
+     * Creates an instance according to @android:string/config_mainBuiltInDisplayCutout.
+     *
+     * @hide
+     */
+    public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
+        return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+                displayWidth, displayHeight, res.getDisplayMetrics().density).first;
+    }
+
+    /**
      * Creates an instance according to the supplied {@link android.util.PathParser.PathData} spec.
      *
      * @hide
@@ -364,11 +379,17 @@
     @VisibleForTesting(visibility = PRIVATE)
     public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
             float density) {
+        return pathAndDisplayCutoutFromSpec(spec, displayWidth, displayHeight, density).second;
+    }
+
+    private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
+            int displayWidth, int displayHeight, float density) {
         if (TextUtils.isEmpty(spec)) {
-            return null;
+            return NULL_PAIR;
         }
         synchronized (CACHE_LOCK) {
             if (spec.equals(sCachedSpec) && sCachedDisplayWidth == displayWidth
+                    && sCachedDisplayHeight == displayHeight
                     && sCachedDensity == density) {
                 return sCachedCutout;
             }
@@ -398,7 +419,7 @@
             p = PathParser.createPathFromPathData(spec);
         } catch (Throwable e) {
             Log.wtf(TAG, "Could not inflate cutout: ", e);
-            return null;
+            return NULL_PAIR;
         }
 
         final Matrix m = new Matrix();
@@ -414,7 +435,7 @@
                 bottomPath = PathParser.createPathFromPathData(bottomSpec);
             } catch (Throwable e) {
                 Log.wtf(TAG, "Could not inflate bottom cutout: ", e);
-                return null;
+                return NULL_PAIR;
             }
             // Keep top transform
             m.postTranslate(0, displayHeight);
@@ -422,10 +443,11 @@
             p.addPath(bottomPath);
         }
 
-        final DisplayCutout result = fromBounds(p);
+        final Pair<Path, DisplayCutout> result = new Pair<>(p, fromBounds(p));
         synchronized (CACHE_LOCK) {
             sCachedSpec = spec;
             sCachedDisplayWidth = displayWidth;
+            sCachedDisplayHeight = displayHeight;
             sCachedDensity = density;
             sCachedCutout = result;
         }
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index d8a5609..f868a00 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -66,6 +66,7 @@
      * @param viewVisibility Window root view's visibility.
      * @param flags Request flags: {@link WindowManagerGlobal#RELAYOUT_INSETS_PENDING},
      * {@link WindowManagerGlobal#RELAYOUT_DEFER_SURFACE_DESTROY}.
+     * @param frameNumber A frame number in which changes requested in this layout will be rendered.
      * @param outFrame Rect in which is placed the new position/size on
      * screen.
      * @param outOverscanInsets Rect in which is placed the offsets from
@@ -96,7 +97,7 @@
      */
     int relayout(IWindow window, int seq, in WindowManager.LayoutParams attrs,
             int requestedWidth, int requestedHeight, int viewVisibility,
-            int flags, out Rect outFrame, out Rect outOverscanInsets,
+            int flags, long frameNumber, out Rect outFrame, out Rect outOverscanInsets,
             out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets,
             out Rect outOutsets, out Rect outBackdropFrame,
             out DisplayCutout.ParcelableWrapper displayCutout,
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index e03f5fa..0872032 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -907,10 +907,6 @@
                 animator.getAnimatorNativePtr());
     }
 
-    public void serializeDisplayListTree() {
-        nSerializeDisplayListTree(mNativeProxy);
-    }
-
     public static int copySurfaceInto(Surface surface, Rect srcRect, Bitmap bitmap) {
         if (srcRect == null) {
             // Empty rect means entire surface
@@ -1202,8 +1198,6 @@
     private static native void nStopDrawing(long nativeProxy);
     private static native void nNotifyFramePending(long nativeProxy);
 
-    private static native void nSerializeDisplayListTree(long nativeProxy);
-
     private static native void nDumpProfileInfo(long nativeProxy, FileDescriptor fd,
             @DumpFlags int dumpFlags);
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6002fe5..2ec42c0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5692,6 +5692,7 @@
         }
         dispatchVisibilityAggregated(isAttachedToWindow() && getWindowVisibility() == VISIBLE
                 && isShown());
+        notifySubtreeAccessibilityStateChangedIfNeeded();
     }
 
     /**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9231277..2c17bdf 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2961,9 +2961,6 @@
      */
     void outputDisplayList(View view) {
         view.mRenderNode.output();
-        if (mAttachInfo.mThreadedRenderer != null) {
-            mAttachInfo.mThreadedRenderer.serializeDisplayListTree();
-        }
     }
 
     /**
@@ -6482,17 +6479,17 @@
                     params.type = mOrigWindowType;
                 }
             }
-
-            if (mSurface.isValid()) {
-                params.frameNumber = mSurface.getNextFrameNumber();
-            }
         }
 
-        int relayoutResult = mWindowSession.relayout(
-                mWindow, mSeq, params,
+        long frameNumber = -1;
+        if (mSurface.isValid()) {
+            frameNumber = mSurface.getNextFrameNumber();
+        }
+
+        int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
                 (int) (mView.getMeasuredWidth() * appScale + 0.5f),
-                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
-                viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
+                (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
+                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
                 mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
                 mPendingMergedConfiguration, mSurface);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index f6181d7..0f5c23f 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2438,13 +2438,6 @@
         public long hideTimeoutMilliseconds = -1;
 
         /**
-         * A frame number in which changes requested in this layout will be rendered.
-         *
-         * @hide
-         */
-        public long frameNumber = -1;
-
-        /**
          * The color mode requested by this window. The target display may
          * not be able to honor the request. When the color mode is not set
          * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the
@@ -2617,7 +2610,6 @@
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
             out.writeInt(mColorMode);
             out.writeLong(hideTimeoutMilliseconds);
-            out.writeLong(frameNumber);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -2674,7 +2666,6 @@
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
             mColorMode = in.readInt();
             hideTimeoutMilliseconds = in.readLong();
-            frameNumber = in.readLong();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2875,10 +2866,6 @@
                 changes |= SURFACE_INSETS_CHANGED;
             }
 
-            // The frame number changing is only relevant in the context of other
-            // changes, and so we don't need to track it with a flag.
-            frameNumber = o.frameNumber;
-
             if (hasManualSurfaceInsets != o.hasManualSurfaceInsets) {
                 hasManualSurfaceInsets = o.hasManualSurfaceInsets;
                 changes |= SURFACE_INSETS_CHANGED;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 95a83da..7946e9e 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -201,6 +201,7 @@
  * <em>Properties:</em></br>
  * <ul>
  *   <li>{@link #getEventType()} - The type of the event.</li>
+ *   <li>{@link #getContentChangeTypes()} - The type of state changes.</li>
  *   <li>{@link #getSource()} - The source info (for registered clients).</li>
  *   <li>{@link #getClassName()} - The class name of the source.</li>
  *   <li>{@link #getPackageName()} - The package name of the source.</li>
@@ -863,16 +864,17 @@
     }
 
     /**
-     * Gets the bit mask of change types signaled by an
-     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event. A single event may represent
-     * multiple change types.
+     * Gets the bit mask of change types signaled by a
+     * {@link #TYPE_WINDOW_CONTENT_CHANGED} event or {@link #TYPE_WINDOW_STATE_CHANGED}. A single
+     * event may represent multiple change types.
      *
      * @return The bit mask of change types. One or more of:
      *         <ul>
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_SUBTREE}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_TEXT}
-     *         <li>{@link AccessibilityEvent#CONTENT_CHANGE_TYPE_UNDEFINED}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_SUBTREE}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_TEXT}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_PANE_TITLE}
+     *         <li>{@link #CONTENT_CHANGE_TYPE_UNDEFINED}
      *         </ul>
      */
     @ContentChangeTypes
@@ -891,6 +893,7 @@
             }
             case CONTENT_CHANGE_TYPE_SUBTREE: return "CONTENT_CHANGE_TYPE_SUBTREE";
             case CONTENT_CHANGE_TYPE_TEXT: return "CONTENT_CHANGE_TYPE_TEXT";
+            case CONTENT_CHANGE_TYPE_PANE_TITLE: return "CONTENT_CHANGE_TYPE_PANE_TITLE";
             case CONTENT_CHANGE_TYPE_UNDEFINED: return "CONTENT_CHANGE_TYPE_UNDEFINED";
             default: return Integer.toHexString(type);
         }
@@ -1324,7 +1327,7 @@
         }
         if (!DEBUG_CONCISE_TOSTRING || mWindowChangeTypes != 0) {
             builder.append("; WindowChangeTypes: ").append(
-                    contentChangeTypesToString(mWindowChangeTypes));
+                    windowChangeTypesToString(mWindowChangeTypes));
         }
         super.appendTo(builder);
         if (DEBUG || DEBUG_CONCISE_TOSTRING) {
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index f80625f..96016b4 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -375,13 +375,13 @@
      */
     public static final class Builder {
 
-        @NonNull private String mText;
         @NonNull private List<RemoteAction> mActions = new ArrayList<>();
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @Nullable Drawable mLegacyIcon;
-        @Nullable String mLegacyLabel;
-        @Nullable Intent mLegacyIntent;
-        @Nullable OnClickListener mLegacyOnClickListener;
+        @Nullable private String mText;
+        @Nullable private Drawable mLegacyIcon;
+        @Nullable private String mLegacyLabel;
+        @Nullable private Intent mLegacyIntent;
+        @Nullable private OnClickListener mLegacyOnClickListener;
         @Nullable private String mId;
 
         /**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 3d503e2..851b2c9 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -339,7 +339,7 @@
 
         /**
          * @return The config representing the set of entities to look for
-         * @see #setEntityConfig(TextClassifier.EntityConfig)
+         * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
          */
         @Nullable
         public TextClassifier.EntityConfig getEntityConfig() {
diff --git a/core/java/android/webkit/TracingConfig.java b/core/java/android/webkit/TracingConfig.java
index d95ca61..2080168 100644
--- a/core/java/android/webkit/TracingConfig.java
+++ b/core/java/android/webkit/TracingConfig.java
@@ -54,37 +54,37 @@
 
     /**
      * Predefined set of categories typically useful for analyzing WebViews.
-     * Typically includes android_webview and Java.
+     * Typically includes "android_webview" and "Java" categories.
      */
     public static final int CATEGORIES_ANDROID_WEBVIEW = 1 << 1;
 
     /**
      * Predefined set of categories typically useful for web developers.
-     * Typically includes blink, compositor, renderer.scheduler and v8 categories.
+     * Typically includes "blink", "compositor", "renderer.scheduler" and "v8" categories.
      */
     public static final int CATEGORIES_WEB_DEVELOPER = 1 << 2;
 
     /**
      * Predefined set of categories for analyzing input latency issues.
-     * Typically includes input, renderer.scheduler categories.
+     * Typically includes "input", "renderer.scheduler" categories.
      */
     public static final int CATEGORIES_INPUT_LATENCY = 1 << 3;
 
     /**
      * Predefined set of categories for analyzing rendering issues.
-     * Typically includes blink, compositor and gpu categories.
+     * Typically includes "blink", "compositor" and "gpu" categories.
      */
     public static final int CATEGORIES_RENDERING = 1 << 4;
 
     /**
      * Predefined set of categories for analyzing javascript and rendering issues.
-     * Typically includes blink, compositor, gpu, renderer.scheduler and v8 categories.
+     * Typically includes "blink", "compositor", "gpu", "renderer.scheduler" and "v8" categories.
      */
     public static final int CATEGORIES_JAVASCRIPT_AND_RENDERING = 1 << 5;
 
     /**
      * Predefined set of categories for studying difficult rendering performance problems.
-     * Typically includes blink, compositor, gpu, renderer.scheduler, v8 and
+     * Typically includes "blink", "compositor", "gpu", "renderer.scheduler", "v8" and
      * some other compositor categories which are disabled by default.
      */
     public static final int CATEGORIES_FRAME_VIEWER = 1 << 6;
@@ -123,7 +123,9 @@
     }
 
     /**
-     * Returns a bitmask of the predefined categories values of this configuration.
+     * Returns a bitmask of the predefined category sets of this configuration.
+     *
+     * @return Bitmask of predefined category sets.
      */
     @PredefinedCategories
     public int getPredefinedCategories() {
@@ -133,7 +135,7 @@
     /**
      * Returns the list of included custom category patterns for this configuration.
      *
-     * @return empty list if no custom category patterns are specified.
+     * @return Empty list if no custom category patterns are specified.
      */
     @NonNull
     public List<String> getCustomIncludedCategories() {
@@ -142,6 +144,8 @@
 
     /**
      * Returns the tracing mode of this configuration.
+     *
+     * @return The tracing mode of this configuration.
      */
     @TracingMode
     public int getTracingMode() {
@@ -150,28 +154,37 @@
 
     /**
      * Builder used to create {@link TracingConfig} objects.
-     *
+     * <p>
      * Examples:
-     *   new TracingConfig.Builder().build()
-     *       -- creates a configuration with default options: {@link #CATEGORIES_NONE},
-     *          {@link #RECORD_UNTIL_FULL}.
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()
-     *       -- records trace events from the "web developer" predefined category sets.
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
-     *                                             CATEGORIES_INPUT_LATENCY).build()
-     *       -- records trace events from the "rendering" and "input latency" predefined
-     *          category sets.
-     *   new TracingConfig.Builder().addCategories("browser").build()
-     *       -- records only the trace events from the "browser" category.
-     *   new TracingConfig.Builder().addCategories("blink*","renderer*").build()
-     *       -- records only the trace events matching the "blink*" and "renderer*" patterns
-     *          (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
-     *   new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
+     * <pre class="prettyprint">
+     *   // Create a configuration with default options: {@link #CATEGORIES_NONE},
+     *   // {@link #RECORD_CONTINUOUSLY}.
+     *   <code>new TracingConfig.Builder().build()</code>
+     *
+     *   // Record trace events from the "web developer" predefined category sets.
+     *   // Uses a ring buffer (the default {@link #RECORD_CONTINUOUSLY} mode) for
+     *   // internal storage during tracing.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER).build()</code>
+     *
+     *   // Record trace events from the "rendering" and "input latency" predefined
+     *   // category sets.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_RENDERING,
+     *                                     CATEGORIES_INPUT_LATENCY).build()</code>
+     *
+     *   // Record only the trace events from the "browser" category.
+     *   <code>new TracingConfig.Builder().addCategories("browser").build()</code>
+     *
+     *   // Record only the trace events matching the "blink*" and "renderer*" patterns
+     *   // (e.g. "blink.animations", "renderer_host" and "renderer.scheduler" categories).
+     *   <code>new TracingConfig.Builder().addCategories("blink*","renderer*").build()</code>
+     *
+     *   // Record events from the "web developer" predefined category set and events from
+     *   // the "disabled-by-default-v8.gc" category to understand where garbage collection
+     *   // is being triggered. Uses a limited size buffer for internal storage during tracing.
+     *   <code>new TracingConfig.Builder().addCategories(CATEGORIES_WEB_DEVELOPER)
      *                              .addCategories("disabled-by-default-v8.gc")
-     *                              .setTracingMode(RECORD_CONTINUOUSLY).build()
-     *       -- records events from the "web developer" predefined category set and events from
-     *          the "disabled-by-default-v8.gc" category to understand where garbage collection
-     *          is being triggered. Uses a ring buffer for internal storage during tracing.
+     *                              .setTracingMode(RECORD_UNTIL_FULL).build()</code>
+     * </pre>
      */
     public static class Builder {
         private @PredefinedCategories int mPredefinedCategories = CATEGORIES_NONE;
@@ -185,6 +198,8 @@
 
         /**
          * Build {@link TracingConfig} using the current settings.
+         *
+         * @return The {@link TracingConfig} with the current settings.
          */
         public TracingConfig build() {
             return new TracingConfig(mPredefinedCategories, mCustomIncludedCategories,
@@ -192,16 +207,15 @@
         }
 
         /**
-         * Adds categories from a predefined set of categories to be included in the trace output.
+         * Adds predefined sets of categories to be included in the trace output.
          *
-         * @param predefinedCategories list or bitmask of predefined category sets to use:
-         *                    {@link #CATEGORIES_NONE}, {@link #CATEGORIES_ALL},
-         *                    {@link #CATEGORIES_ANDROID_WEBVIEW},
-         *                    {@link #CATEGORIES_WEB_DEVELOPER},
-         *                    {@link #CATEGORIES_INPUT_LATENCY},
-         *                    {@link #CATEGORIES_RENDERING},
-         *                    {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
-         *                    {@link #CATEGORIES_FRAME_VIEWER}.
+         * A predefined category set can be one of {@link #CATEGORIES_NONE},
+         * {@link #CATEGORIES_ALL}, {@link #CATEGORIES_ANDROID_WEBVIEW},
+         * {@link #CATEGORIES_WEB_DEVELOPER}, {@link #CATEGORIES_INPUT_LATENCY},
+         * {@link #CATEGORIES_RENDERING}, {@link #CATEGORIES_JAVASCRIPT_AND_RENDERING} or
+         * {@link #CATEGORIES_FRAME_VIEWER}.
+         *
+         * @param predefinedCategories A list or bitmask of predefined category sets.
          * @return The builder to facilitate chaining.
          */
         public Builder addCategories(@PredefinedCategories int... predefinedCategories) {
@@ -215,11 +229,11 @@
          * Adds custom categories to be included in trace output.
          *
          * Note that the categories are defined by the currently-in-use version of WebView. They
-         * live in chromium code and are not part of the Android API. See
+         * live in chromium code and are not part of the Android API.
          * See <a href="https://www.chromium.org/developers/how-tos/trace-event-profiling-tool">
          * chromium documentation on tracing</a> for more details.
          *
-         * @param categories a list of category patterns. A category pattern can contain wilcards,
+         * @param categories A list of category patterns. A category pattern can contain wildcards,
          *        e.g. "blink*" or full category name e.g. "renderer.scheduler".
          * @return The builder to facilitate chaining.
          */
@@ -235,7 +249,7 @@
          *
          * Same as {@link #addCategories(String...)} but allows to pass a Collection as a parameter.
          *
-         * @param categories a list of category patters.
+         * @param categories A list of category patterns.
          * @return The builder to facilitate chaining.
          */
         public Builder addCategories(Collection<String> categories) {
@@ -245,8 +259,9 @@
 
         /**
          * Sets the tracing mode for this configuration.
+         * When tracingMode is not set explicitly, the default is {@link #RECORD_CONTINUOUSLY}.
          *
-         * @param tracingMode tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
+         * @param tracingMode The tracing mode to use, one of {@link #RECORD_UNTIL_FULL} or
          *                    {@link #RECORD_CONTINUOUSLY}.
          * @return The builder to facilitate chaining.
          */
diff --git a/core/java/android/webkit/TracingController.java b/core/java/android/webkit/TracingController.java
index 50068f5..05c0304 100644
--- a/core/java/android/webkit/TracingController.java
+++ b/core/java/android/webkit/TracingController.java
@@ -35,9 +35,9 @@
  * Example usage:
  * <pre class="prettyprint">
  * TracingController tracingController = TracingController.getInstance();
- * tracingController.start(new TraceConfig.Builder()
+ * tracingController.start(new TracingConfig.Builder()
  *                  .addCategories(CATEGORIES_WEB_DEVELOPER).build());
- * [..]
+ * ...
  * tracingController.stop(new FileOutputStream("trace.json"),
  *                        Executors.newSingleThreadExecutor());
  * </pre></p>
@@ -49,7 +49,7 @@
      * only one TracingController instance for all WebView instances,
      * however this restriction may be relaxed in a future Android release.
      *
-     * @return the default TracingController instance
+     * @return The default TracingController instance.
      */
     @NonNull
     public static TracingController getInstance() {
@@ -65,8 +65,10 @@
      * using an internal buffer and flushed to the outputStream when
      * {@link #stop(OutputStream, Executor)} is called.
      *
-     * @param tracingConfig configuration options to use for tracing
-     * @throws IllegalStateException if the system is already tracing.
+     * @param tracingConfig Configuration options to use for tracing.
+     * @throws IllegalStateException If the system is already tracing.
+     * @throws IllegalArgumentException If the configuration is invalid (e.g.
+     *         invalid category pattern or invalid tracing mode).
      */
     public abstract void start(@NonNull TracingConfig tracingConfig);
 
@@ -77,17 +79,22 @@
      * in chunks by invoking {@link java.io.OutputStream#write(byte[])}. On completion
      * the {@link java.io.OutputStream#close()} method is called.
      *
-     * @param outputStream the output steam the tracing data will be sent to. If null
+     * @param outputStream The output stream the tracing data will be sent to. If null
      *                     the tracing data will be discarded.
-     * @param executor the {@link java.util.concurrent.Executor} on which the
-     *        outputStream #write and #close methods will be invoked.
-     * @return false if the system was not tracing at the time of the call, true
-     *         otherwise.
+     * @param executor The {@link java.util.concurrent.Executor} on which the
+     *        outputStream {@link java.io.OutputStream#write(byte[])} and
+     *        {@link java.io.OutputStream#close()} methods will be invoked.
+     * @return False if the WebView framework was not tracing at the time of the call,
+     *         true otherwise.
      */
     public abstract boolean stop(@Nullable OutputStream outputStream,
             @NonNull @CallbackExecutor Executor executor);
 
-    /** True if the system is tracing */
+    /**
+     * Returns whether the WebView framework is tracing.
+     *
+     * @return True if tracing is enabled.
+     */
     public abstract boolean isTracing();
 
 }
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index ddba477..9ab057d 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -34,10 +34,6 @@
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Printer;
 import android.util.Slog;
@@ -129,11 +125,6 @@
     }
     // ----------------------------------------------------------------------
 
-    @Deprecated
-    public static boolean isSystemIme(@NonNull InputMethodInfo inputMethod) {
-        return inputMethod.isSystem();
-    }
-
     public static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi,
             final Context context, final boolean checkDefaultAttribute,
             @Nullable final Locale requiredLocale, final boolean checkCountry,
@@ -783,58 +774,6 @@
     }
 
     /**
-     * Parses the setting stored input methods and subtypes string value.
-     *
-     * @param inputMethodsAndSubtypesString The input method subtypes value stored in settings.
-     * @return Map from input method ID to set of input method subtypes IDs.
-     */
-    @VisibleForTesting
-    public static ArrayMap<String, ArraySet<String>> parseInputMethodsAndSubtypesString(
-            @Nullable final String inputMethodsAndSubtypesString) {
-
-        final ArrayMap<String, ArraySet<String>> imeMap = new ArrayMap<>();
-        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
-            return imeMap;
-        }
-
-        final SimpleStringSplitter typeSplitter =
-                new SimpleStringSplitter(INPUT_METHOD_SEPARATOR);
-        final SimpleStringSplitter subtypeSplitter =
-                new SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATOR);
-
-        List<Pair<String, ArrayList<String>>> allImeSettings =
-                InputMethodSettings.buildInputMethodsAndSubtypeList(inputMethodsAndSubtypesString,
-                        typeSplitter,
-                        subtypeSplitter);
-        for (Pair<String, ArrayList<String>> ime : allImeSettings) {
-            ArraySet<String> subtypes = new ArraySet<>();
-            if (ime.second != null) {
-                subtypes.addAll(ime.second);
-            }
-            imeMap.put(ime.first, subtypes);
-        }
-        return imeMap;
-    }
-
-    @NonNull
-    public static String buildInputMethodsAndSubtypesString(
-            @NonNull final ArrayMap<String, ArraySet<String>> map) {
-        // we want to use the canonical InputMethodSettings implementation,
-        // so we convert data structures first.
-        List<Pair<String, ArrayList<String>>> imeMap = new ArrayList<>(4);
-        for (ArrayMap.Entry<String, ArraySet<String>> entry : map.entrySet()) {
-            final String imeName = entry.getKey();
-            final ArraySet<String> subtypeSet = entry.getValue();
-            final ArrayList<String> subtypes = new ArrayList<>(2);
-            if (subtypeSet != null) {
-                subtypes.addAll(subtypeSet);
-            }
-            imeMap.add(new Pair<>(imeName, subtypes));
-        }
-        return InputMethodSettings.buildInputMethodsSettingString(imeMap);
-    }
-
-    /**
      * Utility class for putting and getting settings for InputMethod
      * TODO: Move all putters and getters of settings to this class.
      */
@@ -871,21 +810,7 @@
             }
         }
 
-        public static String buildInputMethodsSettingString(
-                List<Pair<String, ArrayList<String>>> allImeSettingsMap) {
-            final StringBuilder b = new StringBuilder();
-            boolean needsSeparator = false;
-            for (Pair<String, ArrayList<String>> ime : allImeSettingsMap) {
-                if (needsSeparator) {
-                    b.append(INPUT_METHOD_SEPARATOR);
-                }
-                buildEnabledInputMethodsSettingString(b, ime);
-                needsSeparator = true;
-            }
-            return b.toString();
-        }
-
-        public static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
+        private static List<Pair<String, ArrayList<String>>> buildInputMethodsAndSubtypeList(
                 String enabledInputMethodsStr,
                 TextUtils.SimpleStringSplitter inputMethodSplitter,
                 TextUtils.SimpleStringSplitter subtypeSplitter) {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 5d40a73..f537e3e 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -166,6 +166,11 @@
             return null;
         }
 
+        if (parsedArgs.hiddenApiAccessLogSampleRate != -1) {
+            handleHiddenApiAccessLogSampleRate(parsedArgs.hiddenApiAccessLogSampleRate);
+            return null;
+        }
+
         if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
             throw new ZygoteSecurityException("Client may not specify capabilities: " +
                     "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -294,6 +299,15 @@
         }
     }
 
+    private void handleHiddenApiAccessLogSampleRate(int percent) {
+        try {
+            ZygoteInit.setHiddenApiAccessLogSampleRate(percent);
+            mSocketOutStream.writeInt(0);
+        } catch (IOException ioe) {
+            throw new IllegalStateException("Error writing to command socket", ioe);
+        }
+    }
+
     protected void preload() {
         ZygoteInit.lazyPreload();
     }
@@ -461,6 +475,12 @@
         String[] apiBlacklistExemptions;
 
         /**
+         * Sampling rate for logging hidden API accesses to the event log. This is sent to the
+         * pre-forked zygote at boot time, or when it changes, via --hidden-api-log-sampling-rate.
+         */
+        int hiddenApiAccessLogSampleRate = -1;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -483,6 +503,7 @@
 
             boolean seenRuntimeArgs = false;
 
+            boolean expectRuntimeArgs = true;
             for ( /* curArg */ ; curArg < args.length; curArg++) {
                 String arg = args[curArg];
 
@@ -612,6 +633,7 @@
                     preloadPackageCacheKey = args[++curArg];
                 } else if (arg.equals("--preload-default")) {
                     preloadDefault = true;
+                    expectRuntimeArgs = false;
                 } else if (arg.equals("--start-child-zygote")) {
                     startChildZygote = true;
                 } else if (arg.equals("--set-api-blacklist-exemptions")) {
@@ -619,6 +641,16 @@
                     // with the regular fork command.
                     apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
                     curArg = args.length;
+                    expectRuntimeArgs = false;
+                } else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
+                    String rateStr = arg.substring(arg.indexOf('=') + 1);
+                    try {
+                        hiddenApiAccessLogSampleRate = Integer.parseInt(rateStr);
+                    } catch (NumberFormatException nfe) {
+                        throw new IllegalArgumentException(
+                                "Invalid log sampling rate: " + rateStr, nfe);
+                    }
+                    expectRuntimeArgs = false;
                 } else {
                     break;
                 }
@@ -633,7 +665,7 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
-            } else if (!preloadDefault && apiBlacklistExemptions == null) {
+            } else if (expectRuntimeArgs) {
                 if (!seenRuntimeArgs) {
                     throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
                 }
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c5d41db..6f58365 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -26,8 +26,8 @@
 import android.icu.util.ULocale;
 import android.opengl.EGL14;
 import android.os.Build;
-import android.os.IInstalld;
 import android.os.Environment;
+import android.os.IInstalld;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -44,16 +44,16 @@
 import android.system.StructCapUserData;
 import android.system.StructCapUserHeader;
 import android.text.Hyphenator;
-import android.util.TimingsTraceLog;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.TimingsTraceLog;
 import android.webkit.WebViewFactory;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
-
 import com.android.internal.util.Preconditions;
+
 import dalvik.system.DexFile;
 import dalvik.system.VMRuntime;
 import dalvik.system.ZygoteHooks;
@@ -67,8 +67,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.security.Security;
 import java.security.Provider;
+import java.security.Security;
 
 /**
  * Startup class for the zygote process.
@@ -518,6 +518,10 @@
         VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
     }
 
+    public static void setHiddenApiAccessLogSampleRate(int percent) {
+        VMRuntime.getRuntime().setHiddenApiAccessLogSamplingRate(percent);
+    }
+
     /**
      * Creates a PathClassLoader for the given class path that is associated with a shared
      * namespace, i.e., this classloader can access platform-private native libraries. The
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 24f2fbf..2b7221a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -64,6 +64,8 @@
             in NotificationVisibility[] noLongerVisibleKeys);
     void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded);
     void onNotificationDirectReplied(String key);
+    void onNotificationSmartRepliesAdded(in String key, in int replyCount);
+    void onNotificationSmartReplySent(in String key, in int replyIndex);
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int vis, int mask, String cause);
 
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index 9d79417..41a81ac 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -111,20 +111,13 @@
 }
 
 // Regular JNI
-static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jlong paintPtr,
-                           jint start, jint end, jint bidiFlags, jobject bounds) {
+static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end,
+                       jobject bounds) {
     ScopedCharArrayRO text(env, javaText);
     const minikin::U16StringPiece textBuffer(text.get(), text.size());
+    const minikin::Range range(start, end);
 
-    minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
-    Paint* paint = toPaint(paintPtr);
-    const Typeface* typeface = Typeface::resolveDefault(paint->getAndroidTypeface());
-    minikin::Layout layout = MinikinUtils::doLayout(paint,
-            static_cast<minikin::Bidi>(bidiFlags), typeface, textBuffer.data(), start, end - start,
-            textBuffer.size(), mt);
-
-    minikin::MinikinRect rect;
-    layout.getBounds(&rect);
+    minikin::MinikinRect rect = toMeasuredParagraph(ptr)->getBounds(textBuffer, range);
 
     SkRect r;
     r.fLeft = rect.mLeft;
@@ -156,7 +149,7 @@
 
     // MeasuredParagraph native functions.
     {"nGetWidth", "(JII)F", (void*) nGetWidth},  // Critical Natives
-    {"nGetBounds", "(J[CJIIILandroid/graphics/Rect;)V", (void*) nGetBounds},  // Regular JNI
+    {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds},  // Regular JNI
     {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},  // Critical Natives
     {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage},  // Critical Native
 };
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 7481f1c..a73939c 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -813,12 +813,6 @@
     proxy->notifyFramePending();
 }
 
-static void android_view_ThreadedRenderer_serializeDisplayListTree(JNIEnv* env, jobject clazz,
-        jlong proxyPtr) {
-    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    proxy->serializeDisplayListTree();
-}
-
 static void android_view_ThreadedRenderer_dumpProfileInfo(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject javaFileDescriptor, jint dumpFlags) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
@@ -1081,7 +1075,6 @@
     { "nFence", "(J)V", (void*) android_view_ThreadedRenderer_fence },
     { "nStopDrawing", "(J)V", (void*) android_view_ThreadedRenderer_stopDrawing },
     { "nNotifyFramePending", "(J)V", (void*) android_view_ThreadedRenderer_notifyFramePending },
-    { "nSerializeDisplayListTree", "(J)V", (void*) android_view_ThreadedRenderer_serializeDisplayListTree },
     { "nDumpProfileInfo", "(JLjava/io/FileDescriptor;I)V", (void*) android_view_ThreadedRenderer_dumpProfileInfo },
     { "setupShadersDiskCache", "(Ljava/lang/String;Ljava/lang/String;)V",
                 (void*) android_view_ThreadedRenderer_setupShadersDiskCache },
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index a8d0825..ee371c1 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -220,6 +220,7 @@
   optional float adjust_ime_amount = 10;
   optional float adjust_divider_amount = 11;
   optional .android.graphics.RectProto adjusted_bounds = 12;
+  optional bool animating_bounds = 13;
 }
 
 /* represents Task */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index da15506..c5ab2e6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -487,6 +487,7 @@
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
     <protected-broadcast android:name="android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION" />
+    <protected-broadcast android:name="android.telephony.action.SUBSCRIPTION_PLANS_CHANGED" />
 
     <protected-broadcast android:name="com.android.bluetooth.btservice.action.ALARM_WAKEUP" />
     <protected-broadcast android:name="com.android.server.action.NETWORK_STATS_POLL" />
diff --git a/core/res/res/drawable/ic_zen_24dp.xml b/core/res/res/drawable/ic_zen_24dp.xml
new file mode 100644
index 0000000..790bfb9
--- /dev/null
+++ b/core/res/res/drawable/ic_zen_24dp.xml
@@ -0,0 +1,26 @@
+<!--
+     Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:viewportHeight="24.0"
+        android:viewportWidth="24.0"
+        android:height="24dp"
+        android:width="24dp" >
+
+    <path
+        android:fillColor="?android:attr/colorControlActivated"
+        android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4 11H8c-.55 0-1-.45-1-1s.45-1 1-1h8c.55 0 1 .45 1 1s-.45 1-1 1z" />
+
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml
index 3196d00..b7395cd 100644
--- a/core/res/res/layout/notification_template_header.xml
+++ b/core/res/res/layout/notification_template_header.xml
@@ -138,7 +138,6 @@
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_camera"
-            android:tint="@color/notification_secondary_text_color_light"
             android:background="?android:selectableItemBackgroundBorderless"
             android:visibility="gone"
             />
@@ -147,7 +146,6 @@
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_mic"
-            android:tint="@color/notification_secondary_text_color_light"
             android:background="?android:selectableItemBackgroundBorderless"
             android:layout_marginStart="4dp"
             android:visibility="gone"
@@ -157,7 +155,6 @@
             android:layout_width="?attr/notificationHeaderIconSize"
             android:layout_height="?attr/notificationHeaderIconSize"
             android:src="@drawable/ic_alert_window_layer"
-            android:tint="@color/notification_secondary_text_color_light"
             android:background="?android:selectableItemBackgroundBorderless"
             android:layout_marginStart="4dp"
             android:visibility="gone"
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index aceba08..fe34d98 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2154,9 +2154,6 @@
             Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES</code>.
             -->
             <enum name="shortEdges" value="1" />
-            <!-- Use <code>shortEdges</code> instead. This is temporarily here to unblock pushing
-                 the SDK until all usages have been migrated to <code>shortEdges</code> -->
-            <enum name="always" value="1" />
             <!-- <p>
             The window is never allowed to overlap with the <code>DisplayCutout</code> area.
             <p>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 7048511..0fe80a1 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -38,4 +38,8 @@
     <color name="background_device_default_light">@color/background_material_light</color>
     <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color>
     <color name="background_floating_device_default_light">@color/background_floating_material_light</color>
+
+    <!-- Error color -->
+    <color name="error_color_device_default_dark">@color/error_color_material_dark</color>
+    <color name="error_color_device_default_light">@color/error_color_material_light</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4ce89c9..fc030ca 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1289,6 +1289,32 @@
          in darkness (although they may not be visible in a bright room). -->
     <integer name="config_screenBrightnessDark">1</integer>
 
+    <!-- Array of lux values to define the minimum brightness curve, which guarantees that any
+         brightness curve that dips below it is rejected by the system.
+         This prevents auto-brightness from setting the screen so dark as to prevent the user from
+         resetting or disabling it.
+
+         The values must be non-negative and strictly increasing, and correspond to the values in
+         the config_minimumBrightnessCurveNits array. -->
+    <array name="config_minimumBrightnessCurveLux">
+        <item>0.0</item>
+        <item>2000.0</item>
+        <item>4000.0</item>
+    </array>
+
+    <!-- Array of nits values to define the minimum brightness curve, which guarantees that any
+         brightness curve that dips below it is rejected by the system.
+         This should map lux to the absolute minimum nits that are still readable in that ambient
+         brightness.
+
+         The values must be non-negative and non-decreasing, and correspond to the values in the
+         config_minimumBrightnessCurveLux array. -->
+    <array name="config_minimumBrightnessCurveNits">
+        <item>0.0</item>
+        <item>50.0</item>
+        <item>90.0</item>
+    </array>
+
     <!-- Array of light sensor lux values to define our levels for auto backlight brightness support.
          The N entries of this array define N + 1 control points as follows:
          (1-based arrays)
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 715be5b..bd824de 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4515,7 +4515,7 @@
     <string name="package_deleted_device_owner">Deleted by your admin</string>
 
     <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
-    <string name="battery_saver_description">To extend battery life, Battery Saver reduces your device\'s performance and limits or turns off vibration, location services, and background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.\n\nBattery Saver turns off automatically when your device is charging.</string>
+    <string name="battery_saver_description">To extend your battery life, Battery Saver turns off some device features and restricts apps.</string>
 
     <!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
     <string name="data_saver_description">To help reduce data usage, Data Saver prevents some apps from sending or receiving data in the background. An app you’re currently using can access data, but may do so less frequently. This may mean, for example, that images don’t display until you tap them.</string>
@@ -4943,9 +4943,9 @@
     <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
     <string name="notification_channel_do_not_disturb">Do Not Disturb</string>
     <!-- Title of notification indicating do not disturb visual interruption settings have changed when upgrading to P -->
-    <string name="zen_upgrade_notification_visd_title">Do Not Disturb is hiding notifications to help you focus</string>
+    <string name="zen_upgrade_notification_visd_title">New: Do Not Disturb is hiding notifications</string>
     <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
-    <string name="zen_upgrade_notification_visd_content">This is new behavior. Tap to change.</string>
+    <string name="zen_upgrade_notification_visd_content">Tap to learn more and change.</string>
     <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
     <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
     <!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ce7f1ff..b6f0b70 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1810,6 +1810,8 @@
   <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
+  <java-symbol type="array" name="config_minimumBrightnessCurveLux" />
+  <java-symbol type="array" name="config_minimumBrightnessCurveNits" />
   <java-symbol type="array" name="config_protectedNetworks" />
   <java-symbol type="array" name="config_statusBarIcons" />
   <java-symbol type="array" name="config_tether_bluetooth_regexs" />
@@ -2547,6 +2549,7 @@
   <java-symbol type="drawable" name="ic_settings_24dp" />
   <java-symbol type="drawable" name="ic_storage_48dp" />
   <java-symbol type="drawable" name="ic_usb_48dp" />
+  <java-symbol type="drawable" name="ic_zen_24dp" />
 
   <!-- Floating toolbar -->
   <java-symbol type="id" name="floating_toolbar_menu_item_image" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index fd6cc95..92b2f33 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -210,6 +210,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
     </style>
 
@@ -221,6 +222,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -248,6 +250,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -277,6 +280,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -305,6 +309,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -348,6 +353,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -368,6 +374,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -394,6 +401,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -421,6 +429,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -464,6 +473,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -492,6 +502,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -518,6 +529,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -546,6 +558,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -573,6 +586,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -600,6 +614,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -627,6 +642,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -654,6 +670,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -685,6 +702,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Text styles -->
         <item name="textAppearanceButton">@style/TextAppearance.DeviceDefault.Widget.Button</item>
@@ -706,6 +724,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -731,6 +750,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -908,6 +928,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -917,6 +938,7 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -943,6 +965,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -970,6 +993,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -999,6 +1023,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1027,6 +1052,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1072,6 +1098,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
@@ -1091,6 +1118,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1120,6 +1148,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1150,6 +1179,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1181,6 +1211,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -1194,6 +1225,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -1206,6 +1238,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1237,6 +1270,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1266,6 +1300,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1294,6 +1329,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1321,6 +1357,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1346,6 +1383,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1371,6 +1409,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1404,6 +1443,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorEdgeEffect">@android:color/black</item>
 
         <!-- Add white nav bar with divider that matches material -->
@@ -1438,6 +1478,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
 
@@ -1465,6 +1506,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings_light</item>
         <item name="colorSecondary">@color/secondary_device_default_settings_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Light.Dialog.Alert</item>
 
         <!-- Progress bar attributes -->
@@ -1482,6 +1524,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorError">@color/error_color_device_default_dark</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1511,6 +1554,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1540,6 +1584,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1569,6 +1614,7 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Dialog attributes -->
         <item name="dialogCornerRadius">@dimen/config_dialogCornerRadius</item>
@@ -1617,6 +1663,7 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorError">@color/error_color_device_default_light</item>
 
         <!-- Progress bar attributes -->
         <item name="colorProgressBackgroundNormal">@color/config_progress_background_tint</item>
diff --git a/core/res/res/xml/default_zen_mode_config.xml b/core/res/res/xml/default_zen_mode_config.xml
index f1b61a7..dce8a65 100644
--- a/core/res/res/xml/default_zen_mode_config.xml
+++ b/core/res/res/xml/default_zen_mode_config.xml
@@ -18,7 +18,7 @@
 -->
 
 <!-- Default configuration for zen mode.  See android.service.notification.ZenModeConfig. -->
-<zen version="6">
+<zen version="7">
     <allow alarms="true" media="true" system="false" calls="false" messages="false" reminders="false"
            events="false" />
     <!-- all visual effects that exist as of P -->
diff --git a/core/tests/coretests/AndroidTest.xml b/core/tests/coretests/AndroidTest.xml
index 7b5ad9a..68ef34b 100644
--- a/core/tests/coretests/AndroidTest.xml
+++ b/core/tests/coretests/AndroidTest.xml
@@ -25,5 +25,6 @@
     <option name="test-tag" value="FrameworksCoreTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.coretests" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
new file mode 100644
index 0000000..655efb5
--- /dev/null
+++ b/core/tests/coretests/src/android/graphics/drawable/DrawableWrapperTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Xfermode;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class DrawableWrapperTest {
+
+    static class MyWrapper extends DrawableWrapper {
+        MyWrapper(Drawable dr) {
+          super(dr);
+        }
+    }
+
+    /**
+     * Test {@link Drawable#setXfermode(Xfermode)} which is marked
+     * with the hide annotation
+     */
+    @Test
+    public void testSetXfermode() {
+        CacheXfermodeDrawable xferModeDrawable = new CacheXfermodeDrawable();
+        DrawableWrapper wrapper = new MyWrapper(xferModeDrawable);
+        PorterDuffXfermode mode = new PorterDuffXfermode(Mode.MULTIPLY);
+        wrapper.setXfermode(mode);
+        assertSame(xferModeDrawable, wrapper.getDrawable());
+        assertEquals(mode, xferModeDrawable.mXferMode);
+    }
+
+    private static class CacheXfermodeDrawable extends Drawable {
+
+        private Xfermode mXferMode = null;
+
+        @Override
+        public void draw(Canvas canvas) {
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter colorFilter) {
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        @Override
+        public void setXfermode(Xfermode mode) {
+            mXferMode = mode;
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
index bad0d25..5ef30a8 100644
--- a/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
+++ b/core/tests/coretests/src/android/hardware/display/BrightnessConfigurationTest.java
@@ -41,7 +41,7 @@
     };
 
     private static final float[] NITS_LEVELS = {
-        0.5f,
+        1f,
         90f,
         100f,
     };
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 613ab6d..96ac589 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -60,7 +60,6 @@
                     Settings.System.ALARM_ALERT, // backup candidate?
                     Settings.System.ALARM_ALERT_CACHE, // internal cache
                     Settings.System.APPEND_FOR_LAST_AUDIBLE, // suffix deprecated since API 2
-                    Settings.System.DISPLAY_COLOR_MODE, // bug?
                     Settings.System.EGG_MODE, // I am the lolrus
                     Settings.System.END_BUTTON_BEHAVIOR, // bug?
                     Settings.System
@@ -248,6 +247,10 @@
                     Settings.Global.HDMI_CONTROL_ENABLED,
                     Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                     Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                    Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE,
+                    Settings.Global.HIDDEN_API_POLICY_P_APPS,
+                    Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS,
+                    Settings.Global.HIDE_ERROR_DIALOGS,
                     Settings.Global.HTTP_PROXY,
                     HYBRID_SYSUI_BATTERY_WARNING_FLAGS,
                     Settings.Global.INET_CONDITION_DEBOUNCE_DOWN_DELAY,
diff --git a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
index c205f96..6c05601 100644
--- a/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannableStringNoCopyTest.java
@@ -54,7 +54,7 @@
         first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
 
         // Do not copy NoCopySpan if specified so.
-        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final SpannedString copied = new SpannedString(first, true /* ignoreNoCopySpan */);
         final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
         assertNotNull(spans);
         assertEquals(2, spans.length);
@@ -87,7 +87,7 @@
 
         // Do not copy NoCopySpan if specified so.
         final SpannedString copied = new SpannedString(
-                new CustomSpannable(first), false /* copyNoCopySpan */);
+                new CustomSpannable(first), true /* ignoreNoCopySpan */);
         final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
         assertNotNull(spans);
         assertEquals(2, spans.length);
diff --git a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
index 0680924..380e315 100644
--- a/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
+++ b/core/tests/coretests/src/android/text/SpannedStringNoCopyTest.java
@@ -54,7 +54,7 @@
         first.setSpan(new UnderlineSpan(), 0, first.length(), Spanned.SPAN_PRIORITY);
 
         // Do not copy NoCopySpan if specified so.
-        final SpannedString copied = new SpannedString(first, false /* copyNoCopySpan */);
+        final SpannedString copied = new SpannedString(first, true /* ignoreNoCopySpan */);
         final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
         assertNotNull(spans);
         assertEquals(2, spans.length);
@@ -87,7 +87,7 @@
 
         // Do not copy NoCopySpan if specified so.
         final SpannedString copied = new SpannedString(
-                new CustomSpanned(first), false /* copyNoCopySpan */);
+                new CustomSpanned(first), true /* ignoreNoCopySpan */);
         final Object[] spans = copied.getSpans(0, copied.length(), Object.class);
         assertNotNull(spans);
         assertEquals(2, spans.length);
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 04d2dad..c02d97c 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -16,12 +16,14 @@
 
 package android.text.format;
 
+import static android.text.format.Formatter.FLAG_IEC_UNITS;
+import static android.text.format.Formatter.FLAG_SI_UNITS;
+
 import static org.junit.Assert.assertEquals;
 
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.icu.util.MeasureUnit;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -34,7 +36,6 @@
 import org.junit.runner.RunWith;
 
 import java.util.Locale;
-import java.util.Set;
 
 @Presubmit
 @SmallTest
@@ -104,6 +105,27 @@
         checkFormatBytes(9123000, false, "9,12", 9120000);
     }
 
+    @Test
+    public void testFormatBytesSi() {
+        setLocale(Locale.US);
+
+        checkFormatBytes(1_000, FLAG_SI_UNITS, "1.00", 1_000);
+        checkFormatBytes(1_024, FLAG_SI_UNITS, "1.02", 1_020);
+        checkFormatBytes(1_500, FLAG_SI_UNITS, "1.50", 1_500);
+        checkFormatBytes(12_582_912L, FLAG_SI_UNITS, "12.58", 12_580_000L);
+    }
+
+    @Test
+    public void testFormatBytesIec() {
+        setLocale(Locale.US);
+
+        checkFormatBytes(1_000, FLAG_IEC_UNITS, "0.98", 1_003);
+        checkFormatBytes(1_024, FLAG_IEC_UNITS, "1.00", 1_024);
+        checkFormatBytes(1_500, FLAG_IEC_UNITS, "1.46", 1_495);
+        checkFormatBytes(12_500_000L, FLAG_IEC_UNITS, "11.92", 12_499_025L);
+        checkFormatBytes(12_582_912L, FLAG_IEC_UNITS, "12.00", 12_582_912L);
+    }
+
     private static final long SECOND = 1000;
     private static final long MINUTE = 60 * SECOND;
     private static final long HOUR = 60 * MINUTE;
@@ -195,8 +217,14 @@
 
     private void checkFormatBytes(long bytes, boolean useShort,
             String expectedString, long expectedRounded) {
+        checkFormatBytes(bytes, (useShort ? Formatter.FLAG_SHORTER : 0),
+                expectedString, expectedRounded);
+    }
+
+    private void checkFormatBytes(long bytes, int flags,
+            String expectedString, long expectedRounded) {
         BytesResult r = Formatter.formatBytes(mContext.getResources(), bytes,
-                Formatter.FLAG_CALCULATE_ROUNDED | (useShort ? Formatter.FLAG_SHORTER : 0));
+                Formatter.FLAG_CALCULATE_ROUNDED | flags);
         assertEquals(expectedString, r.value);
         assertEquals(expectedRounded, r.roundedBytes);
     }
diff --git a/core/tests/coretests/src/android/text/util/LinkifyTest.java b/core/tests/coretests/src/android/text/util/LinkifyTest.java
index 2fe7db4..be3a0be 100644
--- a/core/tests/coretests/src/android/text/util/LinkifyTest.java
+++ b/core/tests/coretests/src/android/text/util/LinkifyTest.java
@@ -31,11 +31,6 @@
 import android.text.method.LinkMovementMethod;
 import android.text.style.URLSpan;
 import android.util.Patterns;
-import android.view.textclassifier.SystemTextClassifier;
-import android.view.textclassifier.TextClassificationConstants;
-import android.view.textclassifier.TextClassifier;
-import android.view.textclassifier.TextClassifierImpl;
-import android.view.textclassifier.TextLinks.TextLinkSpan;
 import android.widget.TextView;
 
 import org.junit.After;
@@ -43,11 +38,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 
 /**
  * LinkifyTest tests {@link Linkify}.
@@ -158,55 +149,4 @@
         assertEquals("android.com should be linkified", 1, spans.length);
         assertEquals("https://android.com", spans[0].getURL());
     }
-
-    @Test
-    public void testAddLinksAsync_useLegacyIfSmartDisabled_localTextClassifier()
-            throws Exception {
-        final TextClassificationConstants settings =
-                TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
-        final TextClassifier classifier = new TextClassifierImpl(mContext, settings);
-        testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
-    }
-
-    @Test
-    public void testAddLinksAsync_useLegacyIfSmartDisabled_systemTextClassifier()
-            throws Exception {
-        final TextClassificationConstants settings =
-                TextClassificationConstants.loadFromString("smart_linkify_enabled=false");
-        final TextClassifier classifier = new SystemTextClassifier(mContext, settings);
-        testAddLinksAsync_useLegacyIfSmartDisabled(classifier);
-    }
-
-    private void testAddLinksAsync_useLegacyIfSmartDisabled(TextClassifier classifier)
-            throws Exception {
-        final int linkMask = Linkify.EMAIL_ADDRESSES | Linkify.PHONE_NUMBERS | Linkify.WEB_URLS;
-        final String string = "example@android.com\n"
-                + "(415) 555-1212\n"
-                + "http://android.com\n"
-                + "100 Android Rd California";
-        final Spannable expected = new SpannableString(string);
-        final Spannable actual = new SpannableString(string);
-
-        Linkify.addLinks(expected, linkMask);  // legacy.
-        Linkify.addLinksAsync(actual, classifier, linkMask).get();
-
-        final URLSpan[] expectedSpans = expected.getSpans(0, expected.length(), URLSpan.class);
-        final TextLinkSpan[] actualSpans = actual.getSpans(0, actual.length(), TextLinkSpan.class);
-        assertEquals(expectedSpans.length, actualSpans.length);
-        final Set<List> expectedLinkSpec = new HashSet<>();
-        final Set<List> actualLinkSpec = new HashSet<>();
-        for (int i = 0; i < expectedSpans.length; i++) {
-            final URLSpan expectedSpan = expectedSpans[i];
-            final TextLinkSpan actualSpan = actualSpans[i];
-            expectedLinkSpec.add(Arrays.asList(
-                    expected.getSpanStart(expectedSpan),
-                    expected.getSpanEnd(expectedSpan),
-                    expectedSpan.getURL()));
-            actualLinkSpec.add(Arrays.asList(
-                    actual.getSpanStart(actualSpan),
-                    actual.getSpanEnd(actualSpan),
-                    actualSpan.getUrl()));
-        }
-        assertEquals(expectedLinkSpec, actualLinkSpec);
-    }
 }
diff --git a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
index 42b6048..39d492d 100644
--- a/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
+++ b/core/tests/coretests/src/android/util/RecurrenceRuleTest.java
@@ -57,13 +57,13 @@
 
         assertTrue(r.isMonthly());
 
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
         assertTrue(it.hasNext());
-        assertEquals(Pair.create(
+        assertEquals(new Range<>(
                 ZonedDateTime.parse("2015-11-14T00:00:00.00Z"),
                 ZonedDateTime.parse("2015-12-14T00:00:00.00Z")), it.next());
         assertTrue(it.hasNext());
-        assertEquals(Pair.create(
+        assertEquals(new Range<>(
                 ZonedDateTime.parse("2015-10-14T00:00:00.00Z"),
                 ZonedDateTime.parse("2015-11-14T00:00:00.00Z")), it.next());
     }
@@ -77,13 +77,13 @@
 
         assertFalse(r.isMonthly());
 
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
         assertTrue(it.hasNext());
-        assertEquals(Pair.create(
+        assertEquals(new Range<>(
                 ZonedDateTime.parse("2010-11-17T00:11:00.00Z"),
                 ZonedDateTime.parse("2010-11-20T00:11:00.00Z")), it.next());
         assertTrue(it.hasNext());
-        assertEquals(Pair.create(
+        assertEquals(new Range<>(
                 ZonedDateTime.parse("2010-11-14T00:11:00.00Z"),
                 ZonedDateTime.parse("2010-11-17T00:11:00.00Z")), it.next());
         assertFalse(it.hasNext());
@@ -98,9 +98,9 @@
 
         assertFalse(r.isMonthly());
 
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
         assertTrue(it.hasNext());
-        assertEquals(Pair.create(
+        assertEquals(new Range<>(
                 ZonedDateTime.parse("2010-11-14T00:11:00.000Z"),
                 ZonedDateTime.parse("2010-11-20T00:11:00.000Z")), it.next());
         assertFalse(it.hasNext());
@@ -112,7 +112,7 @@
 
         assertFalse(r.isMonthly());
 
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
         assertFalse(it.hasNext());
     }
 
@@ -122,22 +122,22 @@
                 ZonedDateTime.parse("2030-01-31T00:00:00.000Z"),
                 Period.ofMonths(1));
 
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = r.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = r.cycleIterator();
         ZonedDateTime lastStart = null;
         int months = 0;
         while (it.hasNext()) {
-            final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
+            final Range<ZonedDateTime> cycle = it.next();
 
             // Make sure cycle has reasonable length
-            final long length = cycle.second.toEpochSecond() - cycle.first.toEpochSecond();
+            final long length = cycle.getUpper().toEpochSecond() - cycle.getLower().toEpochSecond();
             assertTrue(cycle + " must be more than 4 weeks", length >= 2419200);
             assertTrue(cycle + " must be less than 5 weeks", length <= 3024000);
 
             // Make sure we have no gaps
             if (lastStart != null) {
-                assertEquals(lastStart, cycle.second);
+                assertEquals(lastStart, cycle.getUpper());
             }
-            lastStart = cycle.first;
+            lastStart = cycle.getLower();
             months++;
         }
 
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index 6e9401d..6ee74cb 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -208,6 +208,12 @@
     }
 
     @Test
+    public void fromSpec_wontCacheIfScreenHeightChanges() {
+        DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 4000, 1f);
+        assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
+    }
+
+    @Test
     public void fromSpec_wontCacheIfDensityChanges() {
         DisplayCutout cached = fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 2f);
         assertThat(fromSpec("L1,0 L1,1 L0,1 z", 200, 400, 1f), not(sameInstance(cached)));
diff --git a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
index 1fd24e3..a70dae8 100644
--- a/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/inputmethod/InputMethodUtilsTest.java
@@ -40,8 +40,6 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
-import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
@@ -1192,248 +1190,6 @@
     }
 
     @Test
-    public void testParseInputMethodsAndSubtypesString() {
-        // Trivial cases.
-        {
-            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString("").isEmpty());
-            assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString(null).isEmpty());
-        }
-
-        // No subtype cases.
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0");
-            assertEquals(1, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.get("ime0").isEmpty());
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0:ime1");
-            assertEquals(2, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.get("ime0").isEmpty());
-            assertTrue(r.containsKey("ime1"));
-            assertTrue(r.get("ime1").isEmpty());
-        }
-
-        // Input metho IDs and their subtypes.
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0");
-            assertEquals(1, r.size());
-            assertTrue(r.containsKey("ime0"));
-            ArraySet<String> subtypes = r.get("ime0");
-            assertEquals(1, subtypes.size());
-            assertTrue(subtypes.contains("subtype0"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype0");
-            assertEquals(1, r.size());
-            assertTrue(r.containsKey("ime0"));
-            ArraySet<String> subtypes = r.get("ime0");
-            assertEquals(1, subtypes.size());
-            assertTrue(subtypes.contains("subtype0"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype1");
-            assertEquals(1, r.size());
-            assertTrue(r.containsKey("ime0"));
-            ArraySet<String> subtypes = r.get("ime0");
-            assertEquals(2, subtypes.size());
-            assertTrue(subtypes.contains("subtype0"));
-            assertTrue(subtypes.contains("subtype1"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString(
-                            "ime0;subtype0:ime1;subtype1");
-            assertEquals(2, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.containsKey("ime1"));
-            ArraySet<String> subtypes0 = r.get("ime0");
-            assertEquals(1, subtypes0.size());
-            assertTrue(subtypes0.contains("subtype0"));
-
-            ArraySet<String> subtypes1 = r.get("ime1");
-            assertEquals(1, subtypes1.size());
-            assertTrue(subtypes1.contains("subtype1"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString(
-                            "ime0;subtype0;subtype1:ime1;subtype2");
-            assertEquals(2, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.containsKey("ime1"));
-            ArraySet<String> subtypes0 = r.get("ime0");
-            assertEquals(2, subtypes0.size());
-            assertTrue(subtypes0.contains("subtype0"));
-            assertTrue(subtypes0.contains("subtype1"));
-
-            ArraySet<String> subtypes1 = r.get("ime1");
-            assertEquals(1, subtypes1.size());
-            assertTrue(subtypes1.contains("subtype2"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString(
-                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2");
-            assertEquals(2, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.containsKey("ime1"));
-            ArraySet<String> subtypes0 = r.get("ime0");
-            assertEquals(2, subtypes0.size());
-            assertTrue(subtypes0.contains("subtype0"));
-            assertTrue(subtypes0.contains("subtype1"));
-
-            ArraySet<String> subtypes1 = r.get("ime1");
-            assertEquals(2, subtypes1.size());
-            assertTrue(subtypes0.contains("subtype1"));
-            assertTrue(subtypes1.contains("subtype2"));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> r =
-                    InputMethodUtils.parseInputMethodsAndSubtypesString(
-                            "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
-            assertEquals(3, r.size());
-            assertTrue(r.containsKey("ime0"));
-            assertTrue(r.containsKey("ime1"));
-            assertTrue(r.containsKey("ime2"));
-            ArraySet<String> subtypes0 = r.get("ime0");
-            assertEquals(2, subtypes0.size());
-            assertTrue(subtypes0.contains("subtype0"));
-            assertTrue(subtypes0.contains("subtype1"));
-
-            ArraySet<String> subtypes1 = r.get("ime1");
-            assertEquals(2, subtypes1.size());
-            assertTrue(subtypes0.contains("subtype1"));
-            assertTrue(subtypes1.contains("subtype2"));
-
-            ArraySet<String> subtypes2 = r.get("ime2");
-            assertTrue(subtypes2.isEmpty());
-        }
-    }
-
-    @Test
-    public void testbuildInputMethodsAndSubtypesString() {
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            map.put("ime0", new ArraySet<>());
-            assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            map.put("ime0", subtypes1);
-            assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            subtypes1.add("subtype1");
-            map.put("ime0", subtypes1);
-
-            // We do not expect what order will be used to concatenate items in
-            // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible
-            // permutations here.
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0;subtype0;subtype1");
-            validSequences.add("ime0;subtype1;subtype0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            map.put("ime0", new ArraySet<>());
-            map.put("ime1", new ArraySet<>());
-
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0:ime1");
-            validSequences.add("ime1:ime0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            map.put("ime0", subtypes1);
-            map.put("ime1", new ArraySet<>());
-
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0;subtype0:ime1");
-            validSequences.add("ime1;ime0;subtype0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            subtypes1.add("subtype1");
-            map.put("ime0", subtypes1);
-            map.put("ime1", new ArraySet<>());
-
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0;subtype0;subtype1:ime1");
-            validSequences.add("ime0;subtype1;subtype0:ime1");
-            validSequences.add("ime1:ime0;subtype0;subtype1");
-            validSequences.add("ime1:ime0;subtype1;subtype0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            map.put("ime0", subtypes1);
-
-            ArraySet<String> subtypes2 = new ArraySet<>();
-            subtypes2.add("subtype1");
-            map.put("ime1", subtypes2);
-
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0;subtype0:ime1;subtype1");
-            validSequences.add("ime1;subtype1:ime0;subtype0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-        {
-            ArrayMap<String, ArraySet<String>> map = new ArrayMap<>();
-            ArraySet<String> subtypes1 = new ArraySet<>();
-            subtypes1.add("subtype0");
-            subtypes1.add("subtype1");
-            map.put("ime0", subtypes1);
-
-            ArraySet<String> subtypes2 = new ArraySet<>();
-            subtypes2.add("subtype2");
-            subtypes2.add("subtype3");
-            map.put("ime1", subtypes2);
-
-            ArraySet<String> validSequences = new ArraySet<>();
-            validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3");
-            validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3");
-            validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2");
-            validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2");
-            validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1");
-            validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1");
-            validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0");
-            validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0");
-            assertTrue(validSequences.contains(
-                    InputMethodUtils.buildInputMethodsAndSubtypesString(map)));
-        }
-    }
-
-    @Test
     public void testConstructLocaleFromString() throws Exception {
         assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en"));
         assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US"));
diff --git a/core/tests/privacytests/AndroidTest.xml b/core/tests/privacytests/AndroidTest.xml
index 8098c14..a3e785c 100644
--- a/core/tests/privacytests/AndroidTest.xml
+++ b/core/tests/privacytests/AndroidTest.xml
@@ -23,5 +23,6 @@
 
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.coretests.privacy" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/core/tests/utiltests/AndroidTest.xml b/core/tests/utiltests/AndroidTest.xml
index 270d773..f32acb4 100644
--- a/core/tests/utiltests/AndroidTest.xml
+++ b/core/tests/utiltests/AndroidTest.xml
@@ -25,5 +25,6 @@
     <option name="test-tag" value="FrameworksUtilTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.utiltests" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 22867df..72d9bce 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -111,6 +111,8 @@
     <family lang="und-Ethi">
         <font weight="400" style="normal">NotoSansEthiopic-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansEthiopic-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifEthiopic-Regular.otf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifEthiopic-Bold.otf</font>
     </family>
     <family lang="und-Hebr">
         <font weight="400" style="normal">NotoSansHebrew-Regular.ttf</font>
@@ -167,6 +169,8 @@
     <family lang="und-Guru" variant="elegant">
         <font weight="400" style="normal">NotoSansGurmukhi-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansGurmukhi-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Regular.otf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifGurmukhi-Bold.otf</font>
     </family>
     <family lang="und-Guru" variant="compact">
         <font weight="400" style="normal">NotoSansGurmukhiUI-Regular.ttf</font>
@@ -231,9 +235,15 @@
         <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font>
     </family>
 
-    <family lang="und-Sinh">
+    <family lang="und-Sinh" variant="elegant">
         <font weight="400" style="normal">NotoSansSinhala-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansSinhala-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifSinhala-Regular.otf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifSinhala-Bold.otf</font>
+    </family>
+    <family lang="und-Sinh" variant="compact">
+        <font weight="400" style="normal">NotoSansSinhalaUI-Regular.otf</font>
+        <font weight="700" style="normal">NotoSansSinhalaUI-Bold.otf</font>
     </family>
     <family lang="und-Khmr" variant="elegant">
         <font weight="100" style="normal">NotoSansKhmer-VF.ttf
@@ -272,7 +282,9 @@
             <axis tag="wdth" stylevalue="100.0" />
             <axis tag="wght" stylevalue="190.0" />
         </font>
-    </family>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font>
+      </family>
     <family lang="und-Khmr" variant="compact">
         <font weight="400" style="normal">NotoSansKhmerUI-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font>
@@ -290,6 +302,8 @@
     <family lang="und-Mymr" variant="elegant">
         <font weight="400" style="normal">NotoSansMyanmar-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansMyanmar-Bold.ttf</font>
+        <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font>
+        <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font>
     </family>
     <family lang="und-Mymr" variant="compact">
         <font weight="400" style="normal">NotoSansMyanmarUI-Regular.ttf</font>
@@ -303,6 +317,9 @@
         <font weight="400" style="normal">NotoSansCham-Regular.ttf</font>
         <font weight="700" style="normal">NotoSansCham-Bold.ttf</font>
     </family>
+    <family lang="und-Ahom">
+        <font weight="400" style="normal">NotoSansAhom-Regular.otf</font>
+    </family>
     <family lang="und-Adlm">
         <font weight="400" style="normal">NotoSansAdlam-Regular.ttf</font>
     </family>
@@ -354,6 +371,9 @@
     <family lang="und-Egyp">
         <font weight="400" style="normal">NotoSansEgyptianHieroglyphs-Regular.ttf</font>
     </family>
+    <family lang="und-Elba">
+        <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font>
+    </family>
     <family lang="und-Glag">
         <font weight="400" style="normal">NotoSansGlagolitic-Regular.ttf</font>
     </family>
@@ -538,4 +558,64 @@
     <family lang="und-Phag">
         <font weight="400" style="normal">NotoSansPhagsPa-Regular.ttf</font>
     </family>
+    <family lang="und-Hluw">
+        <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font>
+    </family>
+    <family lang="und-Bass">
+        <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font>
+    </family>
+    <family lang="und-Bhks">
+        <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font>
+    </family>
+    <family lang="und-Hatr">
+        <font weight="400" style="normal">NotoSansHatran-Regular.otf</font>
+    </family>
+    <family lang="und-Lina">
+        <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font>
+    </family>
+    <family lang="und-Mani">
+        <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font>
+    </family>
+    <family lang="und-Marc">
+        <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font>
+    </family>
+    <family lang="und-Merc">
+        <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font>
+    </family>
+    <family lang="und-Plrd">
+        <font weight="400" style="normal">NotoSansMiao-Regular.otf</font>
+    </family>
+    <family lang="und-Mroo">
+        <font weight="400" style="normal">NotoSansMro-Regular.otf</font>
+    </family>
+    <family lang="und-Mult">
+        <font weight="400" style="normal">NotoSansMultani-Regular.otf</font>
+    </family>
+    <family lang="und-Nbat">
+        <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font>
+    </family>
+    <family lang="und-Newa">
+        <font weight="400" style="normal">NotoSansNewa-Regular.otf</font>
+    </family>
+    <family lang="und-Narb">
+        <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font>
+    </family>
+    <family lang="und-Perm">
+        <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font>
+    </family>
+    <family lang="und-Hmng">
+        <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font>
+    </family>
+    <family lang="und-Palm">
+        <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font>
+    </family>
+    <family lang="und-Pauc">
+        <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font>
+    </family>
+    <family lang="und-Shrd">
+        <font weight="400" style="normal">NotoSansSharada-Regular.otf</font>
+    </family>
+    <family lang="und-Sora">
+        <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font>
+    </family>
 </familyset>
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 01d5825..88a6322 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -35,8 +35,6 @@
      * @param mode The porter-duff mode that is applied
      *
      * @see Color
-     * @see #setColor(int)
-     * @see #setMode(android.graphics.PorterDuff.Mode)
      */
     public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         mColor = color;
@@ -48,7 +46,6 @@
      * is applied.
      *
      * @see Color
-     * @see #setColor(int)
      *
      * @hide
      */
@@ -58,30 +55,10 @@
     }
 
     /**
-     * Specifies the color to tint the source pixels with when this color
-     * filter is applied.
-     *
-     * @param color An ARGB {@link Color color}
-     *
-     * @see Color
-     * @see #getColor()
-     * @see #getMode()
-     *
-     * @hide
-     */
-    public void setColor(@ColorInt int color) {
-        if (mColor != color) {
-            mColor = color;
-            discardNativeInstance();
-        }
-    }
-
-    /**
      * Returns the Porter-Duff mode used to composite this color filter's
      * color with the source pixel when this filter is applied.
      *
      * @see PorterDuff
-     * @see #setMode(android.graphics.PorterDuff.Mode)
      *
      * @hide
      */
@@ -89,24 +66,6 @@
         return mMode;
     }
 
-    /**
-     * Specifies the Porter-Duff mode to use when compositing this color
-     * filter's color with the source pixel at draw time.
-     *
-     * @see PorterDuff
-     * @see #getMode()
-     * @see #getColor()
-     *
-     * @hide
-     */
-    public void setMode(@NonNull PorterDuff.Mode mode) {
-        if (mode == null) {
-            throw new IllegalArgumentException("mode must be non-null");
-        }
-        mMode = mode;
-        discardNativeInstance();
-    }
-
     @Override
     long createNativeInstance() {
         return native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 8af2fd8..b77d74a 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -16,11 +16,6 @@
 
 package android.graphics.drawable;
 
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.AttrRes;
 import android.annotation.ColorInt;
 import android.annotation.IntRange;
@@ -57,6 +52,11 @@
 import android.util.Xml;
 import android.view.View;
 
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -1516,12 +1516,11 @@
         }
 
         final int color = tint.getColorForState(getState(), Color.TRANSPARENT);
-        if (tintFilter == null) {
+        if (tintFilter == null || tintFilter.getColor() != color
+                || tintFilter.getMode() != tintMode) {
             return new PorterDuffColorFilter(color, tintMode);
         }
 
-        tintFilter.setColor(color);
-        tintFilter.setMode(tintMode);
         return tintFilter;
     }
 
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index cf821bb..a9e0f9a 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -16,11 +16,6 @@
 
 package android.graphics.drawable;
 
-import com.android.internal.R;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.pm.ActivityInfo.Config;
@@ -35,10 +30,16 @@
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
+import android.graphics.Xfermode;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.view.View;
 
+import com.android.internal.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 
 /**
@@ -78,6 +79,16 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public void setXfermode(Xfermode mode) {
+        if (mDrawable != null) {
+            mDrawable.setXfermode(mode);
+        }
+    }
+
+    /**
      * Sets the wrapped drawable.
      *
      * @param dr the wrapped drawable
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 0da61c2..266a6ac 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -888,7 +888,10 @@
             // The ripple timing depends on the paint's alpha value, so we need
             // to push just the alpha channel into the paint and let the filter
             // handle the full-alpha color.
-            mMaskColorFilter.setColor(color | 0xFF000000);
+            int maskColor = color | 0xFF000000;
+            if (mMaskColorFilter.getColor() != maskColor) {
+                mMaskColorFilter = new PorterDuffColorFilter(maskColor, mMaskColorFilter.getMode());
+            }
             p.setColor(color & 0xFF000000);
             p.setColorFilter(mMaskColorFilter);
             p.setShader(mMaskShader);
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 88e768c..30389a29d 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,12 +16,15 @@
 
 package android.security;
 
+import android.annotation.TestApi;
+
 /**
  * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
  * ones from keymaster.
  *
  * @hide
  */
+@TestApi
 public class KeyStoreException extends Exception {
 
     private final int mErrorCode;
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index c0d0fb0..58332a2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
@@ -673,8 +674,8 @@
     }
 
     /**
-     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
-     * signing. Decryption and signature verification will still be available when the screen is
+     * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+     * signing. Encryption and signature verification will still be available when the screen is
      * locked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
@@ -1227,6 +1228,7 @@
          *
          * Sets whether to include a temporary unique ID field in the attestation certificate.
          */
+        @TestApi
         @NonNull
         public Builder setUniqueIdIncluded(boolean uniqueIdIncluded) {
             mUniqueIdIncluded = uniqueIdIncluded;
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index 41dc201..92bee8d 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -19,6 +19,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
@@ -496,6 +497,7 @@
      * @see KeymasterUtils#addUserAuthArgs
      * @hide
      */
+    @TestApi
     public long getBoundToSpecificSecureUserId() {
         return mBoundToSecureUserId;
     }
@@ -511,8 +513,8 @@
     }
 
     /**
-     * Returns {@code true} if the screen must be unlocked for this key to be used for encryption or
-     * signing. Decryption and signature verification will still be available when the screen is
+     * Returns {@code true} if the screen must be unlocked for this key to be used for decryption or
+     * signing. Encryption and signature verification will still be available when the screen is
      * locked.
      *
      * @see Builder#setUnlockedDeviceRequired(boolean)
@@ -913,6 +915,7 @@
          * @see KeyProtection#getBoundToSpecificSecureUserId()
          * @hide
          */
+        @TestApi
         public Builder setBoundToSpecificSecureUserId(long secureUserId) {
             mBoundToSecureUserId = secureUserId;
             return this;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 985a756..7d4e6f8 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -456,6 +456,22 @@
 
     uninit();
 
+    // The chunk must be at least the size of the string pool header.
+    if (size < sizeof(ResStringPool_header)) {
+        LOG_ALWAYS_FATAL("Bad string block: data size %zu is too small to be a string block", size);
+        return (mError=BAD_TYPE);
+    }
+
+    // The data is at least as big as a ResChunk_header, so we can safely validate the other
+    // header fields.
+    // `data + size` is safe because the source of `size` comes from the kernel/filesystem.
+    if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header),
+                       reinterpret_cast<const uint8_t*>(data) + size,
+                       "ResStringPool_header") != NO_ERROR) {
+        LOG_ALWAYS_FATAL("Bad string block: malformed block dimensions");
+        return (mError=BAD_TYPE);
+    }
+
     const bool notDeviceEndian = htods(0xf0) != 0xf0;
 
     if (copyData || notDeviceEndian) {
@@ -467,6 +483,8 @@
         data = mOwnedData;
     }
 
+    // The size has been checked, so it is safe to read the data in the ResStringPool_header
+    // data structure.
     mHeader = (const ResStringPool_header*)data;
 
     if (notDeviceEndian) {
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index be7f300..a27d519 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -269,13 +269,8 @@
         "TextureCache.cpp",
         "VectorDrawable.cpp",
         "VkLayer.cpp",
-        "protos/hwui.proto",
     ],
 
-    proto: {
-        export_proto_headers: true,
-    },
-
     export_include_dirs: ["."],
     export_shared_lib_headers: ["libRScpp"],
 }
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d93a757..5ac330b 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -29,9 +29,6 @@
 #include "utils/StringUtils.h"
 #include "utils/TraceUtils.h"
 
-#include "protos/ProtoHelpers.h"
-#include "protos/hwui.pb.h"
-
 #include <SkPathOps.h>
 #include <algorithm>
 #include <sstream>
@@ -101,77 +98,6 @@
     output << std::endl;
 }
 
-void RenderNode::copyTo(proto::RenderNode* pnode) {
-    pnode->set_id(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)));
-    pnode->set_name(mName.string(), mName.length());
-
-    proto::RenderProperties* pprops = pnode->mutable_properties();
-    pprops->set_left(properties().getLeft());
-    pprops->set_top(properties().getTop());
-    pprops->set_right(properties().getRight());
-    pprops->set_bottom(properties().getBottom());
-    pprops->set_clip_flags(properties().getClippingFlags());
-    pprops->set_alpha(properties().getAlpha());
-    pprops->set_translation_x(properties().getTranslationX());
-    pprops->set_translation_y(properties().getTranslationY());
-    pprops->set_translation_z(properties().getTranslationZ());
-    pprops->set_elevation(properties().getElevation());
-    pprops->set_rotation(properties().getRotation());
-    pprops->set_rotation_x(properties().getRotationX());
-    pprops->set_rotation_y(properties().getRotationY());
-    pprops->set_scale_x(properties().getScaleX());
-    pprops->set_scale_y(properties().getScaleY());
-    pprops->set_pivot_x(properties().getPivotX());
-    pprops->set_pivot_y(properties().getPivotY());
-    pprops->set_has_overlapping_rendering(properties().getHasOverlappingRendering());
-    pprops->set_pivot_explicitly_set(properties().isPivotExplicitlySet());
-    pprops->set_project_backwards(properties().getProjectBackwards());
-    pprops->set_projection_receiver(properties().isProjectionReceiver());
-    set(pprops->mutable_clip_bounds(), properties().getClipBounds());
-
-    const Outline& outline = properties().getOutline();
-    if (outline.getType() != Outline::Type::None) {
-        proto::Outline* poutline = pprops->mutable_outline();
-        poutline->clear_path();
-        if (outline.getType() == Outline::Type::Empty) {
-            poutline->set_type(proto::Outline_Type_Empty);
-        } else if (outline.getType() == Outline::Type::ConvexPath) {
-            poutline->set_type(proto::Outline_Type_ConvexPath);
-            if (const SkPath* path = outline.getPath()) {
-                set(poutline->mutable_path(), *path);
-            }
-        } else if (outline.getType() == Outline::Type::RoundRect) {
-            poutline->set_type(proto::Outline_Type_RoundRect);
-        } else {
-            ALOGW("Uknown outline type! %d", static_cast<int>(outline.getType()));
-            poutline->set_type(proto::Outline_Type_None);
-        }
-        poutline->set_should_clip(outline.getShouldClip());
-        poutline->set_alpha(outline.getAlpha());
-        poutline->set_radius(outline.getRadius());
-        set(poutline->mutable_bounds(), outline.getBounds());
-    } else {
-        pprops->clear_outline();
-    }
-
-    const RevealClip& revealClip = properties().getRevealClip();
-    if (revealClip.willClip()) {
-        proto::RevealClip* prevealClip = pprops->mutable_reveal_clip();
-        prevealClip->set_x(revealClip.getX());
-        prevealClip->set_y(revealClip.getY());
-        prevealClip->set_radius(revealClip.getRadius());
-    } else {
-        pprops->clear_reveal_clip();
-    }
-
-    pnode->clear_children();
-    if (mDisplayList) {
-        for (auto&& child : mDisplayList->getChildren()) {
-            child->renderNode->copyTo(pnode->add_children());
-        }
-    }
-}
-
 int RenderNode::getDebugSize() {
     int size = sizeof(RenderNode);
     if (mStagingDisplayList) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1469a15..1e0d4e2 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -108,7 +108,6 @@
 
     ANDROID_API void output();
     ANDROID_API int getDebugSize();
-    void copyTo(proto::RenderNode* node);
 
     bool isRenderable() const { return mDisplayList && !mDisplayList->isEmpty(); }
 
diff --git a/libs/hwui/debug/gles_decls.in b/libs/hwui/debug/gles_decls.in
index 16574a7..3900959 100644
--- a/libs/hwui/debug/gles_decls.in
+++ b/libs/hwui/debug/gles_decls.in
@@ -387,7 +387,6 @@
 GL_ENTRY(void, glDrawElementsBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)
 GL_ENTRY(void, glDrawRangeElementsBaseVertexOES, GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices, GLint basevertex)
 GL_ENTRY(void, glDrawElementsInstancedBaseVertexOES, GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)
-GL_ENTRY(void, glMultiDrawElementsBaseVertexOES, GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex)
 GL_ENTRY(void, glFramebufferTextureOES, GLenum target, GLenum attachment, GLuint texture, GLint level)
 GL_ENTRY(void, glGetProgramBinaryOES, GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, void *binary)
 GL_ENTRY(void, glProgramBinaryOES, GLuint program, GLenum binaryFormat, const void *binary, GLint length)
@@ -541,4 +540,4 @@
 GL_ENTRY(void, glTextureStorage1DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width)
 GL_ENTRY(void, glTextureStorage2DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height)
 GL_ENTRY(void, glTextureStorage3DEXT, GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth)
-GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
\ No newline at end of file
+GL_ENTRY(void, glTextureViewEXT, GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers)
diff --git a/libs/hwui/debug/gles_stubs.in b/libs/hwui/debug/gles_stubs.in
index 4064a39..7cba0c1 100644
--- a/libs/hwui/debug/gles_stubs.in
+++ b/libs/hwui/debug/gles_stubs.in
@@ -1165,9 +1165,6 @@
 void API_ENTRY(glDrawElementsInstancedBaseVertexOES)(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex) {
     CALL_GL_API(glDrawElementsInstancedBaseVertexOES, mode, count, type, indices, instancecount, basevertex);
 }
-void API_ENTRY(glMultiDrawElementsBaseVertexOES)(GLenum mode, const GLsizei *count, GLenum type, const void *const*indices, GLsizei primcount, const GLint *basevertex) {
-    CALL_GL_API(glMultiDrawElementsBaseVertexOES, mode, count, type, indices, primcount, basevertex);
-}
 void API_ENTRY(glFramebufferTextureOES)(GLenum target, GLenum attachment, GLuint texture, GLint level) {
     CALL_GL_API(glFramebufferTextureOES, target, attachment, texture, level);
 }
@@ -1629,4 +1626,4 @@
 }
 void API_ENTRY(glTextureViewEXT)(GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers) {
     CALL_GL_API(glTextureViewEXT, texture, target, origtexture, internalformat, minlevel, numlevels, minlayer, numlayers);
-}
\ No newline at end of file
+}
diff --git a/libs/hwui/protos/ProtoHelpers.h b/libs/hwui/protos/ProtoHelpers.h
deleted file mode 100644
index 833c77f..0000000
--- a/libs/hwui/protos/ProtoHelpers.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef PROTOHELPERS_H
-#define PROTOHELPERS_H
-
-#include "Rect.h"
-#include "protos/hwui.pb.h"
-
-namespace android {
-namespace uirenderer {
-
-void set(proto::RectF* dest, const Rect& src) {
-    dest->set_left(src.left);
-    dest->set_top(src.top);
-    dest->set_right(src.right);
-    dest->set_bottom(src.bottom);
-}
-
-void set(std::string* dest, const SkPath& src) {
-    size_t size = src.writeToMemory(nullptr);
-    dest->resize(size);
-    src.writeToMemory(&*dest->begin());
-}
-
-}  // namespace uirenderer
-}  // namespace android
-
-#endif  // PROTOHELPERS_H
diff --git a/libs/hwui/protos/hwui.proto b/libs/hwui/protos/hwui.proto
deleted file mode 100644
index dcff80a..0000000
--- a/libs/hwui/protos/hwui.proto
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package android.uirenderer.proto;
-
-option optimize_for = LITE_RUNTIME;
-
-message RenderNode {
-    required uint64 id = 1;
-    required string name = 2;
-    required RenderProperties properties = 3;
-    optional DisplayList display_list = 4;
-    repeated RenderNode children = 5;
-};
-
-message RenderProperties {
-    required int32 left = 1;
-    required int32 right = 2;
-    required int32 top = 3;
-    required int32 bottom = 4;
-    required int32 clip_flags = 5;
-    required float alpha = 6;
-    required float translation_x = 7;
-    required float translation_y = 8;
-    required float translation_z = 9;
-    required float elevation = 10;
-    required float rotation = 11;
-    required float rotation_x = 12;
-    required float rotation_y = 13;
-    required float scale_x = 14;
-    required float scale_y = 15;
-    required float pivot_x = 16;
-    required float pivot_y = 17;
-    required bool has_overlapping_rendering = 18;
-    required bool pivot_explicitly_set = 19;
-    required bool project_backwards = 20;
-    required bool projection_receiver = 21;
-    required RectF clip_bounds = 22;
-    optional Outline outline = 23;
-    optional RevealClip reveal_clip = 24;
-};
-
-message RectF {
-    required float left = 1;
-    required float right = 2;
-    required float top = 3;
-    required float bottom = 4;
-}
-
-message Outline {
-    required bool should_clip = 1;
-    enum Type {
-        None = 0;
-        Empty = 1;
-        ConvexPath = 2;
-        RoundRect = 3;
-    }
-    required Type type = 2;
-    required RectF bounds = 3;
-    required float radius = 4;
-    required float alpha = 5;
-    optional bytes path = 6;
-}
-
-message RevealClip {
-    required float x = 1;
-    required float y = 2;
-    required float radius = 3;
-}
-
-message DisplayList {
-    optional int32 projection_receive_index = 1;
-    repeated DrawOp draw_ops = 2;
-}
-
-message DrawOp {
-    oneof drawop {
-        DrawOp_RenderNode render_node = 1;
-    }
-}
-
-message DrawOp_RenderNode {
-    optional RenderNode node = 1;
-}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 23023e7..a0fc83e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -30,14 +30,12 @@
 #include "pipeline/skia/SkiaOpenGLPipeline.h"
 #include "pipeline/skia/SkiaPipeline.h"
 #include "pipeline/skia/SkiaVulkanPipeline.h"
-#include "protos/hwui.pb.h"
 #include "renderstate/RenderState.h"
 #include "renderstate/Stencil.h"
 #include "utils/GLUtils.h"
 #include "utils/TimeUtils.h"
 
 #include <cutils/properties.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
 #include <private/hwui/DrawGlInfo.h>
 #include <strings.h>
 
@@ -50,8 +48,6 @@
 #define TRIM_MEMORY_COMPLETE 80
 #define TRIM_MEMORY_UI_HIDDEN 20
 
-#define ENABLE_RENDERNODE_SERIALIZATION false
-
 #define LOG_FRAMETIME_MMA 0
 
 #if LOG_FRAMETIME_MMA
@@ -585,39 +581,6 @@
     mJankTracker.setDescription(JankTrackerType::Window, std::move(name));
 }
 
-void CanvasContext::serializeDisplayListTree() {
-#if ENABLE_RENDERNODE_SERIALIZATION
-    using namespace google::protobuf::io;
-    char package[128];
-    // Check whether tracing is enabled for this process.
-    FILE* file = fopen("/proc/self/cmdline", "r");
-    if (file) {
-        if (!fgets(package, 128, file)) {
-            ALOGE("Error reading cmdline: %s (%d)", strerror(errno), errno);
-            fclose(file);
-            return;
-        }
-        fclose(file);
-    } else {
-        ALOGE("Error opening /proc/self/cmdline: %s (%d)", strerror(errno), errno);
-        return;
-    }
-    char path[1024];
-    snprintf(path, 1024, "/data/data/%s/cache/rendertree_dump", package);
-    int fd = open(path, O_CREAT | O_WRONLY, S_IRWXU | S_IRGRP | S_IROTH);
-    if (fd == -1) {
-        ALOGD("Failed to open '%s'", path);
-        return;
-    }
-    proto::RenderNode tree;
-    // TODO: Streaming writes?
-    mRootRenderNode->copyTo(&tree);
-    std::string data = tree.SerializeAsString();
-    write(fd, data.c_str(), data.length());
-    close(fd);
-#endif
-}
-
 void CanvasContext::waitOnFences() {
     if (mFrameFences.size()) {
         ATRACE_CALL();
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 8aaa8c1..930498af3 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -155,8 +155,6 @@
 
     void setName(const std::string&& name);
 
-    void serializeDisplayListTree();
-
     void addRenderNode(RenderNode* node, bool placeFront);
     void removeRenderNode(RenderNode* node);
 
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 00645d6..a1a2bad 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -274,10 +274,6 @@
     mDrawFrameTask.setFrameCallback(std::move(callback));
 }
 
-void RenderProxy::serializeDisplayListTree() {
-    mRenderThread.queue().post([=]() { mContext->serializeDisplayListTree(); });
-}
-
 void RenderProxy::addFrameMetricsObserver(FrameMetricsObserver* observerPtr) {
     mRenderThread.queue().post([ this, observer = sp{observerPtr} ]() {
         mContext->addFrameMetricsObserver(observer.get());
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index bd5c9a9..ca20755 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -109,8 +109,6 @@
     ANDROID_API static void setProcessStatsBuffer(int fd);
     ANDROID_API int getRenderThreadTid();
 
-    ANDROID_API void serializeDisplayListTree();
-
     ANDROID_API void addRenderNode(RenderNode* node, bool placeFront);
     ANDROID_API void removeRenderNode(RenderNode* node);
     ANDROID_API void drawRenderNode(RenderNode* node);
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index ffe0578..4723759 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -114,7 +114,7 @@
     sk_sp<GrContext> grContext(GrContext::MakeVulkan(mBackendContext, options));
     LOG_ALWAYS_FATAL_IF(!grContext.get());
     mRenderThread.setGrContext(grContext);
-    DeviceInfo::initialize(mRenderThread.getGrContext()->caps()->maxRenderTargetSize());
+    DeviceInfo::initialize(mRenderThread.getGrContext()->maxRenderTargetSize());
 
     if (Properties::enablePartialUpdates && Properties::useBufferAge) {
         mSwapBehavior = SwapBehavior::BufferAge;
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index dc8e600..6f3d922 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -19,7 +19,6 @@
 
 #include "Properties.h"
 #include "hwui/Typeface.h"
-#include "protos/hwui.pb.h"
 
 #include <benchmark/benchmark.h>
 #include <getopt.h>
diff --git a/location/tests/locationtests/AndroidTest.xml b/location/tests/locationtests/AndroidTest.xml
index 0c5b7cc..bb6547b 100644
--- a/location/tests/locationtests/AndroidTest.xml
+++ b/location/tests/locationtests/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.locationtests" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0458931..9152ff2 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -250,9 +250,10 @@
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE,    SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_GAME,                              SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANT,                         SUPPRESSIBLE_MEDIA);
+        /** default volume assignment is STREAM_MUSIC, handle unknown usage as media */
+        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_MEDIA);
         SUPPRESSIBLE_USAGES.put(USAGE_VOICE_COMMUNICATION_SIGNALLING,    SUPPRESSIBLE_SYSTEM);
         SUPPRESSIBLE_USAGES.put(USAGE_ASSISTANCE_SONIFICATION,           SUPPRESSIBLE_SYSTEM);
-        SUPPRESSIBLE_USAGES.put(USAGE_UNKNOWN,                           SUPPRESSIBLE_SYSTEM);
     }
 
     /**
@@ -1057,8 +1058,7 @@
             case USAGE_ASSISTANCE_ACCESSIBILITY:
                 return AudioSystem.STREAM_ACCESSIBILITY;
             case USAGE_UNKNOWN:
-                return fromGetVolumeControlStream ?
-                        AudioManager.USE_DEFAULT_STREAM_TYPE : AudioSystem.STREAM_MUSIC;
+                return AudioSystem.STREAM_MUSIC;
             default:
                 if (fromGetVolumeControlStream) {
                     throw new IllegalArgumentException("Unknown usage value " + aa.getUsage() +
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6a3a6e5..f0d9ca3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -63,6 +63,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
@@ -4787,6 +4788,21 @@
     }
 
     /**
+     * Add {@link MicrophoneInfo} by device information while filtering certain types.
+     */
+    private void addMicrophonesFromAudioDeviceInfo(ArrayList<MicrophoneInfo> microphones,
+                    HashSet<Integer> filterTypes) {
+        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
+        for (AudioDeviceInfo device : devices) {
+            if (filterTypes.contains(device.getType())) {
+                continue;
+            }
+            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
+            microphones.add(microphone);
+        }
+    }
+
+    /**
      * Returns a list of {@link MicrophoneInfo} that corresponds to the characteristics
      * of all available microphones. The list is empty when no microphones are available
      * on the device. An error during the query will result in an IOException being thrown.
@@ -4797,21 +4813,17 @@
     public List<MicrophoneInfo> getMicrophones() throws IOException {
         ArrayList<MicrophoneInfo> microphones = new ArrayList<MicrophoneInfo>();
         int status = AudioSystem.getMicrophones(microphones);
+        HashSet<Integer> filterTypes = new HashSet<>();
+        filterTypes.add(AudioDeviceInfo.TYPE_TELEPHONY);
         if (status != AudioManager.SUCCESS) {
-            // fail and bail!
+            // fail and populate microphones with unknown characteristics by device information.
             Log.e(TAG, "getMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>(); // Always return a list.
+            addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
+            return microphones;
         }
         setPortIdForMicrophones(microphones);
-        AudioDeviceInfo[] devices = getDevicesStatic(GET_DEVICES_INPUTS);
-        for (AudioDeviceInfo device : devices) {
-            if (device.getType() == AudioDeviceInfo.TYPE_BUILTIN_MIC ||
-                    device.getType() == AudioDeviceInfo.TYPE_TELEPHONY) {
-                continue;
-            }
-            MicrophoneInfo microphone = microphoneInfoFromAudioDeviceInfo(device);
-            microphones.add(microphone);
-        }
+        filterTypes.add(AudioDeviceInfo.TYPE_BUILTIN_MIC);
+        addMicrophonesFromAudioDeviceInfo(microphones, filterTypes);
         return microphones;
     }
 
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 4f0dccb..6b35dd4 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1628,7 +1628,6 @@
         int status = native_get_active_microphones(activeMicrophones);
         if (status != AudioManager.SUCCESS) {
             Log.e(TAG, "getActiveMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
 
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
index 521e897..aaae5e7b 100644
--- a/media/java/android/media/BufferingParams.java
+++ b/media/java/android/media/BufferingParams.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -63,6 +64,7 @@
  * <p>Users should use {@link Builder} to change {@link BufferingParams}.
  * @hide
  */
+@TestApi
 public final class BufferingParams implements Parcelable {
     private static final int BUFFERING_NO_MARK = -1;
 
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f9a1f8b..392a1eb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -19,6 +19,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
@@ -1680,6 +1681,7 @@
      * @hide
      */
     @NonNull
+    @TestApi
     public native BufferingParams getBufferingParams();
 
     /**
@@ -1696,6 +1698,7 @@
      * @throws IllegalArgumentException if params is invalid or not supported.
      * @hide
      */
+    @TestApi
     public native void setBufferingParams(@NonNull BufferingParams params);
 
     /**
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 90b6bff..82d64f3 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1434,7 +1434,6 @@
         int status = native_getActiveMicrophones(activeMicrophones);
         if (status != AudioManager.SUCCESS) {
             Log.e(TAG, "getActiveMicrophones failed:" + status);
-            return new ArrayList<MicrophoneInfo>();
         }
         AudioManager.setPortIdForMicrophones(activeMicrophones);
 
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index b4ca88c..ed2afdd 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -809,7 +809,11 @@
             mAudioTrack = newTrack;
             ALOGV("using new track %p for sample %d", newTrack.get(), sample->sampleID());
         }
-        newTrack->setVolume(leftVolume, rightVolume);
+        if (mMuted) {
+            newTrack->setVolume(0.0f, 0.0f);
+        } else {
+            newTrack->setVolume(leftVolume, rightVolume);
+        }
         newTrack->setLoop(0, frameCount, loop);
         mPos = 0;
         mSample = sample;
diff --git a/packages/MtpDocumentsProvider/tests/AndroidTest.xml b/packages/MtpDocumentsProvider/tests/AndroidTest.xml
index 940d364..f84131c 100644
--- a/packages/MtpDocumentsProvider/tests/AndroidTest.xml
+++ b/packages/MtpDocumentsProvider/tests/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.InstrumentationTest" >
         <option name="package" value="com.android.mtp.tests" />
         <option name="runner" value="com.android.mtp.TestResultInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
index 15dd64b..d21a2e4 100644
--- a/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
+++ b/packages/PrintSpooler/tests/outofprocess/AndroidTest.xml
@@ -25,5 +25,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.printspooler.outofprocess.tests" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 4a6df50..bab59f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -29,5 +29,5 @@
     void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState);
     void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state);
     void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile);
-    void onProfileAudioStateChanged(int bluetoothProfile, int state);
+    void onAudioModeChanged();
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index b74b2cd..06fe4de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -27,6 +27,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.settingslib.R;
@@ -119,6 +120,12 @@
         addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
                    new ActiveDeviceChangedHandler());
 
+        // Headset state changed broadcasts
+        addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED,
+                new AudioModeChangedHandler());
+        addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED,
+                new AudioModeChangedHandler());
+
         mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter, null, mReceiverHandler);
         mContext.registerReceiver(mProfileBroadcastReceiver, mProfileIntentFilter, null, mReceiverHandler);
     }
@@ -456,4 +463,25 @@
             }
         }
     }
+
+    private class AudioModeChangedHandler implements Handler {
+
+        @Override
+        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
+            final String action = intent.getAction();
+            if (action == null) {
+                Log.w(TAG, "AudioModeChangedHandler() action is null");
+                return;
+            }
+            dispatchAudioModeChanged();
+        }
+    }
+
+    private void dispatchAudioModeChanged() {
+        synchronized (mCallbacks) {
+            for (BluetoothCallback callback : mCallbacks) {
+                callback.onAudioModeChanged();
+            }
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index 54d1aba..148b7a7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -16,6 +16,7 @@
 
 package com.android.settingslib.drawable;
 
+import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
@@ -251,11 +252,8 @@
                 mPaint.setColorFilter(null);
             } else {
                 int color = mTintColor.getColorForState(getState(), mTintColor.getDefaultColor());
-                if (mPaint.getColorFilter() == null) {
+                if (shouldUpdateColorFilter(color, mTintMode)) {
                     mPaint.setColorFilter(new PorterDuffColorFilter(color, mTintMode));
-                } else {
-                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setMode(mTintMode);
-                    ((PorterDuffColorFilter) mPaint.getColorFilter()).setColor(color);
                 }
             }
 
@@ -263,6 +261,18 @@
         }
     }
 
+    private boolean shouldUpdateColorFilter(@ColorInt int color, PorterDuff.Mode mode) {
+        ColorFilter colorFilter = mPaint.getColorFilter();
+        if (colorFilter instanceof PorterDuffColorFilter) {
+            PorterDuffColorFilter porterDuffColorFilter = (PorterDuffColorFilter) colorFilter;
+            int currentColor = porterDuffColorFilter.getColor();
+            PorterDuff.Mode currentMode = porterDuffColorFilter.getMode();
+            return currentColor != color || currentMode != mode;
+        } else {
+            return false;
+        }
+    }
+
     @Override
     public void setAlpha(int alpha) {
         mPaint.setAlpha(alpha);
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
index e63bdd6..a6c1634 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtil.java
@@ -60,7 +60,7 @@
 
     // InputMethods and subtypes are saved in the settings as follows:
     // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
-    private static String buildInputMethodsAndSubtypesString(
+    public static String buildInputMethodsAndSubtypesString(
             final HashMap<String, HashSet<String>> imeToSubtypesMap) {
         final StringBuilder builder = new StringBuilder();
         for (final String imi : imeToSubtypesMap.keySet()) {
@@ -115,7 +115,7 @@
         return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
     }
 
-    private static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
+    public static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
             final String inputMethodsAndSubtypesString) {
         final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
         if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
diff --git a/packages/SettingsLib/tests/integ/AndroidTest.xml b/packages/SettingsLib/tests/integ/AndroidTest.xml
index 96621eb..d7ee618 100644
--- a/packages/SettingsLib/tests/integ/AndroidTest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.settingslib" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
new file mode 100644
index 0000000..d1e37f6
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.settingslib.bluetooth;
+
+import static org.mockito.Mockito.verify;
+
+import android.bluetooth.BluetoothHeadset;
+import android.content.Context;
+import android.content.Intent;
+
+import android.telephony.TelephonyManager;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothEventManagerTest {
+
+    @Mock
+    private LocalBluetoothAdapter mLocalAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mCachedDeviceManager;
+    @Mock
+    private BluetoothCallback mBluetoothCallback;
+
+    private Context mContext;
+    private Intent mIntent;
+    private BluetoothEventManager mBluetoothEventManager;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mContext = RuntimeEnvironment.application;
+
+        mBluetoothEventManager = new BluetoothEventManager(mLocalAdapter,
+                mCachedDeviceManager, mContext);
+    }
+
+    /**
+     * Intent ACTION_AUDIO_STATE_CHANGED should dispatch to callback.
+     */
+    @Test
+    public void intentWithExtraState_audioStateChangedShouldDispatchToRegisterCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAudioModeChanged();
+    }
+
+    /**
+     * Intent ACTION_PHONE_STATE_CHANGED should dispatch to callback.
+     */
+    @Test
+    public void intentWithExtraState_phoneStateChangedShouldDispatchToRegisterCallback() {
+        mBluetoothEventManager.registerCallback(mBluetoothCallback);
+        mIntent = new Intent(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mBluetoothCallback).onAudioModeChanged();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
new file mode 100644
index 0000000..7c7957c
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/inputmethod/InputMethodAndSubtypeUtilTest.java
@@ -0,0 +1,190 @@
+/*
+ * 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.settingslib.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.HashMap;
+import java.util.HashSet;
+
+@RunWith(RobolectricTestRunner.class)
+public class InputMethodAndSubtypeUtilTest {
+
+    private static final HashSet<String> EMPTY_STRING_SET = new HashSet<>();
+
+    private static HashSet<String> asHashSet(String... strings) {
+        HashSet<String> hashSet = new HashSet<>();
+        for (String s : strings) {
+            hashSet.add(s);
+        }
+        return hashSet;
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_EmptyString() {
+        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("")).isEmpty();
+        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(null)).isEmpty();
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0");
+        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MultipleImesNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0:ime1");
+        assertThat(r).containsExactly("ime0", EMPTY_STRING_SET, "ime1", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString("ime0;subtype0");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeDuplicateSameSubtypes() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype0");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype1");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"));
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MultiplePairsOfImeSubtype() {
+        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0:ime1;subtype1"))
+                .containsExactly("ime0", asHashSet("subtype0"), "ime1", asHashSet("subtype1"));
+        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0;subtype1:ime1;subtype2"))
+                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                        "ime1", asHashSet("subtype2"));
+        assertThat(InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                "ime0;subtype0;subtype1:ime1;subtype1;subtype2"))
+                .containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                        "ime1", asHashSet("subtype1", "subtype2"));
+
+    }
+
+    @Test
+    public void parseInputMethodsAndSubtypesString_MixedImeSubtypePairsAndImeNoSubtype() {
+        HashMap<String, HashSet<String>> r =
+                InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(
+                        "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2");
+        assertThat(r).containsExactly("ime0", asHashSet("subtype0", "subtype1"),
+                "ime1", asHashSet("subtype1", "subtype2"),
+                "ime2", EMPTY_STRING_SET);
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_EmptyInput() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        assertThat(map).isEmpty();
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleIme() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", new HashSet<>());
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+        assertThat(result).isEqualTo("ime0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleImeSingleSubtype() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0"));
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+        assertThat(result).isEqualTo("ime0;subtype0");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_SingleImeMultipleSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("(ime0;subtype0;subtype1)|(ime0;subtype1;subtype0)");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesNoSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", EMPTY_STRING_SET);
+        map.put("ime1", EMPTY_STRING_SET);
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("(ime0:ime1)|(ime1:ime0)");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesWithAndWithoutSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        map.put("ime1", EMPTY_STRING_SET);
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("(ime0;subtype0;subtype1:ime1)|(ime0;subtype1;subtype0:ime1)"
+                + "|(ime1:ime0;subtype0;subtype1)|(ime1:ime0;subtype1;subtype0)");
+    }
+
+    @Test
+    public void buildInputMethodsAndSubtypesString_MultipleImesWithSubtypes() {
+        HashMap<String, HashSet<String>> map = new HashMap<>();
+        map.put("ime0", asHashSet("subtype0", "subtype1"));
+        map.put("ime1", asHashSet("subtype2", "subtype3"));
+        String result = InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString(map);
+
+        // We do not expect what order will be used to concatenate items in
+        // InputMethodAndSubtypeUtil.buildInputMethodsAndSubtypesString() hence accept all possible
+        // permutations here.
+        assertThat(result).matches("|(ime0;subtype0;subtype1:ime1;subtype2;subtype3)|"
+                + "|(ime0;subtype1;subtype0:ime1;subtype2;subtype3)|"
+                + "|(ime0;subtype0;subtype1:ime1;subtype3;subtype2)|"
+                + "|(ime0;subtype1;subtype0:ime1;subtype3;subtype2)|"
+                + "|(ime1;subtype2;subtype3:ime0;subtype0;subtype1)|"
+                + "|(ime2;subtype3;subtype2:ime0;subtype0;subtype1)|"
+                + "|(ime3;subtype2;subtype3:ime0;subtype1;subtype0)|"
+                + "|(ime4;subtype3;subtype2:ime0;subtype1;subtype0)");
+    }
+}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 313f73f..f728684 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -253,6 +253,7 @@
                     && !RESTORE_FROM_HIGHER_SDK_INT_SUPPORTED_KEYS.contains(key)) {
                 Log.w(TAG, "Not restoring unrecognized key '"
                         + key + "' from future version " + appVersionCode);
+                data.skipEntityData();
                 continue;
             }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index a6d6250..022e306 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2935,7 +2935,7 @@
         }
 
         private final class UpgradeController {
-            private static final int SETTINGS_VERSION = 162;
+            private static final int SETTINGS_VERSION = 163;
 
             private final int mUserId;
 
@@ -3709,6 +3709,21 @@
                     currentVersion = 162;
                 }
 
+                if (currentVersion == 162) {
+                    // Version 162: Add a gesture for silencing phones
+                    final SettingsState settings = getGlobalSettingsLocked();
+                    final Setting currentSetting = settings.getSettingLocked(
+                            Global.SHOW_ZEN_UPGRADE_NOTIFICATION);
+                    if (!currentSetting.isNull()
+                            && TextUtils.equals("0", currentSetting.getValue())) {
+                        settings.insertSettingLocked(
+                                Global.SHOW_ZEN_UPGRADE_NOTIFICATION, "1",
+                                null, true, SettingsState.SYSTEM_PACKAGE_NAME);
+                    }
+
+                    currentVersion = 163;
+                }
+
                 // vXXX: Add new settings above this point.
 
                 if (currentVersion != newVersion) {
diff --git a/packages/SettingsProvider/test/AndroidTest.xml b/packages/SettingsProvider/test/AndroidTest.xml
index 3a156895..46b8f94 100644
--- a/packages/SettingsProvider/test/AndroidTest.xml
+++ b/packages/SettingsProvider/test/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.providers.setting.test" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/Shell/tests/AndroidTest.xml b/packages/Shell/tests/AndroidTest.xml
index bd37712c..e592d82 100644
--- a/packages/Shell/tests/AndroidTest.xml
+++ b/packages/Shell/tests/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.shell.tests" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index d6fab4c..285b89f 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -206,7 +206,7 @@
     <uses-permission android:name="android.permission.WATCH_APPOPS" />
 
     <!-- to read and change hvac values in a car -->
-    <uses-permission android:name="android.car.permission.ADJUST_CAR_CLIMATE" />
+    <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE" />
 
     <application
         android:name=".SystemUIApplication"
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index addbf84..37de433 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -47,6 +47,8 @@
     <dimen name="widget_title_font_size">24sp</dimen>
     <!-- Slice subtitle  -->
     <dimen name="widget_label_font_size">16sp</dimen>
+    <!-- Slice offset when pulsing -->
+    <dimen name="widget_pulsing_bottom_padding">24dp</dimen>
     <!-- Clock without header -->
     <dimen name="widget_big_font_size">64dp</dimen>
     <!-- Clock with header -->
diff --git a/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml b/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml
new file mode 100644
index 0000000..05db3a8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_onboarding_toast_rounded_background.xml
@@ -0,0 +1,19 @@
+<!--
+    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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <solid android:color="?android:attr/colorAccent" />
+    <corners android:radius="8dp" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml
index 5588eb2..d9ed823 100644
--- a/packages/SystemUI/res/drawable/rounded_ripple.xml
+++ b/packages/SystemUI/res/drawable/rounded_ripple.xml
@@ -23,7 +23,7 @@
     </item>
     <item android:id="@android:id/background">
         <shape android:shape="rectangle">
-            <solid android:color="#FFFFFFFF"/>
+            <solid android:color="?android:attr/colorBackgroundFloating"/>
             <corners android:radius="8dp"/>
         </shape>
     </item>
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index ef18725..f424171 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -38,9 +38,7 @@
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:layout_marginTop="1dp"
-        android:layout_marginStart="8dp"
-        android:layout_marginEnd="8dp"
+        android:layout_marginStart="16dp"
         android:layout_gravity="center_vertical"
         android:gravity="end" >
 
@@ -48,18 +46,17 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical|start"
-            android:layout_margin="15dp"
+            android:layout_marginEnd="8dp"
             android:visibility="gone"
             layout="@layout/mobile_signal_group" />
 
         <com.android.keyguard.CarrierText
             android:id="@+id/qs_carrier_text"
             android:layout_width="0dp"
-            android:layout_height="match_parent"
+            android:layout_height="wrap_content"
             android:layout_weight="1"
-            android:layout_marginStart="8dp"
+            android:layout_gravity="center_vertical|start"
             android:layout_marginEnd="32dp"
-            android:gravity="center_vertical|start"
             android:ellipsize="marquee"
             android:textAppearance="@style/TextAppearance.QS.TileLabel"
             android:textColor="?android:attr/textColorPrimary"
diff --git a/packages/SystemUI/res/layout/recents_onboarding.xml b/packages/SystemUI/res/layout/recents_onboarding.xml
index 12f278a..6764eee 100644
--- a/packages/SystemUI/res/layout/recents_onboarding.xml
+++ b/packages/SystemUI/res/layout/recents_onboarding.xml
@@ -14,32 +14,48 @@
      limitations under the License.
 -->
 
-<FrameLayout
+<LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="48dp"
-    android:layout_width="match_parent"
-    android:background="@android:color/black"
-    android:layout_gravity="center">
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="?android:attr/listDivider"
-        android:gravity="top"/>
-    <TextView
-        android:id="@+id/onboarding_text"
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:paddingBottom="13dp"
+    android:orientation="vertical">
+
+    <LinearLayout
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:textColor="@android:color/white"
-        android:textSize="16sp"
-        android:drawableBottom="@drawable/ic_chevron_up"/>
-    <ImageView
-        android:id="@+id/dismiss"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
-        android:padding="12dp"
-        android:layout_marginEnd="6dp"
-        android:src="@drawable/ic_close_white"
-        android:background="?android:attr/selectableItemBackgroundBorderless"
-        android:layout_gravity="center_vertical|end"/>
-</FrameLayout>
\ No newline at end of file
+        android:layout_height="40dp"
+        android:paddingStart="24dp"
+        android:paddingEnd="4dp"
+        android:background="@drawable/recents_onboarding_toast_rounded_background"
+        android:layout_gravity="center_horizontal"
+        android:elevation="2dp"
+        android:orientation="horizontal">
+
+        <TextView
+            android:id="@+id/onboarding_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_vertical"
+            android:textColor="@android:color/white"
+            android:textSize="16sp"/>
+        <ImageView
+            android:id="@+id/dismiss"
+            android:layout_width="40dp"
+            android:layout_height="40dp"
+            android:layout_gravity="center_vertical"
+            android:padding="10dp"
+            android:layout_marginStart="2dp"
+            android:layout_marginEnd="2dp"
+            android:alpha="0.7"
+            android:src="@drawable/ic_close_white"
+            android:background="?android:attr/selectableItemBackgroundBorderless"/>
+    </LinearLayout>
+
+    <View
+        android:id="@+id/arrow"
+        android:elevation="2dp"
+        android:layout_width="10dp"
+        android:layout_height="8dp"
+        android:layout_marginTop="-2dp"
+        android:layout_gravity="center_horizontal"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 84ca657..870aed4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -136,6 +136,10 @@
          views where the distance can't be measured -->
     <dimen name="notification_icon_appear_padding">15dp</dimen>
 
+    <!-- Vertical translation of the shelf during animation that happens after the
+    notification panel collapses -->
+    <dimen name="shelf_appear_translation">9dp</dimen>
+
     <!-- The amount the content shifts upwards when transforming into the icon -->
     <dimen name="notification_icon_transform_content_shift">32dp</dimen>
 
@@ -371,7 +375,7 @@
     <dimen name="qs_header_alarm_icon_size">18dp</dimen>
     <dimen name="qs_header_alarm_text_margin_start">6dp</dimen>
     <dimen name="qs_footer_padding_start">16dp</dimen>
-    <dimen name="qs_footer_padding_end">24dp</dimen>
+    <dimen name="qs_footer_padding_end">16dp</dimen>
     <dimen name="qs_footer_icon_size">16dp</dimen>
 
     <dimen name="qs_notif_collapsed_space">64dp</dimen>
@@ -841,6 +845,9 @@
     <!-- The size of the drag hint text. -->
     <dimen name="recents_drag_hint_text_size">14sp</dimen>
 
+    <!-- The size of corner radius of the arrow in the onboarding toast. -->
+    <dimen name="recents_onboarding_toast_arrow_corner_radius">2dp</dimen>
+
     <!-- The min alpha to apply to a task affiliation group color. -->
     <item name="recents_task_affiliation_color_min_alpha_percentage" format="float" type="dimen">0.6</item>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 0e92c60..d45c427 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -98,5 +98,8 @@
     <item type="id" name="status_bar_view_state_tag" />
 
     <item type="id" name="display_cutout" />
+
+    <!-- Optional cancel button on Keyguard -->
+    <item type="id" name="cancel_button"/>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index da6ecc3..29f1335 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -366,10 +366,10 @@
     <string name="data_connection_3g">3G</string>
 
     <!-- Content description of the data connection type 3.5G. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_3_5g">3.5G</string>
+    <string name="data_connection_3_5g">H</string>
 
     <!-- Content description of the data connection type 3.5G+. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_3_5g_plus">3.5G+</string>
+    <string name="data_connection_3_5g_plus">H+</string>
 
     <!-- Content description of the data connection type 4G . [CHAR LIMIT=NONE] -->
     <string name="data_connection_4g">4G</string>
@@ -384,7 +384,7 @@
     <string name="data_connection_lte_plus">LTE+</string>
 
     <!-- Content description of the data connection type CDMA. [CHAR LIMIT=NONE] -->
-    <string name="data_connection_cdma">CDMA</string>
+    <string name="data_connection_cdma">1X</string>
 
     <!-- Content description of the roaming data connection type. [CHAR LIMIT=NONE] -->
     <string name="data_connection_roaming">Roaming</string>
@@ -405,7 +405,10 @@
     <string name="accessibility_cell_data_on">Mobile Data On</string>
 
     <!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] -->
-    <string name="cell_data_off">Mobile data off</string>
+    <string name="cell_data_off_content_description">Mobile data off</string>
+
+    <!-- Content description of the cell data being disabled but shortened. [CHAR LIMIT=20] -->
+    <string name="cell_data_off">Off</string>
 
     <!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
     <string name="accessibility_bluetooth_tether">Bluetooth tethering.</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
index 20d1418..78b1b2614 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/IconLoader.java
@@ -15,10 +15,13 @@
  */
 package com.android.systemui.shared.recents.model;
 
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -108,10 +111,12 @@
         }
         if (desc.getIconResource() != 0) {
             try {
-                Context packageContext = mContext.createPackageContextAsUser(
-                        taskKey.getPackageName(), 0, UserHandle.of(userId));
-                return createBadgedDrawable(packageContext.getDrawable(desc.getIconResource()),
-                        userId, desc);
+                PackageManager pm = mContext.getPackageManager();
+                ApplicationInfo appInfo = pm.getApplicationInfo(taskKey.getPackageName(),
+                        MATCH_ANY_USER);
+                Resources res = pm.getResourcesForApplication(appInfo);
+                return createBadgedDrawable(res.getDrawable(desc.getIconResource(), null), userId,
+                        desc);
             } catch (Resources.NotFoundException|PackageManager.NameNotFoundException e) {
                 Log.e(TAG, "Could not find icon drawable from resource", e);
             }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
new file mode 100644
index 0000000..b2b140e
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowCallbacksCompat.java
@@ -0,0 +1,109 @@
+/*
+ * 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.graphics.Canvas;
+import android.graphics.Rect;
+import android.view.DisplayListCanvas;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowCallbacks;
+
+public class WindowCallbacksCompat {
+
+    private final WindowCallbacks mWindowCallbacks = new WindowCallbacks() {
+        @Override
+        public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+                Rect stableInsets) {
+            WindowCallbacksCompat.this.onWindowSizeIsChanging(newBounds, fullscreen, systemInsets,
+                    stableInsets);
+        }
+
+        @Override
+        public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen,
+                Rect systemInsets, Rect stableInsets, int resizeMode) {
+            WindowCallbacksCompat.this.onWindowDragResizeStart(initialBounds, fullscreen,
+                    systemInsets, stableInsets, resizeMode);
+        }
+
+        @Override
+        public void onWindowDragResizeEnd() {
+            WindowCallbacksCompat.this.onWindowDragResizeEnd();
+        }
+
+        @Override
+        public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+            return WindowCallbacksCompat.this.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
+        }
+
+        @Override
+        public void onRequestDraw(boolean reportNextDraw) {
+            WindowCallbacksCompat.this.onRequestDraw(reportNextDraw);
+        }
+
+        @Override
+        public void onPostDraw(DisplayListCanvas canvas) {
+            WindowCallbacksCompat.this.onPostDraw(canvas);
+        }
+    };
+
+    private final View mView;
+
+    public WindowCallbacksCompat(View view) {
+        mView = view;
+    }
+
+    public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets) { }
+
+    public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
+            Rect stableInsets, int resizeMode) { }
+
+    public void onWindowDragResizeEnd() { }
+
+    public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
+        return false;
+    }
+
+    public void onRequestDraw(boolean reportNextDraw) {
+        if (reportNextDraw) {
+            reportDrawFinish();
+        }
+    }
+
+    public void onPostDraw(Canvas canvas) { }
+
+    public void reportDrawFinish() {
+        mView.getViewRootImpl().reportDrawFinish();
+    }
+
+    public boolean attach() {
+        ViewRootImpl root = mView.getViewRootImpl();
+        if (root != null) {
+            root.addWindowCallbacks(mWindowCallbacks);
+            root.requestInvalidateRootRenderNode();
+            return true;
+        }
+        return false;
+    }
+
+    public void detach() {
+        ViewRootImpl root = mView.getViewRootImpl();
+        if (root != null) {
+            root.removeWindowCallbacks(mWindowCallbacks);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d63ad08..00cd5a7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -265,11 +265,11 @@
             mPendingLockCheck.cancel(false);
             mPendingLockCheck = null;
         }
+        reset();
     }
 
     @Override
     public void onResume(int reason) {
-        reset();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index c1cff9e..adb2460 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -107,6 +107,13 @@
                 new View[]{
                         null, mEcaView, null
                 }};
+
+        View cancelBtn = findViewById(R.id.cancel_button);
+        if (cancelBtn != null) {
+            cancelBtn.setOnClickListener(view -> {
+                mCallback.reset();
+            });
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 75c52d8..7cc37c4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -205,6 +205,13 @@
             }
         });
 
+        View cancelBtn = findViewById(R.id.cancel_button);
+        if (cancelBtn != null) {
+            cancelBtn.setOnClickListener(view -> {
+                mCallback.reset();
+            });
+        }
+
         // If there's more than one IME, enable the IME switcher button
         updateSwitchImeButton();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 651831e..174dcab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -157,6 +157,13 @@
         if (button != null) {
             button.setCallback(this);
         }
+
+        View cancelBtn = findViewById(R.id.cancel_button);
+        if (cancelBtn != null) {
+            cancelBtn.setOnClickListener(view -> {
+                mCallback.reset();
+            });
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index c71c433..42c7a56 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -78,6 +78,11 @@
                     }
                     break;
                 }
+                case READY: {
+                    mRemainingAttempts = -1;
+                    resetState();
+                    break;
+                }
                 default:
                     resetState();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index a0fa69e..e54b083 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -14,6 +14,10 @@
 
 package com.android.systemui;
 
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -21,12 +25,14 @@
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
 
+import android.annotation.Dimension;
 import android.app.Fragment;
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.PixelFormat;
@@ -41,6 +47,7 @@
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.LayoutInflater;
+import android.view.Surface;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup;
@@ -359,6 +366,7 @@
             if (!mBoundingPath.isEmpty()) {
                 mPaint.setColor(Color.BLACK);
                 mPaint.setStyle(Paint.Style.FILL);
+                mPaint.setAntiAlias(true);
                 canvas.drawPath(mBoundingPath, mPaint);
             }
         }
@@ -388,7 +396,7 @@
             if (hasCutout()) {
                 mBounds.set(mInfo.displayCutout.getBounds());
                 localBounds(mBoundingRect);
-                mInfo.displayCutout.getBounds().getBoundaryPath(mBoundingPath);
+                updateBoundingPath();
                 invalidate();
                 newVisible = VISIBLE;
             } else {
@@ -400,6 +408,44 @@
             }
         }
 
+        private void updateBoundingPath() {
+            int lw = mInfo.logicalWidth;
+            int lh = mInfo.logicalHeight;
+
+            boolean flipped = mInfo.rotation == ROTATION_90 || mInfo.rotation == ROTATION_270;
+
+            int dw = flipped ? lh : lw;
+            int dh = flipped ? lw : lh;
+
+            mBoundingPath.set(DisplayCutout.pathFromResources(getResources(), lw, lh));
+            Matrix m = new Matrix();
+            transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
+            mBoundingPath.transform(m);
+        }
+
+        private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
+                @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+            switch (rotation) {
+                case ROTATION_0:
+                    out.reset();
+                    break;
+                case ROTATION_90:
+                    out.setRotate(270);
+                    out.postTranslate(0, physicalWidth);
+                    break;
+                case ROTATION_180:
+                    out.setRotate(180);
+                    out.postTranslate(physicalWidth, physicalHeight);
+                    break;
+                case ROTATION_270:
+                    out.setRotate(90);
+                    out.postTranslate(physicalHeight, 0);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unknown rotation: " + rotation);
+            }
+        }
+
         private boolean hasCutout() {
             final DisplayCutout displayCutout = mInfo.displayCutout;
             if (displayCutout == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 0ba26e9..52d458c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -25,6 +25,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.keyguard.ViewMediatorCallback;
 import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.classifier.FalsingManager;
@@ -41,6 +42,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.ScrimView;
+import com.android.systemui.statusbar.SmartReplyLogger;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -101,12 +103,13 @@
                 dismissCallbackRegistry, FalsingManager.getInstance(context));
     }
 
-    public ScrimController createScrimController(LightBarController lightBarController,
-            ScrimView scrimBehind, ScrimView scrimInFront, LockscreenWallpaper lockscreenWallpaper,
+    public ScrimController createScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
+            LockscreenWallpaper lockscreenWallpaper, Consumer<Float> scrimBehindAlphaListener,
+            Consumer<GradientColors> scrimInFrontColorListener,
             Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
             AlarmManager alarmManager) {
-        return new ScrimController(lightBarController, scrimBehind, scrimInFront,
-                scrimVisibleListener, dozeParameters, alarmManager);
+        return new ScrimController(scrimBehind, scrimInFront, scrimBehindAlphaListener,
+                scrimInFrontColorListener, scrimVisibleListener, dozeParameters, alarmManager);
     }
 
     public NotificationIconAreaController createNotificationIconAreaController(Context context,
@@ -144,5 +147,6 @@
                 () -> new NotificationViewHierarchyManager(context));
         providers.put(NotificationEntryManager.class, () -> new NotificationEntryManager(context));
         providers.put(KeyguardDismissUtil.class, KeyguardDismissUtil::new);
+        providers.put(SmartReplyLogger.class, () -> new SmartReplyLogger(context));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index 9464105..ebd15f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -613,7 +613,7 @@
                                           int bluetoothProfile) { }
 
         @Override
-        public void onProfileAudioStateChanged(int bluetoothProfile, int state) { }
+        public void onAudioModeChanged() { }
     }
 
     private final class BluetoothErrorListener implements Utils.ErrorListener {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d6e59c7..553b2ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -357,7 +357,12 @@
             // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
             synchronized (KeyguardViewMediator.this) {
                 resetKeyguardDonePendingLocked();
-                resetStateLocked();
+                if (mLockPatternUtils.isLockScreenDisabled(userId)) {
+                    // If we switching to a user that has keyguard disabled, dismiss keyguard.
+                    dismiss(null /* callback */, null /* message */);
+                } else {
+                    resetStateLocked();
+                }
                 adjustStatusBarLocked();
             }
         }
@@ -1184,6 +1189,10 @@
         Trace.endSection();
     }
 
+    public boolean isHiding() {
+        return mHiding;
+    }
+
     /**
      * Handles SET_OCCLUDED message sent by setOccluded()
      */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7161463..a9455f2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -16,17 +16,19 @@
 
 package com.android.systemui.qs;
 
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+
 import android.content.Context;
 import android.content.res.Configuration;
 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.SysUiServiceProvider;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.CommandQueue;
 
 /**
  * Wrapper view with background which contains {@link QSPanel} and {@link BaseStatusBarHeader}
@@ -48,6 +50,7 @@
     private View mStatusBarBackground;
 
     private int mSideMargins;
+    private boolean mQsDisabled;
 
     public QSContainerImpl(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -85,6 +88,7 @@
         }
 
         updateResources();
+        mSizePoint.set(0, 0); // Will be retrieved on next measure pass.
     }
 
     @Override
@@ -96,6 +100,16 @@
 
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mQsDisabled) {
+            // Only show the status bar contents in QQS header when QS is disabled.
+            mHeader.measure(widthMeasureSpec, heightMeasureSpec);
+            LayoutParams layoutParams = (LayoutParams) mHeader.getLayoutParams();
+            int height = layoutParams.topMargin + layoutParams.bottomMargin
+                    + mHeader.getMeasuredHeight();
+            super.onMeasure(
+                    widthMeasureSpec, MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+            return;
+        }
         // Since we control our own bottom, be whatever size we want.
         // Otherwise the QSPanel ends up with 0 height when the window is only the
         // size of the status bar.
@@ -110,9 +124,8 @@
 
         // QSCustomizer will always be the height of the screen, but do this after
         // other measuring to avoid changing the height of the QS.
-        getDisplay().getRealSize(mSizePoint);
         mQSCustomizer.measure(widthMeasureSpec,
-                MeasureSpec.makeMeasureSpec(mSizePoint.y, MeasureSpec.EXACTLY));
+                MeasureSpec.makeMeasureSpec(getDisplayHeight(), MeasureSpec.EXACTLY));
     }
 
     @Override
@@ -121,6 +134,15 @@
         updateExpansion();
     }
 
+    public void disable(int state1, int state2, boolean animate) {
+        final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
+        if (disabled == mQsDisabled) return;
+        mQsDisabled = disabled;
+        mBackgroundGradient.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        mQSPanel.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        mQSFooter.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+    }
+
     private void updateResources() {
         LayoutParams layoutParams = (LayoutParams) mQSPanel.getLayoutParams();
         layoutParams.topMargin = mContext.getResources().getDimensionPixelSize(
@@ -175,4 +197,11 @@
         lp.rightMargin = mSideMargins;
         lp.leftMargin = mSideMargins;
     }
+
+    private int getDisplayHeight() {
+        if (mSizePoint.y == 0) {
+            getDisplay().getRealSize(mSizePoint);
+        }
+        return mSizePoint.y;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 3f3cea2..6c7eda7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -69,4 +69,6 @@
      */
     @Nullable
     View getExpandView();
+
+    default void disable(int state1, int state2, boolean animate) {}
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
index 0fa6597..28dd26f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java
@@ -47,10 +47,8 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.R.dimen;
-import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.TouchAnimator.Builder;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.MultiUserSwitch;
 import com.android.systemui.statusbar.phone.SettingsButton;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -62,8 +60,7 @@
 import com.android.systemui.tuner.TunerService;
 
 public class QSFooterImpl extends FrameLayout implements QSFooter,
-        OnClickListener, OnUserInfoChangedListener, EmergencyListener,
-        SignalCallback, CommandQueue.Callbacks {
+        OnClickListener, OnUserInfoChangedListener, EmergencyListener, SignalCallback {
 
     private ActivityStarter mActivityStarter;
     private UserInfoController mUserInfoController;
@@ -211,16 +208,9 @@
     }
 
     @Override
-    public void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
-    }
-
-    @Override
     @VisibleForTesting
     public void onDetachedFromWindow() {
         setListening(false);
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
         super.onDetachedFromWindow();
     }
 
@@ -393,7 +383,7 @@
             if (TextUtils.equals(mInfo.typeContentDescription,
                     mContext.getString(R.string.data_connection_no_internet))
                 || TextUtils.equals(mInfo.typeContentDescription,
-                    mContext.getString(R.string.cell_data_off))) {
+                    mContext.getString(R.string.cell_data_off_content_description))) {
                 contentDescription.append(mInfo.typeContentDescription);
             }
             mMobileSignal.setContentDescription(contentDescription);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 018a635..cb068e3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -14,9 +14,12 @@
 
 package com.android.systemui.qs;
 
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.app.Fragment;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -35,12 +38,14 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.R.id;
+import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.customize.QSCustomizer;
+import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
 
-public class QSFragment extends Fragment implements QS {
+public class QSFragment extends Fragment implements QS, CommandQueue.Callbacks {
     private static final String TAG = "QS";
     private static final boolean DEBUG = false;
     private static final String EXTRA_EXPANDED = "expanded";
@@ -65,6 +70,7 @@
     private int mLayoutDirection;
     private QSFooter mFooter;
     private float mLastQSExpansion = -1;
+    private boolean mQsDisabled;
 
     @Override
     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@@ -176,6 +182,17 @@
         }
     }
 
+    @Override
+    public void disable(int state1, int state2, boolean animate) {
+        final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
+        if (disabled == mQsDisabled) return;
+        mQsDisabled = disabled;
+        mContainer.disable(state1, state2, animate);
+        mHeader.disable(state1, state2, animate);
+        mFooter.disable(state1, state2, animate);
+        updateQsState();
+    }
+
     private void updateQsState() {
         final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling
                 || mHeaderAnimating;
@@ -189,6 +206,9 @@
         mFooter.setVisibility((mQsExpanded || !mKeyguardShowing || mHeaderAnimating)
                 ? View.VISIBLE
                 : View.INVISIBLE);
+        if (mQsDisabled) {
+            mFooter.setVisibility(View.GONE);
+        }
         mFooter.setExpanded((mKeyguardShowing && !mHeaderAnimating)
                 || (mQsExpanded && !mStackScrollerOverscrolling));
         mQSPanel.setVisibility(expandVisually ? View.VISIBLE : View.INVISIBLE);
@@ -258,6 +278,12 @@
         mHeader.setListening(listening);
         mFooter.setListening(listening);
         mQSPanel.setListening(mListening && mQsExpanded);
+        if (listening) {
+            SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
+        } else {
+            SysUiServiceProvider.getComponent(getContext(), CommandQueue.class)
+                    .removeCallbacks(this);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 70bfad1..e2af90d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -68,7 +68,7 @@
  * battery) and also contains the {@link QuickQSPanel} along with some of the panel's inner
  * contents.
  */
-public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue.Callbacks,
+public class QuickStatusBarHeader extends RelativeLayout implements
         View.OnClickListener, NextAlarmController.NextAlarmChangeCallback {
     private static final String TAG = "QuickStatusBarHeader";
     private static final boolean DEBUG = false;
@@ -249,8 +249,9 @@
                 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);
+        getLayoutParams().height = resources.getDimensionPixelSize(mQsDisabled
+                ? com.android.internal.R.dimen.quick_qs_offset_height
+                : com.android.internal.R.dimen.quick_qs_total_height);
         setLayoutParams(getLayoutParams());
 
         updateStatusIconAlphaAnimator();
@@ -320,20 +321,18 @@
                 TOOLTIP_NOT_YET_SHOWN_COUNT);
     }
 
-    @Override
     public void disable(int state1, int state2, boolean animate) {
         final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
         if (disabled == mQsDisabled) return;
         mQsDisabled = disabled;
         mHeaderQsPanel.setDisabledByPolicy(disabled);
-        final int rawHeight = (int) getResources().getDimension(
-                com.android.internal.R.dimen.quick_qs_total_height);
-        getLayoutParams().height = disabled ? (rawHeight - mHeaderQsPanel.getHeight()) : rawHeight;
+        mHeaderTextContainerView.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        mQuickQsStatusIcons.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
+        updateResources();
     }
 
     @Override
     public void onAttachedToWindow() {
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
         Dependency.get(StatusBarIconController.class).addIconGroup(mIconManager);
         requestApplyInsets();
     }
@@ -354,7 +353,6 @@
     @VisibleForTesting
     public void onDetachedFromWindow() {
         setListening(false);
-        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).removeCallbacks(this);
         Dependency.get(StatusBarIconController.class).removeIconGroup(mIconManager);
         super.onDetachedFromWindow();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 2abe9d9..4554c79 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -194,7 +194,18 @@
             state.state = Tile.STATE_INACTIVE;
             state.secondaryLabel = r.getString(R.string.cell_data_off);
         }
-        state.contentDescription = state.label + ", " + state.secondaryLabel;
+
+
+        // TODO(b/77881974): Instead of switching out the description via a string check for
+        // we need to have two strings provided by the MobileIconGroup.
+        final CharSequence contentDescriptionSuffix;
+        if (state.state == Tile.STATE_INACTIVE) {
+            contentDescriptionSuffix = r.getString(R.string.cell_data_off_content_description);
+        } else {
+            contentDescriptionSuffix = state.secondaryLabel;
+        }
+
+        state.contentDescription = state.label + ", " + contentDescriptionSuffix;
     }
 
     private CharSequence getMobileDataDescription(CallbackInfo cb) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index f7cbee6..4616d1e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -143,26 +143,41 @@
     public void showDetail(boolean show) {
         int zenDuration = Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.ZEN_DURATION, 0);
-        switch (zenDuration) {
-            case Settings.Global.ZEN_DURATION_PROMPT:
-                mUiHandler.post(() -> {
-                    Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
-                    mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-                    SystemUIDialog.setShowForAllUsers(mDialog, true);
-                    SystemUIDialog.registerDismissListener(mDialog);
-                    SystemUIDialog.setWindowOnTop(mDialog);
-                    mUiHandler.post(() -> mDialog.show());
-                    mHost.collapsePanels();
-                });
-                break;
-            case Settings.Global.ZEN_DURATION_FOREVER:
-                mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
-                break;
-            default:
-                Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
-                        ActivityManager.getCurrentUser(), true).id;
-                mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
-                        conditionId, TAG);
+        boolean showOnboarding = Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0) != 0;
+        if (showOnboarding) {
+            // don't show on-boarding again or notification ever
+            Settings.Global.putInt(mContext.getContentResolver(),
+                    Global.SHOW_ZEN_UPGRADE_NOTIFICATION, 0);
+            // turn on DND
+            mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
+            // show on-boarding screen
+            Intent intent = new Intent(Settings.ZEN_MODE_ONBOARDING);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+            Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(intent, 0);
+        } else {
+            switch (zenDuration) {
+                case Settings.Global.ZEN_DURATION_PROMPT:
+                    mUiHandler.post(() -> {
+                        Dialog mDialog = new EnableZenModeDialog(mContext).createDialog();
+                        mDialog.getWindow().setType(
+                                WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+                        SystemUIDialog.setShowForAllUsers(mDialog, true);
+                        SystemUIDialog.registerDismissListener(mDialog);
+                        SystemUIDialog.setWindowOnTop(mDialog);
+                        mUiHandler.post(() -> mDialog.show());
+                        mHost.collapsePanels();
+                    });
+                    break;
+                case Settings.Global.ZEN_DURATION_FOREVER:
+                    mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, TAG);
+                    break;
+                default:
+                    Uri conditionId = ZenModeConfig.toTimeCondition(mContext, zenDuration,
+                            ActivityManager.getCurrentUser(), true).id;
+                    mController.setZen(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                            conditionId, TAG);
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index 75bc9558..0d8aed4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -21,19 +21,19 @@
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
 import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.RippleDrawable;
+import android.graphics.drawable.ShapeDrawable;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -62,6 +62,7 @@
 
     private static final String TAG = "RecentsOnboarding";
     private static final boolean RESET_PREFS_FOR_DEBUG = false;
+    private static final boolean ONBOARDING_ENABLED = false;
     private static final long SHOW_DELAY_MS = 500;
     private static final long SHOW_HIDE_DURATION_MS = 300;
     // Don't show the onboarding until the user has launched this number of apps.
@@ -76,17 +77,13 @@
     private final View mLayout;
     private final TextView mTextView;
     private final ImageView mDismissView;
-    private final ColorDrawable mBackgroundDrawable;
-    private final int mDarkBackgroundColor;
-    private final int mLightBackgroundColor;
-    private final int mDarkContentColor;
-    private final int mLightContentColor;
-    private final RippleDrawable mDarkRipple;
-    private final RippleDrawable mLightRipple;
+    private final View mArrowView;
+    private final int mOnboardingToastColor;
+    private final int mOnboardingToastArrowRadius;
+    private int mNavBarHeight;
 
     private boolean mTaskListenerRegistered;
     private boolean mLayoutAttachedToWindow;
-    private boolean mBackgroundIsLight;
     private int mLastTaskId;
     private boolean mHasDismissed;
     private int mNumAppsLaunchedSinceDismiss;
@@ -159,24 +156,30 @@
         mLayout = LayoutInflater.from(mContext).inflate(R.layout.recents_onboarding, null);
         mTextView = mLayout.findViewById(R.id.onboarding_text);
         mDismissView = mLayout.findViewById(R.id.dismiss);
-        mDarkBackgroundColor = res.getColor(android.R.color.background_dark);
-        mLightBackgroundColor = res.getColor(android.R.color.background_light);
-        mDarkContentColor = res.getColor(R.color.primary_text_default_material_light);
-        mLightContentColor = res.getColor(R.color.primary_text_default_material_dark);
-        mDarkRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_light),
-                null, null);
-        mLightRipple = new RippleDrawable(res.getColorStateList(R.color.ripple_material_dark),
-                null, null);
-        mBackgroundDrawable = new ColorDrawable(mDarkBackgroundColor);
+        mArrowView = mLayout.findViewById(R.id.arrow);
+
+        TypedValue typedValue = new TypedValue();
+        context.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+        mOnboardingToastColor = res.getColor(typedValue.resourceId);
+        mOnboardingToastArrowRadius = res.getDimensionPixelSize(
+                R.dimen.recents_onboarding_toast_arrow_corner_radius);
 
         mLayout.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        mLayout.setBackground(mBackgroundDrawable);
         mDismissView.setOnClickListener(v -> {
             hide(true);
             mHasDismissed = true;
             mNumAppsLaunchedSinceDismiss = 0;
         });
 
+        ViewGroup.LayoutParams arrowLp = mArrowView.getLayoutParams();
+        ShapeDrawable arrowDrawable = new ShapeDrawable(TriangleShape.create(
+                arrowLp.width, arrowLp.height, false));
+        Paint arrowPaint = arrowDrawable.getPaint();
+        arrowPaint.setColor(mOnboardingToastColor);
+        // The corner path effect won't be reflected in the shadow, but shouldn't be noticeable.
+        arrowPaint.setPathEffect(new CornerPathEffect(mOnboardingToastArrowRadius));
+        mArrowView.setBackground(arrowDrawable);
+
         if (RESET_PREFS_FOR_DEBUG) {
             Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
             Prefs.putInt(mContext, Prefs.Key.NUM_APPS_LAUNCHED, 0);
@@ -184,6 +187,9 @@
     }
 
     public void onConnectedToLauncher() {
+        if (!ONBOARDING_ENABLED) {
+            return;
+        }
         boolean alreadySeenRecentsOnboarding = Prefs.getBoolean(mContext,
                 Prefs.Key.HAS_SEEN_RECENTS_ONBOARDING, false);
         if (!mTaskListenerRegistered && !alreadySeenRecentsOnboarding) {
@@ -231,6 +237,7 @@
         int orientation = mContext.getResources().getConfiguration().orientation;
         if (!mLayoutAttachedToWindow && orientation == Configuration.ORIENTATION_PORTRAIT) {
             mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+
             mWindowManager.addView(mLayout, getWindowLayoutParams());
             int layoutHeight = mLayout.getHeight();
             if (layoutHeight == 0) {
@@ -278,29 +285,18 @@
         }
     }
 
-    public void setContentDarkIntensity(float contentDarkIntensity) {
-        boolean backgroundIsLight = contentDarkIntensity > 0.5f;
-        if (backgroundIsLight != mBackgroundIsLight) {
-            mBackgroundIsLight = backgroundIsLight;
-            mBackgroundDrawable.setColor(mBackgroundIsLight
-                    ? mLightBackgroundColor : mDarkBackgroundColor);
-            int contentColor = mBackgroundIsLight ? mDarkContentColor : mLightContentColor;
-            mTextView.setTextColor(contentColor);
-            mTextView.getCompoundDrawables()[3].setColorFilter(contentColor,
-                    PorterDuff.Mode.SRC_IN);
-            mDismissView.setColorFilter(contentColor);
-            mDismissView.setBackground(mBackgroundIsLight ? mDarkRipple : mLightRipple);
-        }
+    public void setNavBarHeight(int navBarHeight) {
+        mNavBarHeight = navBarHeight;
     }
 
     private WindowManager.LayoutParams getWindowLayoutParams() {
-        int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
-                | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR
+        int flags = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.MATCH_PARENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG,
+                0, -mNavBarHeight / 2,
+                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                 flags,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
new file mode 100644
index 0000000..de85c0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/TriangleShape.java
@@ -0,0 +1,56 @@
+/*
+ * 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.recents;
+
+import android.graphics.Outline;
+import android.graphics.Path;
+import android.graphics.drawable.shapes.PathShape;
+import android.support.annotation.NonNull;
+
+/**
+ * Wrapper around {@link android.graphics.drawable.shapes.PathShape}
+ * that creates a shape with a triangular path (pointing up or down).
+ */
+public class TriangleShape extends PathShape {
+    private Path mTriangularPath;
+
+    public TriangleShape(Path path, float stdWidth, float stdHeight) {
+        super(path, stdWidth, stdHeight);
+        mTriangularPath = path;
+    }
+
+    public static TriangleShape create(float width, float height, boolean isPointingUp) {
+        Path triangularPath = new Path();
+        if (isPointingUp) {
+            triangularPath.moveTo(0, height);
+            triangularPath.lineTo(width, height);
+            triangularPath.lineTo(width / 2, 0);
+            triangularPath.close();
+        } else {
+            triangularPath.moveTo(0, 0);
+            triangularPath.lineTo(width / 2, height);
+            triangularPath.lineTo(width, 0);
+            triangularPath.close();
+        }
+        return new TriangleShape(triangularPath, width, height);
+    }
+
+    @Override
+    public void getOutline(@NonNull Outline outline) {
+        outline.setConvexPath(mTriangularPath);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 58bc4dd..87e6608 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -436,19 +436,13 @@
     }
 
     /**
-     * Returns whether this row is considered non-blockable (e.g. it's a non-blockable system notif,
-     * covers multiple channels, or is in a whitelist).
+     * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif
+     * or is in a whitelist).
      */
     public boolean getIsNonblockable() {
         boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
                 .isNonblockablePackage(mStatusBarNotification.getPackageName());
 
-        // Only bother with going through the children if the row is still blockable based on the
-        // number of unique channels.
-        if (!isNonblockable) {
-            isNonblockable = getNumUniqueChannels() > 1;
-        }
-
         // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
         // again, but in-place on the main thread this time. This should rarely ever get called.
         if (mIsSystemNotification == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 29c2edc..4256cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -80,7 +80,7 @@
     private RemoteInputView mHeadsUpRemoteInput;
 
     private SmartReplyConstants mSmartReplyConstants;
-    private SmartReplyView mExpandedSmartReplyView;
+    private SmartReplyLogger mSmartReplyLogger;
 
     private NotificationViewWrapper mContractedWrapper;
     private NotificationViewWrapper mExpandedWrapper;
@@ -153,6 +153,7 @@
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext(), this);
         mSmartReplyConstants = Dependency.get(SmartReplyConstants.class);
+        mSmartReplyLogger = Dependency.get(SmartReplyLogger.class);
         initView();
     }
 
@@ -1243,7 +1244,7 @@
         }
 
         applyRemoteInput(entry, hasRemoteInput);
-        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices);
+        applySmartReplyView(remoteInputWithChoices, pendingIntentWithChoices, entry);
     }
 
     private void applyRemoteInput(NotificationData.Entry entry, boolean hasRemoteInput) {
@@ -1344,13 +1345,21 @@
         return null;
     }
 
-    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent) {
-        mExpandedSmartReplyView = mExpandedChild == null ?
-                null : applySmartReplyView(mExpandedChild, remoteInput, pendingIntent);
+    private void applySmartReplyView(RemoteInput remoteInput, PendingIntent pendingIntent,
+            NotificationData.Entry entry) {
+        if (mExpandedChild != null) {
+            SmartReplyView view =
+                    applySmartReplyView(mExpandedChild, remoteInput, pendingIntent, entry);
+            if (view != null && remoteInput != null && remoteInput.getChoices() != null
+                    && remoteInput.getChoices().length > 0) {
+                mSmartReplyLogger.smartRepliesAdded(entry, remoteInput.getChoices().length);
+            }
+        }
     }
 
     private SmartReplyView applySmartReplyView(
-            View view, RemoteInput remoteInput, PendingIntent pendingIntent) {
+            View view, RemoteInput remoteInput, PendingIntent pendingIntent,
+            NotificationData.Entry entry) {
         View smartReplyContainerCandidate = view.findViewById(
                 com.android.internal.R.id.smart_reply_container);
         if (!(smartReplyContainerCandidate instanceof LinearLayout)) {
@@ -1372,7 +1381,8 @@
             }
         }
         if (smartReplyView != null) {
-            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent);
+            smartReplyView.setRepliesFromRemoteInput(remoteInput, pendingIntent,
+                    mSmartReplyLogger, entry);
             smartReplyContainer.setVisibility(View.VISIBLE);
         }
         return smartReplyView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index a93be00..81dd9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -23,6 +23,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
 import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
@@ -34,10 +35,12 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.service.notification.StatusBarNotification;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
@@ -63,10 +66,10 @@
     private INotificationManager mINotificationManager;
     private PackageManager mPm;
 
-    private String mPkg;
+    private String mPackageName;
     private String mAppName;
     private int mAppUid;
-    private int mNumNotificationChannels;
+    private int mNumUniqueChannelsInRow;
     private NotificationChannel mSingleNotificationChannel;
     private int mStartingUserImportance;
     private int mChosenImportance;
@@ -87,7 +90,7 @@
 
     private OnClickListener mOnKeepShowing = this::closeControls;
 
-    private OnClickListener mOnStopMinNotifications = v -> {
+    private OnClickListener mOnStopOrMinimizeNotifications = v -> {
         swapContent(false);
     };
 
@@ -120,16 +123,16 @@
             final INotificationManager iNotificationManager,
             final String pkg,
             final NotificationChannel notificationChannel,
-            final int numChannels,
+            final int numUniqueChannelsInRow,
             final StatusBarNotification sbn,
             final CheckSaveListener checkSaveListener,
             final OnSettingsClickListener onSettingsClick,
             final OnAppSettingsClickListener onAppSettingsClick,
             boolean isNonblockable)
             throws RemoteException {
-        bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn,
-                checkSaveListener, onSettingsClick, onAppSettingsClick, isNonblockable,
-                false /* isBlockingHelper */,
+        bindNotification(pm, iNotificationManager, pkg, notificationChannel,
+                numUniqueChannelsInRow, sbn, checkSaveListener, onSettingsClick,
+                onAppSettingsClick, isNonblockable, false /* isBlockingHelper */,
                 false /* isUserSentimentNegative */);
     }
 
@@ -138,7 +141,7 @@
             INotificationManager iNotificationManager,
             String pkg,
             NotificationChannel notificationChannel,
-            int numChannels,
+            int numUniqueChannelsInRow,
             StatusBarNotification sbn,
             CheckSaveListener checkSaveListener,
             OnSettingsClickListener onSettingsClick,
@@ -148,12 +151,12 @@
             boolean isUserSentimentNegative)
             throws RemoteException {
         mINotificationManager = iNotificationManager;
-        mPkg = pkg;
-        mNumNotificationChannels = numChannels;
+        mPackageName = pkg;
+        mNumUniqueChannelsInRow = numUniqueChannelsInRow;
         mSbn = sbn;
         mPm = pm;
         mAppSettingsClickListener = onAppSettingsClick;
-        mAppName = mPkg;
+        mAppName = mPackageName;
         mCheckSaveListener = checkSaveListener;
         mOnSettingsClickListener = onSettingsClick;
         mSingleNotificationChannel = notificationChannel;
@@ -167,11 +170,11 @@
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
-        if (mNumNotificationChannels == 0) {
+        if (mNumUniqueChannelsInRow == 0) {
             throw new IllegalArgumentException("bindNotification requires at least one channel");
         } else  {
             // Special behavior for the Default channel if no other channels have been defined.
-            mIsSingleDefaultChannel = mNumNotificationChannels == 1
+            mIsSingleDefaultChannel = mNumUniqueChannelsInRow == 1
                     && mSingleNotificationChannel.getId().equals(
                             NotificationChannel.DEFAULT_CHANNEL_ID)
                     && numTotalChannels == 1;
@@ -187,7 +190,8 @@
         Drawable pkgicon = null;
         ApplicationInfo info;
         try {
-            info = mPm.getApplicationInfo(mPkg,
+            info = mPm.getApplicationInfo(
+                    mPackageName,
                     PackageManager.MATCH_UNINSTALLED_PACKAGES
                             | PackageManager.MATCH_DISABLED_COMPONENTS
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@@ -208,7 +212,7 @@
         if (mSingleNotificationChannel != null && mSingleNotificationChannel.getGroup() != null) {
             final NotificationChannelGroup notificationChannelGroup =
                     mINotificationManager.getNotificationChannelGroupForPackage(
-                            mSingleNotificationChannel.getGroup(), mPkg, mAppUid);
+                            mSingleNotificationChannel.getGroup(), mPackageName, mAppUid);
             if (notificationChannelGroup != null) {
                 groupName = notificationChannelGroup.getName();
             }
@@ -232,7 +236,7 @@
             settingsButton.setOnClickListener(
                     (View view) -> {
                         mOnSettingsClickListener.onClick(view,
-                                mNumNotificationChannels > 1 ? null : mSingleNotificationChannel,
+                                mNumUniqueChannelsInRow > 1 ? null : mSingleNotificationChannel,
                                 appUidF);
                     });
         } else {
@@ -248,7 +252,7 @@
         } else {
             if (mNegativeUserSentiment) {
                 blockPrompt.setText(R.string.inline_blocking_helper);
-            }  else if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) {
+            }  else if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
                 blockPrompt.setText(R.string.inline_keep_showing_app);
             } else {
                 blockPrompt.setText(R.string.inline_keep_showing);
@@ -258,7 +262,7 @@
 
     private void bindName() {
         final TextView channelName = findViewById(R.id.channel_name);
-        if (mIsSingleDefaultChannel || mNumNotificationChannels > 1) {
+        if (mIsSingleDefaultChannel || mNumUniqueChannelsInRow > 1) {
             channelName.setVisibility(View.GONE);
         } else {
             channelName.setText(mSingleNotificationChannel.getName());
@@ -270,19 +274,26 @@
     }
 
     private void saveImportance() {
-        if (mIsNonblockable) {
-            return;
+        if (!mIsNonblockable) {
+            if (mCheckSaveListener != null) {
+                mCheckSaveListener.checkSave(this::updateImportance, mSbn);
+            } else {
+                updateImportance();
+            }
         }
+    }
+
+    /**
+     * Commits the updated importance values on the background thread.
+     */
+    private void updateImportance() {
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
                 mChosenImportance - mStartingUserImportance);
-        mSingleNotificationChannel.setImportance(mChosenImportance);
-        mSingleNotificationChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
-        try {
-            mINotificationManager.updateNotificationChannelForPackage(
-                    mPkg, mAppUid, mSingleNotificationChannel);
-        } catch (RemoteException e) {
-            // :(
-        }
+
+        Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
+        bgHandler.post(new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
+                mNumUniqueChannelsInRow == 1 ? mSingleNotificationChannel : null,
+                mStartingUserImportance, mChosenImportance));
     }
 
     private void bindButtons() {
@@ -292,9 +303,9 @@
         View minimize = findViewById(R.id.minimize);
 
         findViewById(R.id.undo).setOnClickListener(mOnUndo);
-        block.setOnClickListener(mOnStopMinNotifications);
+        block.setOnClickListener(mOnStopOrMinimizeNotifications);
         keep.setOnClickListener(mOnKeepShowing);
-        minimize.setOnClickListener(mOnStopMinNotifications);
+        minimize.setOnClickListener(mOnStopOrMinimizeNotifications);
 
         if (mIsNonblockable) {
             keep.setText(R.string.notification_done);
@@ -308,15 +319,15 @@
             minimize.setVisibility(GONE);
         }
 
-        // Set up app settings link
+        // Set up app settings link (i.e. Customize)
         TextView settingsLinkView = findViewById(R.id.app_settings);
-        Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
+        Intent settingsIntent = getAppSettingsIntent(mPm, mPackageName, mSingleNotificationChannel,
                 mSbn.getId(), mSbn.getTag());
-        if (settingsIntent != null
+        if (!mIsForBlockingHelper
+                && settingsIntent != null
                 && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) {
             settingsLinkView.setVisibility(VISIBLE);
-            settingsLinkView.setText(mContext.getString(R.string.notification_app_settings,
-                    mSbn.getNotification().getSettingsText()));
+            settingsLinkView.setText(mContext.getString(R.string.notification_app_settings));
             settingsLinkView.setOnClickListener((View view) -> {
                 mAppSettingsClickListener.onClick(view, settingsIntent);
             });
@@ -415,12 +426,25 @@
         return intent;
     }
 
+    /**
+     * Closes the controls and commits the updated importance values (indirectly). If this view is
+     * being used to show the blocking helper, this will immediately dismiss the blocking helper and
+     * commit the updated importance.
+     *
+     * <p><b>Note,</b> this will only get called once the view is dismissing. This means that the
+     * user does not have the ability to undo the action anymore. See {@link #swapContent(boolean)}
+     * for where undo is handled.
+     */
     @VisibleForTesting
     void closeControls(View v) {
         if (mIsForBlockingHelper) {
             NotificationBlockingHelperManager manager =
                     Dependency.get(NotificationBlockingHelperManager.class);
             manager.dismissCurrentBlockingHelper();
+
+            // Since this won't get a callback via gutsContainer.closeControls, save the new
+            // importance values immediately.
+            saveImportance();
         } else {
             int[] parentLoc = new int[2];
             int[] targetLoc = new int[2];
@@ -454,11 +478,7 @@
         // Save regardless of the importance so we can lock the importance field if the user wants
         // to keep getting notifications
         if (save) {
-            if (mCheckSaveListener != null) {
-                mCheckSaveListener.checkSave(this::saveImportance, mSbn);
-            } else {
-                saveImportance();
-            }
+            saveImportance();
         }
         return false;
     }
@@ -467,4 +487,48 @@
     public int getActualHeight() {
         return getHeight();
     }
+
+    /**
+     * Runnable to either update the given channel (with a new importance value) or, if no channel
+     * is provided, update notifications enabled state for the package.
+     */
+    private static class UpdateImportanceRunnable implements Runnable {
+        private final INotificationManager mINotificationManager;
+        private final String mPackageName;
+        private final int mAppUid;
+        private final @Nullable NotificationChannel mChannelToUpdate;
+        private final int mCurrentImportance;
+        private final int mNewImportance;
+
+
+        public UpdateImportanceRunnable(INotificationManager notificationManager,
+                String packageName, int appUid, @Nullable NotificationChannel channelToUpdate,
+                int currentImportance, int newImportance) {
+            mINotificationManager = notificationManager;
+            mPackageName = packageName;
+            mAppUid = appUid;
+            mChannelToUpdate = channelToUpdate;
+            mCurrentImportance = currentImportance;
+            mNewImportance = newImportance;
+        }
+
+        @Override
+        public void run() {
+            try {
+                if (mChannelToUpdate != null) {
+                    mChannelToUpdate.setImportance(mNewImportance);
+                    mChannelToUpdate.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
+                    mINotificationManager.updateNotificationChannelForPackage(
+                            mPackageName, mAppUid, mChannelToUpdate);
+                } else {
+                    // For notifications with more than one channel, update notification enabled
+                    // state. If the importance was lowered, we disable notifications.
+                    mINotificationManager.setNotificationsEnabledForPackage(
+                            mPackageName, mAppUid, mNewImportance >= mCurrentImportance);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to update notification importance", e);
+            }
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0112661..6364f5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -17,7 +17,6 @@
 package com.android.systemui.statusbar;
 
 import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
-import static com.android.systemui.statusbar.phone.NotificationIconContainer.OVERFLOW_EARLY_AMOUNT;
 
 import android.content.Context;
 import android.content.res.Configuration;
@@ -26,6 +25,7 @@
 import android.os.SystemProperties;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.MathUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -58,6 +58,8 @@
             = SystemProperties.getBoolean("debug.icon_scroll_animations", true);
     private static final int TAG_CONTINUOUS_CLIPPING = R.id.continuous_clipping_tag;
     private static final String TAG = "NotificationShelf";
+    private static final long SHELF_IN_TRANSLATION_DURATION = 220;
+
     private ViewInvertHelper mViewInvertHelper;
     private boolean mDark;
     private NotificationIconContainer mShelfIcons;
@@ -65,6 +67,7 @@
     private int[] mTmp = new int[2];
     private boolean mHideBackground;
     private int mIconAppearTopPadding;
+    private int mShelfAppearTranslation;
     private int mStatusBarHeight;
     private int mStatusBarPaddingStart;
     private AmbientState mAmbientState;
@@ -120,6 +123,7 @@
         mStatusBarHeight = res.getDimensionPixelOffset(R.dimen.status_bar_height);
         mStatusBarPaddingStart = res.getDimensionPixelOffset(R.dimen.status_bar_padding_start);
         mPaddingBetweenElements = res.getDimensionPixelSize(R.dimen.notification_divider_height);
+        mShelfAppearTranslation = res.getDimensionPixelSize(R.dimen.shelf_appear_translation);
 
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
         layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height);
@@ -151,6 +155,18 @@
         updateInteractiveness();
     }
 
+    public void fadeInTranslating() {
+        float translation = mShelfIcons.getTranslationY();
+        mShelfIcons.setTranslationY(translation + mShelfAppearTranslation);
+        mShelfIcons.setAlpha(0);
+        mShelfIcons.animate()
+                .alpha(1)
+                .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+                .translationY(translation)
+                .setDuration(SHELF_IN_TRANSLATION_DURATION)
+                .start();
+    }
+
     @Override
     protected View getContentView() {
         return mShelfIcons;
@@ -175,12 +191,14 @@
             float viewEnd = lastViewState.yTranslation + lastViewState.height;
             mShelfState.copyFrom(lastViewState);
             mShelfState.height = getIntrinsicHeight();
-            mShelfState.yTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
+
+            float awakenTranslation = Math.max(Math.min(viewEnd, maxShelfEnd) - mShelfState.height,
                     getFullyClosedTranslation());
+            float darkTranslation = mAmbientState.getDarkTopPadding();
+            float yRatio = mAmbientState.hasPulsingNotifications() ?
+                    0 : mAmbientState.getDarkAmount();
+            mShelfState.yTranslation = MathUtils.lerp(awakenTranslation, darkTranslation, yRatio);
             mShelfState.zTranslation = ambientState.getBaseZHeight();
-            if (mAmbientState.isDark() && !mAmbientState.hasPulsingNotifications()) {
-                mShelfState.yTranslation = mAmbientState.getDarkTopPadding();
-            }
             float openedAmount = (mShelfState.yTranslation - getFullyClosedTranslation())
                     / (getIntrinsicHeight() * 2);
             openedAmount = Math.min(1.0f, openedAmount);
@@ -555,7 +573,9 @@
             iconState.translateContent = false;
         }
         float transitionAmount;
-        if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
+        if (mAmbientState.getDarkAmount() > 0 && !row.isInShelf()) {
+            transitionAmount = mAmbientState.isFullyDark() ? 1 : 0;
+        } else if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
                 || iconState.useLinearTransitionAmount) {
             transitionAmount = iconTransitionAmount;
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
index 1807465..c99bdef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScrimView.java
@@ -26,6 +26,7 @@
 import android.graphics.Color;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
@@ -272,12 +273,18 @@
                     tintAmount);
             drawable.setColors(mainTinted, secondaryTinted, animated);
         } else {
-            if (mColorFilter == null) {
-                mColorFilter = new PorterDuffColorFilter(mTintColor, PorterDuff.Mode.SRC_OVER);
+            boolean hasAlpha = Color.alpha(mTintColor) != 0;
+            if (hasAlpha) {
+                PorterDuff.Mode targetMode = mColorFilter == null ? Mode.SRC_OVER :
+                    mColorFilter.getMode();
+                if (mColorFilter == null || mColorFilter.getColor() != mTintColor) {
+                    mColorFilter = new PorterDuffColorFilter(mTintColor, targetMode);
+                }
             } else {
-                mColorFilter.setColor(mTintColor);
+                mColorFilter = null;
             }
-            mDrawable.setColorFilter(Color.alpha(mTintColor) == 0 ? null : mColorFilter);
+
+            mDrawable.setColorFilter(mColorFilter);
             mDrawable.invalidateSelf();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java
new file mode 100644
index 0000000..75dd77d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyLogger.java
@@ -0,0 +1,52 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import com.android.internal.statusbar.IStatusBarService;
+
+/**
+ * Handles reporting when smart replies are added to a notification
+ * and clicked upon.
+ */
+public class SmartReplyLogger {
+    protected IStatusBarService mBarService;
+
+    public SmartReplyLogger(Context context) {
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    public void smartReplySent(NotificationData.Entry entry, int replyIndex) {
+        try {
+            mBarService.onNotificationSmartReplySent(entry.notification.getKey(),
+                    replyIndex);
+        } catch (RemoteException e) {
+            // Nothing to do, system going down
+        }
+    }
+
+    public void smartRepliesAdded(final NotificationData.Entry entry, int replyCount) {
+        try {
+            mBarService.onNotificationSmartRepliesAdded(entry.notification.getKey(),
+                    replyCount);
+        } catch (RemoteException e) {
+            // Nothing to do, system going down
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
index 9f79ef2..43e45b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
@@ -16,9 +16,10 @@
 
 package com.android.systemui.statusbar.notification;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Color;
-import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
 import android.widget.ImageView;
@@ -29,8 +30,9 @@
 
     private final int mImageDarkAlpha;
     private final int mImageDarkColor = 0xffffffff;
-    private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter(
-            0, PorterDuff.Mode.SRC_ATOP);
+
+    @Nullable
+    private PorterDuffColorFilter mImageColorFilter = null;
 
     private int mColor = Color.BLACK;
 
@@ -80,7 +82,9 @@
 
     private void updateImageColorFilter(ImageView target, float intensity) {
         int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity);
-        mImageColorFilter.setColor(color);
+        if (mImageColorFilter == null || mImageColorFilter.getColor() != color) {
+            mImageColorFilter = new PorterDuffColorFilter(color, Mode.SRC_ATOP);
+        }
         Drawable imageDrawable = target.getDrawable();
 
         // Also, the notification might have been modified during the animation, so background
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
index 4bca797..5ec822d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BarTransitions.java
@@ -183,20 +183,19 @@
 
         @Override
         public void setTint(int color) {
-            if (mTintFilter == null) {
-                mTintFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN);
-            } else {
-                mTintFilter.setColor(color);
+            PorterDuff.Mode targetMode = mTintFilter == null ? Mode.SRC_IN :
+                mTintFilter.getMode();
+            if (mTintFilter == null || mTintFilter.getColor() != color) {
+                mTintFilter = new PorterDuffColorFilter(color, targetMode);
             }
             invalidateSelf();
         }
 
         @Override
         public void setTintMode(Mode tintMode) {
-            if (mTintFilter == null) {
-                mTintFilter = new PorterDuffColorFilter(0, tintMode);
-            } else {
-                mTintFilter.setMode(tintMode);
+            int targetColor = mTintFilter == null ? 0 : mTintFilter.getColor();
+            if (mTintFilter == null || mTintFilter.getMode() != tintMode) {
+                mTintFilter = new PorterDuffColorFilter(targetColor, tintMode);
             }
             invalidateSelf();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 3d7067d..1fb1ddd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -99,6 +99,11 @@
     private int mBurnInPreventionOffsetY;
 
     /**
+     * Clock vertical padding when pulsing.
+     */
+    private int mPulsingPadding;
+
+    /**
      * Doze/AOD transition amount.
      */
     private float mDarkAmount;
@@ -109,9 +114,9 @@
     private boolean mCurrentlySecure;
 
     /**
-     * If notification panel view currently has a touch.
+     * Dozing and receiving a notification (AOD notification.)
      */
-    private boolean mTracking;
+    private boolean mPulsing;
 
     /**
      * Distance in pixels between the top of the screen and the first view of the bouncer.
@@ -130,11 +135,13 @@
                 R.dimen.burn_in_prevention_offset_x);
         mBurnInPreventionOffsetY = res.getDimensionPixelSize(
                 R.dimen.burn_in_prevention_offset_y);
+        mPulsingPadding = res.getDimensionPixelSize(
+                R.dimen.widget_pulsing_bottom_padding);
     }
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
             float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
-            float dark, boolean secure, boolean tracking, int bouncerTop) {
+            float dark, boolean secure, boolean pulsing, int bouncerTop) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -144,7 +151,7 @@
         mKeyguardStatusHeight = keyguardStatusHeight;
         mDarkAmount = dark;
         mCurrentlySecure = secure;
-        mTracking = tracking;
+        mPulsing = pulsing;
         mBouncerTop = bouncerTop;
     }
 
@@ -152,7 +159,7 @@
         final int y = getClockY();
         result.clockY = y;
         result.clockAlpha = getClockAlpha(y);
-        result.stackScrollerPadding = y + mKeyguardStatusHeight;
+        result.stackScrollerPadding = y + (mPulsing ? 0 : mKeyguardStatusHeight);
         result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount);
     }
 
@@ -194,9 +201,13 @@
 
     private int getClockY() {
         // Dark: Align the bottom edge of the clock at about half of the screen:
-        final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
-        final float clockYRegular = getExpandedClockPosition();
-        final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
+        float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
+        if (mPulsing) {
+            clockYDark -= mPulsingPadding;
+        }
+
+        float clockYRegular = getExpandedClockPosition();
+        boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
         float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
                 mMinTopMargin : -mKeyguardStatusHeight;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 58f8baa6..ca6d596 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -103,6 +103,7 @@
 import java.io.PrintWriter;
 import java.util.List;
 import java.util.Locale;
+import java.util.Optional;
 
 /**
  * Fragment containing the NavigationBarFragment. Contains logic for what happens
@@ -1109,8 +1110,11 @@
         public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) {
             // Only hide the icon if the top task changes its requestedOrientation
             // Launcher can alter its requestedOrientation while it's not on top, don't hide on this
-            final boolean top = ActivityManagerWrapper.getInstance().getRunningTask().id == taskId;
-            if (top) setRotateSuggestionButtonState(false);
+            Optional.ofNullable(ActivityManagerWrapper.getInstance())
+                    .map(ActivityManagerWrapper::getRunningTask)
+                    .ifPresent(a -> {
+                        if (a.id == taskId) setRotateSuggestionButtonState(false);
+                    });
         }
     }
 
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 db2139d..14a36d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -812,9 +812,6 @@
         if (mGestureHelper != null) {
             mGestureHelper.onDarkIntensityChange(intensity);
         }
-        if (mRecentsOnboarding != null) {
-            mRecentsOnboarding.setContentDarkIntensity(intensity);
-        }
     }
 
     @Override
@@ -831,6 +828,7 @@
         updateButtonLocationOnScreen(getHomeButton(), mHomeButtonBounds);
         updateButtonLocationOnScreen(getRecentsButton(), mRecentsButtonBounds);
         mGestureHelper.onLayout(changed, left, top, right, bottom);
+        mRecentsOnboarding.setNavBarHeight(getMeasuredHeight());
     }
 
     private void updateButtonLocationOnScreen(ButtonDispatcher button, Rect buttonBounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index b7af84a..351633b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -238,6 +238,7 @@
     private boolean mIsFullWidth;
     private float mDarkAmount;
     private float mDarkAmountTarget;
+    private boolean mPulsing;
     private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
     private boolean mNoVisibleNotifications = true;
     private ValueAnimator mDarkAnimator;
@@ -477,7 +478,7 @@
                     mKeyguardStatusView.getHeight(),
                     mDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
-                    mTracking,
+                    mPulsing,
                     mBouncerTop);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
@@ -2689,14 +2690,8 @@
         positionClockAndNotifications();
     }
 
-    public void setNoVisibleNotifications(boolean noNotifications) {
-        mNoVisibleNotifications = noNotifications;
-        if (mQs != null) {
-            mQs.setHasNotifications(!noNotifications);
-        }
-    }
-
     public void setPulsing(boolean pulsing) {
+        mPulsing = pulsing;
         mKeyguardStatusView.setPulsing(pulsing);
         positionClockAndNotifications();
         mNotificationStackScroller.setPulsing(pulsing, mKeyguardStatusView.getLocationOnScreen()[1]
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 2c335a3..cc143bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -111,7 +111,6 @@
     private final Context mContext;
     protected final ScrimView mScrimBehind;
     protected final ScrimView mScrimInFront;
-    private final LightBarController mLightBarController;
     private final UnlockMethodCache mUnlockMethodCache;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final DozeParameters mDozeParameters;
@@ -145,6 +144,8 @@
     private int mCurrentBehindTint;
     private boolean mWallpaperVisibilityTimedOut;
     private int mScrimsVisibility;
+    private final Consumer<GradientColors> mScrimInFrontColorListener;
+    private final Consumer<Float> mScrimBehindAlphaListener;
     private final Consumer<Integer> mScrimVisibleListener;
     private boolean mBlankScreen;
     private boolean mScreenBlankingCallbackCalled;
@@ -161,17 +162,20 @@
     private boolean mWakeLockHeld;
     private boolean mKeyguardOccluded;
 
-    public ScrimController(LightBarController lightBarController, ScrimView scrimBehind,
-            ScrimView scrimInFront, Consumer<Integer> scrimVisibleListener,
-            DozeParameters dozeParameters, AlarmManager alarmManager) {
+    public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
+            Consumer<Float> scrimBehindAlphaListener,
+            Consumer<GradientColors> scrimInFrontColorListener,
+            Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
+            AlarmManager alarmManager) {
         mScrimBehind = scrimBehind;
         mScrimInFront = scrimInFront;
+        mScrimBehindAlphaListener = scrimBehindAlphaListener;
+        mScrimInFrontColorListener = scrimInFrontColorListener;
         mScrimVisibleListener = scrimVisibleListener;
         mContext = scrimBehind.getContext();
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         mDarkenWhileDragging = !mUnlockMethodCache.canSkipBouncer();
         mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-        mLightBarController = lightBarController;
         mScrimBehindAlphaResValue = mContext.getResources().getFloat(R.dimen.scrim_behind_alpha);
         mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout,
                 "hide_aod_wallpaper", new Handler());
@@ -370,6 +374,8 @@
 
             setOrAdaptCurrentAnimation(mScrimBehind);
             setOrAdaptCurrentAnimation(mScrimInFront);
+
+            mScrimBehindAlphaListener.accept(mScrimBehind.getViewAlpha());
         }
     }
 
@@ -478,7 +484,7 @@
             float minOpacity = ColorUtils.calculateMinimumBackgroundAlpha(textColor, mainColor,
                     4.5f /* minimumContrast */) / 255f;
             mScrimBehindAlpha = Math.max(mScrimBehindAlphaResValue, minOpacity);
-            mLightBarController.setScrimColor(mScrimInFront.getColors());
+            mScrimInFrontColorListener.accept(mScrimInFront.getColors());
         }
 
         // We want to override the back scrim opacity for the AOD state
@@ -705,9 +711,8 @@
             }
         }
 
-        // TODO factor mLightBarController out of this class
         if (scrim == mScrimBehind) {
-            mLightBarController.setScrimAlpha(alpha);
+            mScrimBehindAlphaListener.accept(alpha);
         }
 
         final boolean wantsAlphaUpdate = alpha != currentAlpha;
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 aa9bbe08..4b2bc45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -916,8 +916,14 @@
 
         ScrimView scrimBehind = mStatusBarWindow.findViewById(R.id.scrim_behind);
         ScrimView scrimInFront = mStatusBarWindow.findViewById(R.id.scrim_in_front);
-        mScrimController = SystemUIFactory.getInstance().createScrimController(mLightBarController,
+        mScrimController = SystemUIFactory.getInstance().createScrimController(
                 scrimBehind, scrimInFront, mLockscreenWallpaper,
+                scrimBehindAlpha -> {
+                    mLightBarController.setScrimAlpha(scrimBehindAlpha);
+                },
+                scrimInFrontColor -> {
+                    mLightBarController.setScrimColor(scrimInFrontColor);
+                },
                 scrimsVisible -> {
                     if (mStatusBarWindowManager != null) {
                         mStatusBarWindowManager.setScrimsVisibility(scrimsVisible);
@@ -1428,13 +1434,13 @@
     }
 
     public void addQsTile(ComponentName tile) {
-        if (mQSPanel.getHost() != null) {
+        if (mQSPanel != null && mQSPanel.getHost() != null) {
             mQSPanel.getHost().addTile(tile);
         }
     }
 
     public void remQsTile(ComponentName tile) {
-        if (mQSPanel.getHost() != null) {
+        if (mQSPanel != null && mQSPanel.getHost() != null) {
             mQSPanel.getHost().removeTile(tile);
         }
     }
@@ -1446,7 +1452,8 @@
     @VisibleForTesting
     protected void updateFooter() {
         boolean showFooterView = mState != StatusBarState.KEYGUARD
-                && mEntryManager.getNotificationData().getActiveNotifications().size() != 0;
+                && mEntryManager.getNotificationData().getActiveNotifications().size() != 0
+                && !mRemoteInputManager.getController().isRemoteInputActive();
         boolean showDismissView = mClearAllEnabled && mState != StatusBarState.KEYGUARD
                 && hasActiveClearableNotifications();
 
@@ -2826,6 +2833,7 @@
                             boolean remoteInputActive) {
                         mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                         entry.row.notifyHeightChanged(true /* needsAnimation */);
+                        updateFooter();
                     }
                     public void lockScrollTo(NotificationData.Entry entry) {
                         mStackScroller.lockScrollTo(entry.row);
@@ -3934,7 +3942,8 @@
     }
 
     private void showBouncerIfKeyguard() {
-        if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+        if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+                && !mKeyguardViewMediator.isHiding()) {
             showBouncer(true /* animated */);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 94db95a..cd17cfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -280,7 +280,7 @@
     public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
 
     @Override
-    public void onProfileAudioStateChanged(int bluetoothProfile, int state) {}
+    public void onAudioModeChanged() {}
 
     private ActuallyCachedState getCachedState(CachedBluetoothDevice device) {
         ActuallyCachedState state = mCachedState.get(device);
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 baeaaad..9aa8044 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -16,9 +16,6 @@
 
 package com.android.systemui.statusbar.policy;
 
-import libcore.icu.LocaleData;
-
-import android.app.ActivityManager;
 import android.app.StatusBarManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -46,6 +43,7 @@
 import com.android.systemui.FontSizeUtils;
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
+import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -53,6 +51,8 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 
+import libcore.icu.LocaleData;
+
 import java.text.SimpleDateFormat;
 import java.util.Calendar;
 import java.util.Locale;
@@ -66,6 +66,9 @@
 
     public static final String CLOCK_SECONDS = "clock_seconds";
 
+    private final CurrentUserTracker mCurrentUserTracker;
+    private int mCurrentUserId;
+
     private boolean mClockVisibleByPolicy = true;
     private boolean mClockVisibleByUser = true;
 
@@ -117,6 +120,12 @@
         } finally {
             a.recycle();
         }
+        mCurrentUserTracker = new CurrentUserTracker(context) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                mCurrentUserId = newUserId;
+            }
+        };
     }
 
     @Override
@@ -141,6 +150,8 @@
             if (mShowDark) {
                 Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
             }
+            mCurrentUserTracker.startTracking();
+            mCurrentUserId = mCurrentUserTracker.getCurrentUserId();
         }
 
         // NOTE: It's safe to do these after registering the receiver since the receiver always runs
@@ -166,6 +177,7 @@
             if (mShowDark) {
                 Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(this);
             }
+            mCurrentUserTracker.stopTracking();
         }
     }
 
@@ -302,7 +314,7 @@
 
     private final CharSequence getSmallTime() {
         Context context = getContext();
-        boolean is24 = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser());
+        boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
         LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
 
         final char MAGIC1 = '\uEF00';
@@ -392,8 +404,7 @@
             } else if (hhmm != null && hhmm.length() == 4) {
                 int hh = Integer.parseInt(hhmm.substring(0, 2));
                 int mm = Integer.parseInt(hhmm.substring(2));
-                boolean is24 = DateFormat.is24HourFormat(
-                        getContext(), ActivityManager.getCurrentUser());
+                boolean is24 = DateFormat.is24HourFormat(getContext(), mCurrentUserId);
                 if (is24) {
                     mCalendar.set(Calendar.HOUR_OF_DAY, hh);
                 } else {
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 74b3926..4c79ee32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java
@@ -23,6 +23,8 @@
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.SmartReplyLogger;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.text.BreakIterator;
@@ -109,14 +111,16 @@
                 Math.max(getChildCount(), 1), DECREASING_MEASURED_WIDTH_WITHOUT_PADDING_COMPARATOR);
     }
 
-    public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent) {
+    public void setRepliesFromRemoteInput(RemoteInput remoteInput, PendingIntent pendingIntent,
+            SmartReplyLogger smartReplyLogger, NotificationData.Entry entry) {
         removeAllViews();
         if (remoteInput != null && pendingIntent != null) {
             CharSequence[] choices = remoteInput.getChoices();
             if (choices != null) {
-                for (CharSequence choice : choices) {
+                for (int i = 0; i < choices.length; ++i) {
                     Button replyButton = inflateReplyButton(
-                            getContext(), this, choice, remoteInput, pendingIntent);
+                            getContext(), this, i, choices[i], remoteInput, pendingIntent,
+                            smartReplyLogger, entry);
                     addView(replyButton);
                 }
             }
@@ -130,8 +134,9 @@
     }
 
     @VisibleForTesting
-    Button inflateReplyButton(Context context, ViewGroup root, CharSequence choice,
-            RemoteInput remoteInput, PendingIntent pendingIntent) {
+    Button inflateReplyButton(Context context, ViewGroup root, int replyIndex,
+            CharSequence choice, RemoteInput remoteInput, PendingIntent pendingIntent,
+            SmartReplyLogger smartReplyLogger, NotificationData.Entry entry) {
         Button b = (Button) LayoutInflater.from(context).inflate(
                 R.layout.smart_reply_button, root, false);
         b.setText(choice);
@@ -147,6 +152,7 @@
             } catch (PendingIntent.CanceledException e) {
                 Log.w(TAG, "Unable to send smart reply", e);
             }
+            smartReplyLogger.smartReplySent(entry, replyIndex);
             return false; // do not defer
         };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index 7e6fe02..bd76820 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -208,7 +208,7 @@
             0,
             0,
             AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
-            R.string.cell_data_off,
+            R.string.cell_data_off_content_description,
             0,
             false);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 7c1c566..91a4b07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -70,8 +70,8 @@
     private int mIntrinsicPadding;
     private int mExpandAnimationTopChange;
     private ExpandableNotificationRow mExpandingNotification;
-    private boolean mFullyDark;
     private int mDarkTopPadding;
+    private float mDarkAmount;
 
     public AmbientState(Context context) {
         reload(context);
@@ -149,6 +149,16 @@
         mDark = dark;
     }
 
+    /** Dark ratio of the status bar **/
+    public void setDarkAmount(float darkAmount) {
+        mDarkAmount = darkAmount;
+    }
+
+    /** Returns the dark ratio of the status bar */
+    public float getDarkAmount() {
+        return mDarkAmount;
+    }
+
     public void setHideSensitive(boolean hideSensitive) {
         mHideSensitive = hideSensitive;
     }
@@ -413,17 +423,10 @@
     }
 
     /**
-     * {@see isFullyDark}
-     */
-    public void setFullyDark(boolean fullyDark) {
-        mFullyDark = fullyDark;
-    }
-
-    /**
      * @return {@code true } when shade is completely dark: in AOD or ambient display.
      */
     public boolean isFullyDark() {
-        return mFullyDark;
+        return mDarkAmount == 1;
     }
 
     public void setDarkTopPadding(int darkTopPadding) {
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 d282f25..bc5a848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -96,6 +96,7 @@
 import com.android.systemui.statusbar.notification.FakeShadowView;
 import com.android.systemui.statusbar.notification.NotificationUtils;
 import com.android.systemui.statusbar.notification.VisibilityLocationProvider;
+import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
@@ -269,8 +270,6 @@
      */
     private boolean mOnlyScrollingInThisMotion;
     private boolean mDisallowDismissInThisMotion;
-    private boolean mInterceptDelegateEnabled;
-    private boolean mDelegateToScrollView;
     private boolean mDisallowScrollingInThisMotion;
     private long mGoToFullShadeDelay;
     private ViewTreeObserver.OnPreDrawListener mChildrenUpdater
@@ -562,17 +561,17 @@
             return;
         }
 
-        final int color;
-        if (mAmbientState.isDark()) {
-            color = Color.WHITE;
-        } else {
-            float alpha =
-                    BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
-            alpha *= 1f - mDarkAmount;
-            // We need to manually blend in the background color
-            int scrimColor = mScrimController.getBackgroundColor();
-            color = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
-        }
+        float alpha =
+                BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+        alpha *= 1f - mDarkAmount;
+        // We need to manually blend in the background color.
+        int scrimColor = mScrimController.getBackgroundColor();
+        int awakeColor = ColorUtils.blendARGB(scrimColor, mBgColor, alpha);
+
+        // Interpolate between semi-transparent notification panel background color
+        // and white AOD separator.
+        float colorInterpolation = Interpolators.DECELERATE_QUINT.getInterpolation(mDarkAmount);
+        int color = ColorUtils.blendARGB(awakeColor, Color.WHITE, colorInterpolation);
 
         if (mCachedBackgroundColor != color) {
             mCachedBackgroundColor = color;
@@ -3961,13 +3960,18 @@
 
     private void setDarkAmount(float darkAmount) {
         mDarkAmount = darkAmount;
-        final boolean fullyDark = darkAmount == 1;
-        if (mAmbientState.isFullyDark() != fullyDark) {
-            mAmbientState.setFullyDark(fullyDark);
+        boolean wasFullyDark = mAmbientState.isFullyDark();
+        mAmbientState.setDarkAmount(darkAmount);
+        if (mAmbientState.isFullyDark() != wasFullyDark) {
             updateContentHeight();
+            DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
+            if (mAmbientState.isFullyDark() && dozeParameters.shouldControlScreenOff()) {
+                mShelf.fadeInTranslating();
+            }
         }
         updateBackgroundDimming();
         updateAntiBurnInTranslation();
+        requestChildrenUpdate();
     }
 
     public float getDarkAmount() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index a8d2d98..f4d7f8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -276,8 +276,6 @@
                 if (i >= firstHiddenIndex) {
                     // we need normal padding now, to be in sync with what the stack calculates
                     lastView = null;
-                    ExpandableViewState viewState = resultState.getViewStateForView(v);
-                    viewState.hidden = true;
                 }
                 notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
                 float increasedPadding = v.getIncreasedPaddingAmount();
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 5a4478f..639e49b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -58,7 +58,7 @@
 
     private static final String TUNER_VERSION = "sysui_tuner_version";
 
-    private static final int CURRENT_TUNER_VERSION = 2;
+    private static final int CURRENT_TUNER_VERSION = 3;
 
     private final Observer mObserver = new Observer();
     // Map of Uris we listen on to their settings keys.
@@ -119,6 +119,10 @@
         if (oldVersion < 2) {
             setTunerEnabled(mContext, false);
         }
+        if (oldVersion < 3) {
+            // Delay this so that we can wait for everything to be registered first.
+            new Handler(Dependency.get(Dependency.BG_LOOPER)).postDelayed(() -> clearAll(), 5000);
+        }
         setValue(TUNER_VERSION, newVersion);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 2c85bb6..ca55e1f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -52,26 +52,28 @@
     public static final int EVENT_MUTE_CHANGED = 15;  // (stream|int) (muted|bool)
     public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|bool)
     public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
+    public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
 
     private static final String[] EVENT_TAGS = {
-        "show_dialog",
-        "dismiss_dialog",
-        "active_stream_changed",
-        "expand",
-        "key",
-        "collection_started",
-        "collection_stopped",
-        "icon_click",
-        "settings_click",
-        "touch_level_changed",
-        "level_changed",
-        "internal_ringer_mode_changed",
-        "external_ringer_mode_changed",
-        "zen_mode_changed",
-        "suppressor_changed",
-        "mute_changed",
-        "touch_level_done",
-        "zen_mode_config_changed",
+            "show_dialog",
+            "dismiss_dialog",
+            "active_stream_changed",
+            "expand",
+            "key",
+            "collection_started",
+            "collection_stopped",
+            "icon_click",
+            "settings_click",
+            "touch_level_changed",
+            "level_changed",
+            "internal_ringer_mode_changed",
+            "external_ringer_mode_changed",
+            "zen_mode_changed",
+            "suppressor_changed",
+            "mute_changed",
+            "touch_level_done",
+            "zen_mode_config_changed",
+            "ringer_toggle"
     };
 
     public static final int DISMISS_REASON_UNKNOWN = 0;
@@ -112,6 +114,7 @@
     public static Callback sCallback;
 
     public static void writeEvent(Context context, int tag, Object... list) {
+        MetricsLogger logger = new MetricsLogger();
         final long time = System.currentTimeMillis();
         final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
         if (list != null && list.length > 0) {
@@ -139,7 +142,7 @@
                     break;
                 case EVENT_ICON_CLICK:
                     MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_ICON,
-                            (Integer) list[1]);
+                            (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(iconStateToString((Integer) list[1]));
                     break;
@@ -155,10 +158,16 @@
                     break;
                 case EVENT_KEY:
                     MetricsLogger.action(context, MetricsEvent.ACTION_VOLUME_KEY,
-                            (Integer) list[1]);
+                            (Integer) list[0]);
                     sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
                             .append(list[1]);
                     break;
+                case EVENT_RINGER_TOGGLE:
+                    logger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, (Integer) list[0]);
+                    break;
+                case EVENT_SETTINGS_CLICK:
+                    logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
+                    break;
                 case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
                     MetricsLogger.action(context, MetricsEvent.ACTION_RINGER_MODE,
                             (Integer) list[0]);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index a2d2615..00874e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -301,15 +301,8 @@
         if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
         VolumeRow row = new VolumeRow();
         initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
-        if (dynamic && mRows.size() > 2) {
-            // Dynamic Streams should be the first in the list, so they're shown to start of
-            // everything except a11y
-            mDialogRowsView.addView(row.view, 1);
-            mRows.add(1, row);
-        } else {
-            mDialogRowsView.addView(row.view);
-            mRows.add(row);
-        }
+        mDialogRowsView.addView(row.view);
+        mRows.add(row);
     }
 
     private void addExistingRows() {
@@ -422,6 +415,7 @@
         mSettingsView.setVisibility(
                 mDeviceProvisionedController.isDeviceProvisioned() ? VISIBLE : GONE);
         mSettingsIcon.setOnClickListener(v -> {
+            Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK);
             Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             dismissH(DISMISS_REASON_SETTINGS_CLICKED);
@@ -431,8 +425,6 @@
 
     public void initRingerH() {
         mRingerIcon.setOnClickListener(v -> {
-            Events.writeEvent(mContext, Events.EVENT_ICON_CLICK, AudioManager.STREAM_RING,
-                    mRingerIcon.getTag());
             Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true);
             final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
             if (ss == null) {
@@ -456,6 +448,7 @@
                     mController.setStreamVolume(AudioManager.STREAM_RING, 1);
                 }
             }
+            Events.writeEvent(mContext, Events.EVENT_RINGER_TOGGLE, newRingerMode);
             updateRingerH();
             provideTouchFeedbackH(newRingerMode);
             mController.setRingerMode(newRingerMode, false);
@@ -603,7 +596,8 @@
             return activeRow.stream == STREAM_RING
                     || activeRow.stream == STREAM_ALARM
                     || activeRow.stream == STREAM_VOICE_CALL
-                    || activeRow.stream == STREAM_ACCESSIBILITY;
+                    || activeRow.stream == STREAM_ACCESSIBILITY
+                    || mDynamic.get(activeRow.stream);
         }
 
         return false;
diff --git a/packages/SystemUI/tests/AndroidTest.xml b/packages/SystemUI/tests/AndroidTest.xml
index 53839a9b..fc353a1 100644
--- a/packages/SystemUI/tests/AndroidTest.xml
+++ b/packages/SystemUI/tests/AndroidTest.xml
@@ -24,5 +24,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.systemui.tests" />
         <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
index d86e947..3cbe274 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -52,17 +52,20 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.PollingCheck;
+import android.testing.TestableLooper;
 import android.testing.UiThreadTest;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 
@@ -81,7 +84,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@UiThreadTest
+@TestableLooper.RunWithLooper
 public class NotificationInfoTest extends SysuiTestCase {
     private static final String TEST_PACKAGE_NAME = "test_package";
     private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
@@ -90,12 +93,14 @@
     private static final String TEST_CHANNEL = "test_channel";
     private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
 
+    private TestableLooper mTestableLooper;
     private NotificationInfo mNotificationInfo;
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
     private StatusBarNotification mSbn;
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    private Looper mLooper;
     @Mock private INotificationManager mMockINotificationManager;
     @Mock private PackageManager mMockPackageManager;
     @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
@@ -105,7 +110,8 @@
         mDependency.injectTestDependency(
                 NotificationBlockingHelperManager.class,
                 mBlockingHelperManager);
-
+        mTestableLooper = TestableLooper.get(this);
+        mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper());
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -355,6 +361,7 @@
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -366,6 +373,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -378,6 +386,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -390,6 +399,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.handleCloseControls(true, false);
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
         assertEquals(originalImportance, mNotificationChannel.getImportance());
@@ -403,6 +413,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
         assertEquals(IMPORTANCE_UNSPECIFIED, mNotificationChannel.getImportance());
@@ -436,6 +448,8 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -450,6 +464,7 @@
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -460,12 +475,46 @@
     }
 
     @Test
+    public void testBlockChangedCallsUpdateNotificationChannel_blockingHelper() throws Exception {
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                1 /* numChannels */,
+                mSbn,
+                null /* checkSaveListener */,
+                null /* onSettingsClick */,
+                null /* onAppSettingsClick */,
+                false /* isNonblockable */,
+                true /* isForBlockingHelper */,
+                true /* isUserSentimentNegative */);
+
+        mNotificationInfo.findViewById(R.id.block).performClick();
+        waitForUndoButton();
+        mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
+        ArgumentCaptor<NotificationChannel> updated =
+                ArgumentCaptor.forClass(NotificationChannel.class);
+        verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
+                anyString(), eq(TEST_UID), updated.capture());
+        assertTrue((updated.getValue().getUserLockedFields()
+                & USER_LOCKED_IMPORTANCE) != 0);
+        assertEquals(IMPORTANCE_NONE, updated.getValue().getImportance());
+    }
+
+
+    @Test
     public void testNonBlockableAppDoesNotBecomeMin() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -481,6 +530,7 @@
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -498,6 +548,7 @@
 
         mNotificationInfo.handleCloseControls(true, false);
 
+        mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -518,6 +569,7 @@
         waitForStopButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -538,6 +590,7 @@
         waitForStopButton();
         mNotificationInfo.handleCloseControls(true, false);
 
+        mTestableLooper.processAllMessages();
         ArgumentCaptor<NotificationChannel> updated =
                 ArgumentCaptor.forClass(NotificationChannel.class);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -555,6 +608,8 @@
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(false, false);
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
     }
@@ -568,6 +623,8 @@
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(false, false);
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
     }
@@ -583,6 +640,8 @@
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
     }
@@ -599,6 +658,8 @@
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
         mNotificationInfo.handleCloseControls(true, false);
+
+        mTestableLooper.processAllMessages();
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
                 eq(TEST_PACKAGE_NAME), eq(TEST_UID), eq(mNotificationChannel));
     }
@@ -696,6 +757,29 @@
         assertEquals(GONE, settingsLink.getVisibility());
     }
 
+    @Test
+    public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception {
+        final String settingsText = "work chats";
+        final ResolveInfo ri = new ResolveInfo();
+        ri.activityInfo = new ActivityInfo();
+        ri.activityInfo.packageName = TEST_PACKAGE_NAME;
+        ri.activityInfo.name = "something";
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris);
+        mNotificationChannel.setImportance(IMPORTANCE_LOW);
+        Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
+                .setSettingsText(settingsText).build();
+        StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME,
+                0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
+
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false, true,
+                true);
+        final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
+        assertEquals(GONE, settingsLink.getVisibility());
+    }
+
 
     @Test
     public void testWillBeRemovedReturnsFalseBeforeBind() throws Exception {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 82129a1..0416232 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -42,6 +42,7 @@
 import android.testing.TestableLooper;
 import android.view.View;
 
+import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.ScrimView;
@@ -65,9 +66,12 @@
     private SynchronousScrimController mScrimController;
     private ScrimView mScrimBehind;
     private ScrimView mScrimInFront;
+    private Consumer<Float> mScrimBehindAlphaCallback;
+    private Consumer<GradientColors> mScrimInFrontColorCallback;
     private Consumer<Integer> mScrimVisibilityCallback;
+    private float mScrimBehindAlpha;
+    private GradientColors mScrimInFrontColor;
     private int mScrimVisibility;
-    private LightBarController mLightBarController;
     private DozeParameters mDozeParamenters;
     private WakeLock mWakeLock;
     private boolean mAlwaysOnEnabled;
@@ -75,19 +79,20 @@
 
     @Before
     public void setup() {
-        mLightBarController = mock(LightBarController.class);
         mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
         mWakeLock = mock(WakeLock.class);
         mAlarmManager = mock(AlarmManager.class);
         mAlwaysOnEnabled = true;
+        mScrimBehindAlphaCallback = (Float alpha) -> mScrimBehindAlpha = alpha;
+        mScrimInFrontColorCallback = (GradientColors color) -> mScrimInFrontColor = color;
         mScrimVisibilityCallback = (Integer visible) -> mScrimVisibility = visible;
         mDozeParamenters = mock(DozeParameters.class);
         when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
         when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
-        mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
-                mScrimInFront, mScrimVisibilityCallback, mDozeParamenters,
-                mAlarmManager);
+        mScrimController = new SynchronousScrimController(mScrimBehind, mScrimInFront,
+                mScrimBehindAlphaCallback, mScrimInFrontColorCallback, mScrimVisibilityCallback,
+                mDozeParamenters, mAlarmManager);
     }
 
     @Test
@@ -206,6 +211,28 @@
     }
 
     @Test
+    public void panelExpansion() {
+        mScrimController.setPanelExpansion(0f);
+        mScrimController.setPanelExpansion(0.5f);
+        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.finishAnimationsImmediately();
+
+        reset(mScrimBehind);
+        mScrimController.setPanelExpansion(0f);
+        mScrimController.setPanelExpansion(1.0f);
+        mScrimController.onPreDraw();
+
+        Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
+                mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+
+        mScrimController.setPanelExpansion(0f);
+        mScrimController.onPreDraw();
+
+        Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
+                mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
+    }
+
+    @Test
     public void panelExpansionAffectsAlpha() {
         mScrimController.setPanelExpansion(0f);
         mScrimController.setPanelExpansion(0.5f);
@@ -531,11 +558,12 @@
         private boolean mAnimationCancelled;
         boolean mOnPreDrawCalled;
 
-        SynchronousScrimController(LightBarController lightBarController,
-                ScrimView scrimBehind, ScrimView scrimInFront,
+        SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
+                Consumer<Float> scrimBehindAlphaListener,
+                Consumer<GradientColors> scrimInFrontColorListener,
                 Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
                 AlarmManager alarmManager) {
-            super(lightBarController, scrimBehind, scrimInFront,
+            super(scrimBehind, scrimInFront, scrimBehindAlphaListener, scrimInFrontColorListener,
                     scrimVisibleListener, dozeParameters, alarmManager);
             mHandler = new FakeHandler(Looper.myLooper());
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e68c7c8..d3cb5a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -95,6 +95,7 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -139,6 +140,8 @@
     @Mock private NotificationListener mNotificationListener;
     @Mock private KeyguardViewMediator mKeyguardViewMediator;
     @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock private NotificationRemoteInputManager mRemoteInputManager;
+    @Mock private RemoteInputController mRemoteInputController;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
@@ -200,13 +203,14 @@
 
         mEntryManager = new TestableNotificationEntryManager(mSystemServicesProxy, mPowerManager,
                 mContext);
+        when(mRemoteInputManager.getController()).thenReturn(mRemoteInputController);
         mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache,
                 mKeyguardIndicationController, mStackScroller, mHeadsUpManager,
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
                 mEntryManager, mScrimController, mFingerprintUnlockController,
                 mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
-                mock(NotificationRemoteInputManager.class), mock(NotificationGroupManager.class),
+                mRemoteInputManager, mock(NotificationGroupManager.class),
                 mock(FalsingManager.class), mock(StatusBarWindowManager.class),
                 mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
                 mock(NotificationShelf.class), mLockscreenUserManager,
@@ -588,6 +592,23 @@
     }
 
     @Test
+    public void testUpdateFooter_remoteInput() {
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+        ArrayList<Entry> entries = new ArrayList<>();
+        entries.add(mock(Entry.class));
+        when(mNotificationData.getActiveNotifications()).thenReturn(entries);
+
+        ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+        when(row.canViewBeDismissed()).thenReturn(true);
+        when(mStackScroller.getChildCount()).thenReturn(1);
+        when(mStackScroller.getChildAt(anyInt())).thenReturn(row);
+        when(mRemoteInputController.isRemoteInputActive()).thenReturn(true);
+
+        mStatusBar.updateFooter();
+        verify(mStackScroller).updateFooterView(false, true);
+    }
+
+    @Test
     public void testUpdateFooter_oneClearableNotification() {
         mStatusBar.setBarStateForTest(StatusBarState.SHADE);
         ArrayList<Entry> entries = new ArrayList<>();
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 56882c6..2bb8106 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
@@ -22,11 +22,16 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.verify;
+
 import android.app.PendingIntent;
 import android.app.RemoteInput;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Resources;
+import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -38,6 +43,8 @@
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationData;
+import com.android.systemui.statusbar.SmartReplyLogger;
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 
 import java.util.concurrent.atomic.AtomicReference;
@@ -46,6 +53,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
@@ -66,8 +75,12 @@
     private int mDoubleLinePaddingHorizontal;
     private int mSpacing;
 
+    @Mock private SmartReplyLogger mLogger;
+    private NotificationData.Entry mEntry;
+
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
         mReceiver = new BlockingQueueIntentReceiver();
         mContext.registerReceiver(mReceiver, new IntentFilter(TEST_ACTION));
         mDependency.get(KeyguardDismissUtil.class).setDismissHandler(
@@ -82,6 +95,10 @@
         mDoubleLinePaddingHorizontal = res.getDimensionPixelSize(
                 R.dimen.smart_reply_button_padding_horizontal_double_line);
         mSpacing = res.getDimensionPixelSize(R.dimen.smart_reply_button_spacing);
+
+        StatusBarNotification notification = mock(StatusBarNotification.class);
+        when(notification.getKey()).thenReturn("akey");
+        mEntry = new NotificationData.Entry(notification);
     }
 
     @After
@@ -138,6 +155,13 @@
     }
 
     @Test
+    public void testSendSmartReply_LoggerCall() {
+        setRepliesFromRemoteInput(TEST_CHOICES);
+        mView.getChildAt(2).performClick();
+        verify(mLogger).smartReplySent(mEntry, 2);
+    }
+
+    @Test
     public void testMeasure_empty() {
         mView.measure(WIDTH_SPEC, HEIGHT_SPEC);
         assertEquals(500, mView.getMeasuredWidthAndState());
@@ -316,7 +340,7 @@
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
                 new Intent(TEST_ACTION), 0);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
-        mView.setRepliesFromRemoteInput(input, pendingIntent);
+        mView.setRepliesFromRemoteInput(input, pendingIntent, mLogger, mEntry);
     }
 
     /** Builds a {@link ViewGroup} whose measures and layout mirror a {@link SmartReplyView}. */
@@ -343,8 +367,9 @@
         }
 
         Button previous = null;
-        for (CharSequence choice : choices) {
-            Button current = mView.inflateReplyButton(mContext, mView, choice, null, null);
+        for (int i = 0; i < choices.length; ++i) {
+            Button current = mView.inflateReplyButton(mContext, mView, i, choices[i],
+                    null, null, null, null);
             current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal,
                     current.getPaddingBottom());
             if (previous != null) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
index cd3031b..dd2b581 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayoutTest.java
@@ -97,6 +97,7 @@
         doNothing().when(mGroupManager).collapseAllGroups();
         doNothing().when(mExpandHelper).cancelImmediately();
         doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
+        doNothing().when(notificationShelf).fadeInTranslating();
     }
 
     @Test
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 739b716..1631316 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4779,7 +4779,7 @@
     // CATEGORY: SETTINGS
     ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1210;
 
-    // FIELD - Manually selected mobile network
+    // Not used anymore.
     FIELD_MOBILE_NETWORK = 1211;
 
     // OPEN: Settings > Network & Internet > Mobile network > Access Point Names
@@ -5583,6 +5583,68 @@
     // OS: P
     SETTINGS_GESTURE_SWIPE_UP = 1374;
 
+    // OPEN: Settings > Storage > Dialog to format a storage volume
+    // CATEGORY: SETTINGS
+    // OS: P
+    DIALOG_VOLUME_FORMAT = 1375;
+
+    // OPEN: DND onboarding activity > screen on checkbox
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_SCREEN_ON = 1376;
+
+    // OPEN: DND onboarding activity > screen off checkbox
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_SCREEN_OFF = 1377;
+
+    // OPEN: DND onboarding activity > Ok button
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_OK = 1378;
+
+    // OPEN: DND onboarding activity > Settings link
+    // CATEGORY: SETTINGS
+    // OS: P
+    ACTION_ZEN_ONBOARDING_SETTINGS = 1379;
+
+    // OPEN: DND onboarding activity
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_ZEN_ONBOARDING = 1380;
+
+    // OPEN: Settings > Display > Auto brightness
+    // CATEGORY: SETTINGS
+    // OS: P
+    SETTINGS_AUTO_BRIGHTNESS = 1381;
+
+    // OPEN: Smart replies in a notification seen at least once
+    // CATEGORY: NOTIFICATION
+    //   PACKAGE: App that posted the notification
+    //   SUBTYPE: Number of smart replies.
+    // OS: P
+    SMART_REPLY_VISIBLE = 1382;
+
+    // ACTION: Smart reply in a notification clicked.
+    // CATEGORY: NOTIFICATION
+    //   PACKAGE: App that posted the notification
+    //   SUBTYPE: Index of smart reply clicked.
+    // OS: P
+    SMART_REPLY_ACTION = 1383;
+
+    // Tagged data for SMART_REPLY_VISIBLE. Count of number of smart replies.
+    // OS: P
+    NOTIFICATION_SMART_REPLY_COUNT = 1384;
+
+    // Volume dialog > ringer toggle
+    // OS: P
+    ACTION_VOLUME_RINGER_TOGGLE = 1385;
+
+    // Volume dialog > settings button
+    // OS: P
+    ACTION_VOLUME_SETTINGS = 1386;
+
+
     // ---- End P Constants, all P constants go above this line ----
 
     // First Q constant in master goes here:
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
index a2de8e7..fbec5cb 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbRestoreEngine.java
@@ -64,8 +64,7 @@
         try {
             File restoreData = prepareRestoreData(mInfo, mInFD);
 
-            // TODO: version ?
-            invokeAgentForAdbRestore(mAgent, mInfo, restoreData, 0);
+            invokeAgentForAdbRestore(mAgent, mInfo, restoreData);
         } catch (IOException e) {
             e.printStackTrace();
         }
@@ -83,8 +82,8 @@
         return sortedDataName;
     }
 
-    private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData,
-            int versionCode) throws IOException {
+    private void invokeAgentForAdbRestore(IBackupAgent agent, FileMetadata info, File restoreData)
+            throws IOException {
         String pkg = info.packageName;
         File newStateName = new File(mDataDir, pkg + ".new");
         try {
@@ -95,9 +94,9 @@
 
             if (DEBUG) {
                 Slog.i(TAG, "Starting restore of package " + pkg + " for version code "
-                        + versionCode);
+                        + info.version);
             }
-            agent.doRestore(backupData, versionCode, newState, mToken,
+            agent.doRestore(backupData, info.version, newState, mToken,
                     mBackupManagerService.getBackupManagerBinder());
         } catch (IOException e) {
             Slog.e(TAG, "Exception opening file. " + e);
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index dc28cd1..e4ce62d 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -27,6 +27,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.util.Slog;
@@ -240,12 +241,13 @@
                         PackageManager.GET_SIGNING_CERTIFICATES);
                 homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
                 homeVersion = homeInfo.getLongVersionCode();
-                Signature[][] signingHistory = homeInfo.signingCertificateHistory;
-                if (signingHistory == null || signingHistory.length == 0) {
-                    Slog.e(TAG, "Home app has no signing history");
+                SigningInfo signingInfo = homeInfo.signingInfo;
+                if (signingInfo == null) {
+                    Slog.e(TAG, "Home app has no signing information");
                 } else {
                     // retrieve the newest sigs to back up
-                    Signature[] homeInfoSignatures = signingHistory[signingHistory.length - 1];
+                    // TODO (b/73988180) use entire signing history in case of rollbacks
+                    Signature[] homeInfoSignatures = signingInfo.getApkContentsSigners();
                     homeSigHashes = BackupUtils.hashSignatureArray(homeInfoSignatures);
                 }
             } catch (NameNotFoundException e) {
@@ -334,8 +336,8 @@
                         }
                     }
 
-                    Signature[][] signingHistory = info.signingCertificateHistory;
-                    if (signingHistory == null || signingHistory.length == 0) {
+                    SigningInfo signingInfo = info.signingInfo;
+                    if (signingInfo == null) {
                         Slog.w(TAG, "Not backing up package " + packName
                                 + " since it appears to have no signatures.");
                         continue;
@@ -358,7 +360,7 @@
                         outputBufferStream.writeInt(info.versionCode);
                     }
                     // retrieve the newest sigs to back up
-                    Signature[] infoSignatures = signingHistory[signingHistory.length - 1];
+                    Signature[] infoSignatures = signingInfo.getApkContentsSigners();
                     writeSignatureHashArray(outputBufferStream,
                             BackupUtils.hashSignatureArray(infoSignatures));
 
diff --git a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
index 77163d3..0c99b44 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -99,6 +99,7 @@
     private FullBackupObbConnection mObbConnection = null;
     private ParcelFileDescriptor[] mPipes = null;
     private byte[] mWidgetData = null;
+    private long mAppVersion;
 
     private long mBytes;
     private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
@@ -476,6 +477,9 @@
                 if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
                     Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
                             info);
+                    // readAppManifestAndReturnSignatures() will have extracted the version from
+                    // the manifest, so we save it to use in key-value restore later.
+                    mAppVersion = info.version;
                     PackageManagerInternal pmi = LocalServices.getService(
                             PackageManagerInternal.class);
                     RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
@@ -667,6 +671,8 @@
                                     Slog.d(TAG, "Restoring key-value file for " + pkg
                                             + " : " + info.path);
                                 }
+                                // Set the version saved from manifest entry.
+                                info.version = mAppVersion;
                                 KeyValueAdbRestoreEngine restoreEngine =
                                         new KeyValueAdbRestoreEngine(
                                                 mBackupManagerService,
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index 5518374..c39cceb 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Process;
 import android.util.Slog;
 
@@ -203,15 +204,16 @@
             return false;
         }
 
-        Signature[][] deviceHistorySigs = target.signingCertificateHistory;
-        if (ArrayUtils.isEmpty(deviceHistorySigs)) {
-            Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+        SigningInfo signingInfo = target.signingInfo;
+        if (signingInfo == null) {
+            Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" +
                     " PackageManager#GET_SIGNING_CERTIFICATES was not specified");
             return false;
         }
 
         if (DEBUG) {
-            Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceHistorySigs);
+            Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device="
+                    + signingInfo.getApkContentsSigners());
         }
 
         final int nStored = storedSigs.length;
@@ -225,8 +227,8 @@
         } else {
             // the app couldn't have rotated keys, since it was signed with multiple sigs - do
             // a check to see if we find a match for all stored sigs
-            // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
-            Signature[] deviceSigs = deviceHistorySigs[0];
+            // since app hasn't rotated key, we only need to check with its current signers
+            Signature[] deviceSigs = signingInfo.getApkContentsSigners();
             int nDevice = deviceSigs.length;
 
             // ensure that each stored sig matches an on-device sig
diff --git a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
index 994d5a9..a3d5601 100644
--- a/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/FullBackupUtils.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.util.Slog;
@@ -106,12 +107,13 @@
         printer.println(withApk ? "1" : "0");
 
         // write the signature block
-        Signature[][] signingHistory = pkg.signingCertificateHistory;
-        if (signingHistory == null) {
+        SigningInfo signingInfo = pkg.signingInfo;
+        if (signingInfo == null) {
             printer.println("0");
         } else {
             // retrieve the newest sigs to write
-            Signature[] signatures = signingHistory[signingHistory.length - 1];
+            // TODO (b/73988180) use entire signing history in case of rollbacks
+            Signature[] signatures = signingInfo.getApkContentsSigners();
             printer.println(Integer.toString(signatures.length));
             for (Signature sig : signatures) {
                 printer.println(sig.toCharsString());
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 6217e24..5e63397 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -52,6 +52,8 @@
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.PacketKeepalive;
 import android.net.IConnectivityManager;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkPolicyListener;
 import android.net.INetworkPolicyManager;
@@ -140,6 +142,7 @@
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.DnsManager;
 import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
+import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
 import com.android.server.connectivity.IpConnectivityMetrics;
 import com.android.server.connectivity.KeepaliveTracker;
 import com.android.server.connectivity.LingerMonitor;
@@ -155,6 +158,7 @@
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
 import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.net.BaseNetdEventCallback;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -256,6 +260,7 @@
     private INetworkStatsService mStatsService;
     private INetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
+    private IIpConnectivityMetrics mIpConnectivityMetrics;
 
     private String mCurrentTcpBufferSizes;
 
@@ -414,6 +419,9 @@
     // Handle changes in Private DNS settings.
     private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
 
+    // Handle private DNS validation status updates.
+    private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+
     private static String eventName(int what) {
         return sMagicDecoderRing.get(what, Integer.toString(what));
     }
@@ -1553,6 +1561,41 @@
         return true;
     }
 
+    @VisibleForTesting
+    protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
+        @Override
+        public void onPrivateDnsValidationEvent(int netId, String ipAddress,
+                String hostname, boolean validated) {
+            try {
+                mHandler.sendMessage(mHandler.obtainMessage(
+                        EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
+                        new PrivateDnsValidationUpdate(netId,
+                                InetAddress.parseNumericAddress(ipAddress),
+                                hostname, validated)));
+            } catch (IllegalArgumentException e) {
+                loge("Error parsing ip address in validation event");
+            }
+        }
+    };
+
+    @VisibleForTesting
+    protected void registerNetdEventCallback() {
+        mIpConnectivityMetrics =
+                (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+                ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+        if (mIpConnectivityMetrics == null) {
+            Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+        }
+
+        try {
+            mIpConnectivityMetrics.addNetdEventCallback(
+                    INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
+                    mNetdEventCallback);
+        } catch (Exception e) {
+            loge("Error registering netd callback: " + e);
+        }
+    }
+
     private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
@@ -1738,6 +1781,7 @@
 
     void systemReady() {
         loadGlobalProxy();
+        registerNetdEventCallback();
 
         synchronized (this) {
             mSystemReady = true;
@@ -2288,6 +2332,9 @@
 
         for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
             handlePerNetworkPrivateDnsConfig(nai, cfg);
+            if (networkRequiresValidation(nai)) {
+                handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+            }
         }
     }
 
@@ -2312,6 +2359,15 @@
         updateDnses(nai.linkProperties, null, nai.network.netId);
     }
 
+    private void handlePrivateDnsValidationUpdate(PrivateDnsValidationUpdate update) {
+        NetworkAgentInfo nai = getNetworkAgentInfoForNetId(update.netId);
+        if (nai == null) {
+            return;
+        }
+        mDnsManager.updatePrivateDnsValidation(update);
+        handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+    }
+
     private void updateLingerState(NetworkAgentInfo nai, long now) {
         // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
         // 2. If the network was lingering and there are now requests, unlinger it.
@@ -3002,6 +3058,10 @@
                 case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
                     handlePrivateDnsSettingsChanged();
                     break;
+                case EVENT_PRIVATE_DNS_VALIDATION_UPDATE:
+                    handlePrivateDnsValidationUpdate(
+                            (PrivateDnsValidationUpdate) msg.obj);
+                    break;
             }
         }
     }
@@ -4575,6 +4635,11 @@
 
         updateRoutes(newLp, oldLp, netId);
         updateDnses(newLp, oldLp, netId);
+        // Make sure LinkProperties represents the latest private DNS status.
+        // This does not need to be done before updateDnses because the
+        // LinkProperties are not the source of the private DNS configuration.
+        // updateDnses will fetch the private DNS configuration from DnsManager.
+        mDnsManager.updatePrivateDnsStatus(netId, newLp);
 
         // Start or stop clat accordingly to network state.
         networkAgent.updateClat(mNetd);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 46ce95d..022f865 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -52,6 +52,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
+import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
 import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
 import static android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY;
@@ -118,6 +119,7 @@
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
+import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS;
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.provider.Settings.System.FONT_SCALE;
@@ -765,7 +767,7 @@
     /**
      * The controller for all operations related to locktask.
      */
-    final LockTaskController mLockTaskController;
+    private final LockTaskController mLockTaskController;
 
     final UserController mUserController;
 
@@ -1282,17 +1284,24 @@
 
     private final class FontScaleSettingObserver extends ContentObserver {
         private final Uri mFontScaleUri = Settings.System.getUriFor(FONT_SCALE);
+        private final Uri mHideErrorDialogsUri = Settings.Global.getUriFor(HIDE_ERROR_DIALOGS);
 
         public FontScaleSettingObserver() {
             super(mHandler);
             ContentResolver resolver = mContext.getContentResolver();
             resolver.registerContentObserver(mFontScaleUri, false, this, UserHandle.USER_ALL);
+            resolver.registerContentObserver(mHideErrorDialogsUri, false, this,
+                    UserHandle.USER_ALL);
         }
 
         @Override
         public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
             if (mFontScaleUri.equals(uri)) {
                 updateFontScaleIfNeeded(userId);
+            } else if (mHideErrorDialogsUri.equals(uri)) {
+                synchronized (ActivityManagerService.this) {
+                    updateShouldShowDialogsLocked(getGlobalConfiguration());
+                }
             }
         }
     }
@@ -1949,7 +1958,7 @@
     final ActivityManagerConstants mConstants;
 
     // Encapsulates the global setting "hidden_api_blacklist_exemptions"
-    final HiddenApiBlacklist mHiddenApiBlacklist;
+    final HiddenApiSettings mHiddenApiBlacklist;
 
     PackageManagerInternal mPackageManagerInt;
 
@@ -2884,17 +2893,20 @@
     }
 
     /**
-     * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the
-     * latest value via a content observer.
+     * Encapsulates global settings related to hidden API enforcement behaviour, including tracking
+     * the latest value via a content observer.
      */
-    static class HiddenApiBlacklist extends ContentObserver {
+    static class HiddenApiSettings extends ContentObserver {
 
         private final Context mContext;
         private boolean mBlacklistDisabled;
         private String mExemptionsStr;
         private List<String> mExemptions = Collections.emptyList();
+        private int mLogSampleRate = -1;
+        @HiddenApiEnforcementPolicy private int mPolicyPreP = HIDDEN_API_ENFORCEMENT_DEFAULT;
+        @HiddenApiEnforcementPolicy private int mPolicyP = HIDDEN_API_ENFORCEMENT_DEFAULT;
 
-        public HiddenApiBlacklist(Handler handler, Context context) {
+        public HiddenApiSettings(Handler handler, Context context) {
             super(handler);
             mContext = context;
         }
@@ -2904,6 +2916,18 @@
                     Settings.Global.getUriFor(Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS),
                     false,
                     this);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE),
+                    false,
+                    this);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS),
+                    false,
+                    this);
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY_P_APPS),
+                    false,
+                    this);
             update();
         }
 
@@ -2923,13 +2947,41 @@
                 }
                 zygoteProcess.setApiBlacklistExemptions(mExemptions);
             }
+            int logSampleRate = Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.HIDDEN_API_ACCESS_LOG_SAMPLING_RATE, -1);
+            if (logSampleRate < 0 || logSampleRate > 0x10000) {
+                logSampleRate = -1;
+            }
+            if (logSampleRate != -1 && logSampleRate != mLogSampleRate) {
+                mLogSampleRate = logSampleRate;
+                zygoteProcess.setHiddenApiAccessLogSampleRate(mLogSampleRate);
+            }
+            mPolicyPreP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_PRE_P_APPS);
+            mPolicyP = getValidEnforcementPolicy(Settings.Global.HIDDEN_API_POLICY_P_APPS);
+        }
 
+        private @HiddenApiEnforcementPolicy int getValidEnforcementPolicy(String settingsKey) {
+            int policy = Settings.Global.getInt(mContext.getContentResolver(), settingsKey,
+                    ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT);
+            if (ApplicationInfo.isValidHiddenApiEnforcementPolicy(policy)) {
+                return policy;
+            } else {
+                return ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
+            }
         }
 
         boolean isDisabled() {
             return mBlacklistDisabled;
         }
 
+        @HiddenApiEnforcementPolicy int getPolicyForPrePApps() {
+            return mPolicyPreP;
+        }
+
+        @HiddenApiEnforcementPolicy int getPolicyForPApps() {
+            return mPolicyP;
+        }
+
         public void onChange(boolean selfChange) {
             update();
         }
@@ -3102,7 +3154,7 @@
             }
         };
 
-        mHiddenApiBlacklist = new HiddenApiBlacklist(mHandler, mContext);
+        mHiddenApiBlacklist = new HiddenApiSettings(mHandler, mContext);
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
@@ -4218,6 +4270,9 @@
             }
 
             if (!disableHiddenApiChecks && !mHiddenApiBlacklist.isDisabled()) {
+                app.info.maybeUpdateHiddenApiEnforcementPolicy(
+                        mHiddenApiBlacklist.getPolicyForPrePApps(),
+                        mHiddenApiBlacklist.getPolicyForPApps());
                 @HiddenApiEnforcementPolicy int policy =
                         app.info.getHiddenApiEnforcementPolicy();
                 int policyBits = (policy << Zygote.API_ENFORCEMENT_POLICY_SHIFT);
@@ -5251,7 +5306,7 @@
             synchronized (this) {
                 mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
                         ? REORDER_MOVE_TO_ORIGINAL_POSITION
-                        : REORDER_KEEP_IN_PLACE);
+                        : REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation");
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
@@ -12274,6 +12329,10 @@
         return mActivityStartController;
     }
 
+    LockTaskController getLockTaskController() {
+        return mLockTaskController;
+    }
+
     ClientLifecycleManager getLifecycleManager() {
         return mLifecycleManager;
     }
@@ -22323,7 +22382,7 @@
                 mUserController.getCurrentUserId());
 
         // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
-        mShowDialogs = shouldShowDialogs(mTempConfig);
+        updateShouldShowDialogsLocked(mTempConfig);
 
         AttributeCache ac = AttributeCache.instance();
         if (ac != null) {
@@ -22578,7 +22637,7 @@
      * A thought: SystemUI might also want to get told about this, the Power
      * dialog / global actions also might want different behaviors.
      */
-    private static boolean shouldShowDialogs(Configuration config) {
+    private void updateShouldShowDialogsLocked(Configuration config) {
         final boolean inputMethodExists = !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                                    && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH
                                    && config.navigation == Configuration.NAVIGATION_NONAV);
@@ -22587,7 +22646,9 @@
                 && !(modeType == Configuration.UI_MODE_TYPE_WATCH && Build.IS_USER)
                 && modeType != Configuration.UI_MODE_TYPE_TELEVISION
                 && modeType != Configuration.UI_MODE_TYPE_VR_HEADSET);
-        return inputMethodExists && uiModeSupportsDialogs;
+        final boolean hideDialogsSet = Settings.Global.getInt(mContext.getContentResolver(),
+                HIDE_ERROR_DIALOGS, 0) != 0;
+        mShowDialogs = inputMethodExists && uiModeSupportsDialogs && !hideDialogsSet;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index e539bf8..f32717a 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -779,11 +779,13 @@
      * @param task The new parent {@link TaskRecord}.
      */
     void setTask(TaskRecord task) {
-        setTask(task, false /*reparenting*/);
+        setTask(task /* task */, false /* reparenting */);
     }
 
     /**
      * This method should only be called by {@link TaskRecord#removeActivity(ActivityRecord)}.
+     * @param task          The new parent task.
+     * @param reparenting   Whether we're in the middle of reparenting.
      */
     void setTask(TaskRecord task, boolean reparenting) {
         // Do nothing if the {@link TaskRecord} is the same as the current {@link getTask}.
@@ -791,12 +793,19 @@
             return;
         }
 
-        final ActivityStack stack = getStack();
+        final ActivityStack oldStack = getStack();
+        final ActivityStack newStack = task != null ? task.getStack() : null;
 
-        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
-        // {@link ActivityRecord} from its current {@link ActivityStack}.
-        if (!reparenting && stack != null && (task == null || stack != task.getStack())) {
-            stack.onActivityRemovedFromStack(this);
+        // Inform old stack (if present) of activity removal and new stack (if set) of activity
+        // addition.
+        if (oldStack != newStack) {
+            if (!reparenting && oldStack != null) {
+                oldStack.onActivityRemovedFromStack(this);
+            }
+
+            if (newStack != null) {
+                newStack.onActivityAddedToStack(this);
+            }
         }
 
         this.task = task;
@@ -1073,8 +1082,15 @@
         // Must reparent first in window manager
         mWindowContainerController.reparent(newTask.getWindowContainerController(), position);
 
+        // Reparenting prevents informing the parent stack of activity removal in the case that
+        // the new stack has the same parent. we must manually signal here if this is not the case.
+        final ActivityStack prevStack = prevTask.getStack();
+
+        if (prevStack != newTask.getStack()) {
+            prevStack.onActivityRemovedFromStack(this);
+        }
         // Remove the activity from the old task and add it to the new task.
-        prevTask.removeActivity(this, true /*reparenting*/);
+        prevTask.removeActivity(this, true /* reparenting */);
 
         newTask.addActivityAtIndex(position, this);
     }
@@ -1198,10 +1214,7 @@
     }
 
     boolean isFocusable() {
-        if (inSplitScreenPrimaryWindowingMode() && mStackSupervisor.mIsDockMinimized) {
-            return false;
-        }
-        return getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable();
+        return mStackSupervisor.isFocusable(this, isAlwaysFocusable());
     }
 
     boolean isResizeable() {
@@ -1589,14 +1602,20 @@
     void pauseKeyDispatchingLocked() {
         if (!keysPaused) {
             keysPaused = true;
-            mWindowContainerController.pauseKeyDispatching();
+
+            if (mWindowContainerController != null) {
+                mWindowContainerController.pauseKeyDispatching();
+            }
         }
     }
 
     void resumeKeyDispatchingLocked() {
         if (keysPaused) {
             keysPaused = false;
-            mWindowContainerController.resumeKeyDispatching();
+
+            if (mWindowContainerController != null) {
+                mWindowContainerController.resumeKeyDispatching();
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 649d577..eb482c1 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -489,13 +489,13 @@
      */
     void onActivityStateChanged(ActivityRecord record, ActivityState state, String reason) {
         if (record == mResumedActivity && state != RESUMED) {
-            clearResumedActivity(reason + " - onActivityStateChanged");
+            setResumedActivity(null, reason + " - onActivityStateChanged");
         }
 
         if (state == RESUMED) {
             if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
                     + reason);
-            mResumedActivity = record;
+            setResumedActivity(record, reason + " - onActivityStateChanged");
             mService.setResumedActivityUncheckLocked(record, reason);
             mStackSupervisor.mRecentTasks.add(record.getTask());
         }
@@ -1077,13 +1077,8 @@
     }
 
     boolean isFocusable() {
-        if (getWindowConfiguration().canReceiveKeys()) {
-            return true;
-        }
-        // The stack isn't focusable. See if its top activity is focusable to force focus on the
-        // stack.
         final ActivityRecord r = topRunningActivityLocked();
-        return r != null && r.isFocusable();
+        return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
     }
 
     final boolean isAttached() {
@@ -2314,14 +2309,14 @@
         return mResumedActivity;
     }
 
-    /**
-     * Clears reference to currently resumed activity.
-     */
-    private void clearResumedActivity(String reason) {
-        if (DEBUG_STACK) Slog.d(TAG_STACK, "clearResumedActivity: " + mResumedActivity + " reason:"
-                + reason);
+    private void setResumedActivity(ActivityRecord r, String reason) {
+        if (mResumedActivity == r) {
+            return;
+        }
 
-        mResumedActivity = null;
+        if (DEBUG_STACK) Slog.d(TAG_STACK, "setResumedActivity stack:" + this + " + from: "
+                + mResumedActivity + " to:" + r + " reason:" + reason);
+        mResumedActivity = r;
     }
 
     @GuardedBy("mService")
@@ -3743,7 +3738,7 @@
                 }
 
                 if (endTask) {
-                    mService.mLockTaskController.clearLockedTask(task);
+                    mService.getLockTaskController().clearLockedTask(task);
                 }
             } else if (!r.isState(PAUSING)) {
                 // If the activity is PAUSING, we will complete the finish once
@@ -4027,14 +4022,20 @@
      * an activity moves away from the stack.
      */
     void onActivityRemovedFromStack(ActivityRecord r) {
-        if (mResumedActivity == r) {
-            clearResumedActivity("onActivityRemovedFromStack");
+        removeTimeoutsForActivityLocked(r);
+
+        if (mResumedActivity != null && mResumedActivity == r) {
+            setResumedActivity(null, "onActivityRemovedFromStack");
         }
-        if (mPausingActivity == r) {
+        if (mPausingActivity != null && mPausingActivity == r) {
             mPausingActivity = null;
         }
+    }
 
-        removeTimeoutsForActivityLocked(r);
+    void onActivityAddedToStack(ActivityRecord r) {
+        if(r.getState() == RESUMED) {
+            setResumedActivity(r, "onActivityAddedToStack");
+        }
     }
 
     /**
@@ -4639,7 +4640,7 @@
 
         // In LockTask mode, moving a locked task to the back of the stack may expose unlocked
         // ones. Therefore we need to check if this operation is allowed.
-        if (!mService.mLockTaskController.canMoveTaskToBack(tr)) {
+        if (!mService.getLockTaskController().canMoveTaskToBack(tr)) {
             return false;
         }
 
@@ -4752,30 +4753,32 @@
         mTmpBounds.clear();
         mTmpInsetBounds.clear();
 
-        for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
-            final TaskRecord task = mTaskHistory.get(i);
-            if (task.isResizeable()) {
-                if (inFreeformWindowingMode()) {
-                    // TODO: Can be removed now since each freeform task is in its own stack.
-                    // For freeform stack we don't adjust the size of the tasks to match that
-                    // of the stack, but we do try to make sure the tasks are still contained
-                    // with the bounds of the stack.
-                    mTmpRect2.set(task.getOverrideBounds());
-                    fitWithinBounds(mTmpRect2, bounds);
-                    task.updateOverrideConfiguration(mTmpRect2);
-                } else {
-                    task.updateOverrideConfiguration(taskBounds, insetBounds);
+        synchronized (mWindowManager.getWindowManagerLock()) {
+            for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+                final TaskRecord task = mTaskHistory.get(i);
+                if (task.isResizeable()) {
+                    if (inFreeformWindowingMode()) {
+                        // TODO: Can be removed now since each freeform task is in its own stack.
+                        // For freeform stack we don't adjust the size of the tasks to match that
+                        // of the stack, but we do try to make sure the tasks are still contained
+                        // with the bounds of the stack.
+                        mTmpRect2.set(task.getOverrideBounds());
+                        fitWithinBounds(mTmpRect2, bounds);
+                        task.updateOverrideConfiguration(mTmpRect2);
+                    } else {
+                        task.updateOverrideConfiguration(taskBounds, insetBounds);
+                    }
+                }
+
+                mTmpBounds.put(task.taskId, task.getOverrideBounds());
+                if (tempTaskInsetBounds != null) {
+                    mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
                 }
             }
 
-            mTmpBounds.put(task.taskId, task.getOverrideBounds());
-            if (tempTaskInsetBounds != null) {
-                mTmpInsetBounds.put(task.taskId, tempTaskInsetBounds);
-            }
+            mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
+            setBounds(bounds);
         }
-
-        mWindowContainerController.resize(bounds, mTmpBounds, mTmpInsetBounds);
-        setBounds(bounds);
     }
 
 
@@ -5084,7 +5087,12 @@
             onActivityRemovedFromStack(record);
         }
 
-        mTaskHistory.remove(task);
+        final boolean removed = mTaskHistory.remove(task);
+
+        if (removed) {
+            EventLog.writeEvent(EventLogTags.AM_REMOVE_TASK, task.taskId, getStackId());
+        }
+
         removeActivitiesFromLRUListLocked(task);
         updateTaskMovement(task, true);
 
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index e9a1358..cbf30bd 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -667,6 +667,14 @@
         return mFocusedStack;
     }
 
+    boolean isFocusable(ConfigurationContainer container, boolean alwaysFocusable) {
+        if (container.inSplitScreenPrimaryWindowingMode() && mIsDockMinimized) {
+            return false;
+        }
+
+        return container.getWindowConfiguration().canReceiveKeys() || alwaysFocusable;
+    }
+
     ActivityStack getLastStack() {
         return mLastFocusedStack;
     }
@@ -1372,12 +1380,13 @@
             mService.updateLruProcessLocked(app, true, null);
             mService.updateOomAdjLocked();
 
+            final LockTaskController lockTaskController = mService.getLockTaskController();
             if (task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE
                     || task.mLockTaskAuth == LOCK_TASK_AUTH_LAUNCHABLE_PRIV
                     || (task.mLockTaskAuth == LOCK_TASK_AUTH_WHITELISTED
-                            && mService.mLockTaskController.getLockTaskModeState()
-                            == LOCK_TASK_MODE_LOCKED)) {
-                mService.mLockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
+                            && lockTaskController.getLockTaskModeState()
+                                    == LOCK_TASK_MODE_LOCKED)) {
+                lockTaskController.startLockTaskMode(task, false, 0 /* blank UID */);
             }
 
             try {
@@ -2900,7 +2909,7 @@
         if (tr != null) {
             tr.removeTaskActivitiesLocked(pauseImmediately, reason);
             cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
-            mService.mLockTaskController.clearLockedTask(tr);
+            mService.getLockTaskController().clearLockedTask(tr);
             if (tr.isPersistable) {
                 mService.notifyTaskPersisterLocked(null, true);
             }
@@ -3814,7 +3823,7 @@
         pw.print(mRecentTasks.isRecentsComponentHomeActivity(mCurrentUser));
 
         getKeyguardController().dump(pw, prefix);
-        mService.mLockTaskController.dump(pw, prefix);
+        mService.getLockTaskController().dump(pw, prefix);
     }
 
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 5337566..fb89e67 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -1156,9 +1156,10 @@
             // If we are not able to proceed, disassociate the activity from the task. Leaving an
             // activity in an incomplete state can lead to issues, such as performing operations
             // without a window container.
-            if (!ActivityManager.isStartResultSuccessful(result)
-                    && mStartActivity.getTask() != null) {
-                mStartActivity.getTask().removeActivity(mStartActivity);
+            final ActivityStack stack = mStartActivity.getStack();
+            if (!ActivityManager.isStartResultSuccessful(result) && stack != null) {
+                stack.finishActivityLocked(mStartActivity, RESULT_CANCELED,
+                        null /* intentResultData */, "startActivity", true /* oomAdj */);
             }
             mService.mWindowManager.continueSurfaceLayout();
         }
@@ -1208,7 +1209,7 @@
             // When the flags NEW_TASK and CLEAR_TASK are set, then the task gets reused but
             // still needs to be a lock task mode violation since the task gets cleared out and
             // the device would otherwise leave the locked task.
-            if (mService.mLockTaskController.isLockTaskModeViolation(reusedActivity.getTask(),
+            if (mService.getLockTaskController().isLockTaskModeViolation(reusedActivity.getTask(),
                     (mLaunchFlags & (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))
                             == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK))) {
                 Slog.e(TAG, "startActivityUnchecked: Attempt to violate Lock Task Mode");
@@ -2020,7 +2021,7 @@
             mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
         }
 
-        if (mService.mLockTaskController.isLockTaskModeViolation(mStartActivity.getTask())) {
+        if (mService.getLockTaskController().isLockTaskModeViolation(mStartActivity.getTask())) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
             return START_RETURN_LOCK_TASK_MODE_VIOLATION;
         }
@@ -2043,7 +2044,7 @@
     }
 
     private int setTaskFromSourceRecord() {
-        if (mService.mLockTaskController.isLockTaskModeViolation(mSourceRecord.getTask())) {
+        if (mService.getLockTaskController().isLockTaskModeViolation(mSourceRecord.getTask())) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
             return START_RETURN_LOCK_TASK_MODE_VIOLATION;
         }
@@ -2137,7 +2138,7 @@
     private int setTaskFromInTask() {
         // The caller is asking that the new activity be started in an explicit
         // task it has provided to us.
-        if (mService.mLockTaskController.isLockTaskModeViolation(mInTask)) {
+        if (mService.getLockTaskController().isLockTaskModeViolation(mInTask)) {
             Slog.e(TAG, "Attempted Lock Task Mode violation mStartActivity=" + mStartActivity);
             return START_RETURN_LOCK_TASK_MODE_VIOLATION;
         }
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9caef4a..40b9e4f 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -133,4 +133,7 @@
 # The activity's onStart has been called.
 30059 am_on_start_called (User|1|5),(Component Name|3),(Reason|3)
 # The activity's onDestroy has been called.
-30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
\ No newline at end of file
+30060 am_on_destroy_called (User|1|5),(Component Name|3),(Reason|3)
+
+# The task is being removed from its parent stack
+30061 am_remove_task (Task ID|1|5), (Stack ID|1|5)
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 365c436..a20452b 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -523,7 +523,7 @@
         }
         for (int i = mTasks.size() - 1; i >= 0; --i) {
             final TaskRecord tr = mTasks.get(i);
-            if (tr.userId == userId && !mService.mLockTaskController.isTaskWhitelisted(tr)) {
+            if (tr.userId == userId && !mService.getLockTaskController().isTaskWhitelisted(tr)) {
                 remove(tr);
             }
         }
@@ -534,8 +534,8 @@
             final TaskRecord tr = mTasks.get(i);
             final String taskPackageName =
                     tr.getBaseIntent().getComponent().getPackageName();
-            if (tr.userId != userId) return;
-            if (!taskPackageName.equals(packageName)) return;
+            if (tr.userId != userId) continue;
+            if (!taskPackageName.equals(packageName)) continue;
 
             mService.mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS,
                     "remove-package-task");
@@ -1156,7 +1156,7 @@
         }
 
         // If we're in lock task mode, ignore the root task
-        if (task == mService.mLockTaskController.getRootTask()) {
+        if (task == mService.getLockTaskController().getRootTask()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 17eeb5b..06b5e20 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -46,6 +46,8 @@
  */
 class RecentsAnimation implements RecentsAnimationCallbacks {
     private static final String TAG = RecentsAnimation.class.getSimpleName();
+    // TODO (b/73188263): Reset debugging flags
+    private static final boolean DEBUG = true;
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
@@ -74,10 +76,13 @@
 
     void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
             ComponentName recentsComponent, int recentsUid) {
+        if (DEBUG) Slog.d(TAG, "startRecentsActivity(): intent=" + intent);
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
 
         if (!mWindowManager.canStartRecentsAnimation()) {
             notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+            if (DEBUG) Slog.d(TAG, "Can't start recents animation, nextAppTransition="
+                        + mWindowManager.getPendingAppTransition());
             return;
         }
 
@@ -97,6 +102,7 @@
             mRestoreTargetBehindStack = display.getStackAbove(targetStack);
             if (mRestoreTargetBehindStack == null) {
                 notifyAnimationCancelBeforeStart(recentsAnimationRunner);
+                if (DEBUG) Slog.d(TAG, "No stack above target stack=" + targetStack);
                 return;
             }
         }
@@ -119,6 +125,8 @@
                 // Move the recents activity into place for the animation if it is not top most
                 display = targetActivity.getDisplay();
                 display.moveStackBehindBottomMostVisibleStack(targetStack);
+                if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+                            + display.getStackAbove(targetStack));
             } else {
                 // No recents activity
                 ActivityOptions options = ActivityOptions.makeBasic();
@@ -140,6 +148,8 @@
                 display = targetActivity.getDisplay();
 
                 // TODO: Maybe wait for app to draw in this particular case?
+
+                if (DEBUG) Slog.d(TAG, "Started intent=" + intent);
             }
 
             // Mark the target activity as launch-behind to bump its visibility for the
@@ -148,7 +158,8 @@
 
             // Fetch all the surface controls and pass them to the client to get the animation
             // started
-            mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+            mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
+                    "startRecentsActivity");
             mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
                     this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds());
 
@@ -158,6 +169,9 @@
 
             mStackSupervisor.getActivityMetricsLogger().notifyActivityLaunched(START_TASK_TO_FRONT,
                     targetActivity);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to start recents activity", e);
+            throw e;
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -167,6 +181,9 @@
     @Override
     public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode) {
         synchronized (mService) {
+            if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+                        + mWindowManager.getRecentsAnimationController()
+                        + " reorderMode=" + reorderMode);
             if (mWindowManager.getRecentsAnimationController() == null) return;
 
             // Just to be sure end the launch hint in case the target activity was never launched.
@@ -187,6 +204,9 @@
                     final ActivityStack targetStack = mDefaultDisplay.getStack(
                             WINDOWING_MODE_UNDEFINED, mTargetActivityType);
                     final ActivityRecord targetActivity = targetStack.getTopActivity();
+                    if (DEBUG) Slog.d(TAG, "onAnimationFinished(): targetStack=" + targetStack
+                            + " targetActivity=" + targetActivity
+                            + " mRestoreTargetBehindStack=" + mRestoreTargetBehindStack);
                     if (targetActivity == null) {
                         return;
                     }
@@ -198,10 +218,27 @@
                         // Bring the target stack to the front
                         mStackSupervisor.mNoAnimActivities.add(targetActivity);
                         targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+                        if (DEBUG) {
+                            final ActivityStack topStack = getTopNonAlwaysOnTopStack();
+                            if (topStack != targetStack) {
+                                Slog.w(TAG, "Expected target stack=" + targetStack
+                                        + " to be top most but found stack=" + topStack);
+                            }
+                        }
                     } else if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION){
                         // Restore the target stack to its previous position
                         final ActivityDisplay display = targetActivity.getDisplay();
                         display.moveStackBehindStack(targetStack, mRestoreTargetBehindStack);
+                        if (DEBUG) {
+                            final ActivityStack aboveTargetStack =
+                                    mDefaultDisplay.getStackAbove(targetStack);
+                            if (mRestoreTargetBehindStack != null
+                                    && aboveTargetStack != mRestoreTargetBehindStack) {
+                                Slog.w(TAG, "Expected target stack=" + targetStack
+                                        + " to restored behind stack=" + mRestoreTargetBehindStack
+                                        + " but it is behind stack=" + aboveTargetStack);
+                            }
+                        }
                     } else {
                         // Keep target stack in place, nothing changes, so ignore the transition
                         // logic below
@@ -221,6 +258,9 @@
                     // split-screen), or we will have returned to the app, and the minimized state
                     // should be reset
                     mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Failed to clean up recents activity", e);
+                    throw e;
                 } finally {
                     mWindowManager.continueSurfaceLayout();
                     Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
@@ -239,4 +279,18 @@
             Slog.e(TAG, "Failed to cancel recents animation before start", e);
         }
     }
+
+    /**
+     * @return The top stack that is not always-on-top.
+     */
+    private ActivityStack getTopNonAlwaysOnTopStack() {
+        for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
+            final ActivityStack s = mDefaultDisplay.getChildAt(i);
+            if (s.getWindowConfiguration().isAlwaysOnTop()) {
+                continue;
+            }
+            return s;
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/am/SafeActivityOptions.java b/services/core/java/com/android/server/am/SafeActivityOptions.java
index ac6f01f..2de75273 100644
--- a/services/core/java/com/android/server/am/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/am/SafeActivityOptions.java
@@ -210,7 +210,7 @@
         // Check if someone tries to launch an unwhitelisted activity into LockTask mode.
         final boolean lockTaskMode = options.getLockTaskMode();
         if (aInfo != null && lockTaskMode
-                && !supervisor.mService.mLockTaskController.isPackageWhitelisted(
+                && !supervisor.mService.getLockTaskController().isPackageWhitelisted(
                         UserHandle.getUserId(callingUid), aInfo.packageName)) {
             final String msg = "Permission Denial: starting " + getIntentString(intent)
                     + " from " + callerApp + " (pid=" + callingPid
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 034cb2e..0e418ad 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -451,7 +451,7 @@
     }
 
     void removeWindowContainer() {
-        mService.mLockTaskController.clearLockedTask(this);
+        mService.getLockTaskController().clearLockedTask(this);
         mWindowContainerController.removeContainer();
         if (!getWindowConfiguration().persistTaskBounds()) {
             // Reset current bounds for task whose bounds shouldn't be persisted so it uses
@@ -927,7 +927,26 @@
         if (stack != null && !stack.isInStackLocked(this)) {
             throw new IllegalStateException("Task must be added as a Stack child first.");
         }
+        final ActivityStack oldStack = mStack;
         mStack = stack;
+
+        // If the new {@link TaskRecord} is from a different {@link ActivityStack}, remove this
+        // {@link ActivityRecord} from its current {@link ActivityStack}.
+
+        if (oldStack != mStack) {
+            for (int i = getChildCount() - 1; i >= 0; --i) {
+                final ActivityRecord activity = getChildAt(i);
+
+                if (oldStack != null) {
+                    oldStack.onActivityRemovedFromStack(activity);
+                }
+
+                if (mStack != null) {
+                    stack.onActivityAddedToStack(activity);
+                }
+            }
+        }
+
         onParentChanged();
     }
 
@@ -1232,6 +1251,7 @@
 
         index = Math.min(size, index);
         mActivities.add(index, r);
+
         updateEffectiveIntent();
         if (r.isPersistable()) {
             mService.notifyTaskPersisterLocked(this, false);
@@ -1257,7 +1277,7 @@
      * @return true if this was the last activity in the task.
      */
     boolean removeActivity(ActivityRecord r) {
-        return removeActivity(r, false /*reparenting*/);
+        return removeActivity(r, false /* reparenting */);
     }
 
     boolean removeActivity(ActivityRecord r, boolean reparenting) {
@@ -1266,7 +1286,7 @@
                     "Activity=" + r + " does not belong to task=" + this);
         }
 
-        r.setTask(null /*task*/, reparenting);
+        r.setTask(null /* task */, reparenting /* reparenting */);
 
         if (mActivities.remove(r) && r.fullscreen) {
             // Was previously in list.
@@ -1446,9 +1466,10 @@
         }
 
         final String pkg = (realActivity != null) ? realActivity.getPackageName() : null;
+        final LockTaskController lockTaskController = mService.getLockTaskController();
         switch (r.lockTaskLaunchMode) {
             case LOCK_TASK_LAUNCH_MODE_DEFAULT:
-                mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
                         ? LOCK_TASK_AUTH_WHITELISTED : LOCK_TASK_AUTH_PINNABLE;
                 break;
 
@@ -1461,7 +1482,7 @@
                 break;
 
             case LOCK_TASK_LAUNCH_MODE_IF_WHITELISTED:
-                mLockTaskAuth = mService.mLockTaskController.isPackageWhitelisted(userId, pkg)
+                mLockTaskAuth = lockTaskController.isPackageWhitelisted(userId, pkg)
                         ? LOCK_TASK_AUTH_LAUNCHABLE : LOCK_TASK_AUTH_PINNABLE;
                 break;
         }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index a294334..fecb934 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1184,11 +1184,6 @@
             Slog.w(TAG, "No user info for user #" + targetUserId);
             return false;
         }
-        if (!targetUserInfo.isDemo() && UserManager.isDeviceInDemoMode(mInjector.getContext())) {
-            Slog.w(TAG, "Cannot switch to non-demo user #" + targetUserId
-                    + " when device is in demo mode");
-            return false;
-        }
         if (!targetUserInfo.supportsSwitchTo()) {
             Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported");
             return false;
@@ -2220,7 +2215,7 @@
 
         protected void clearAllLockedTasks(String reason) {
             synchronized (mService) {
-                mService.mLockTaskController.clearLockedTasks(reason);
+                mService.getLockTaskController().clearLockedTasks(reason);
             }
         }
 
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
index d817534..96c5621 100644
--- a/services/core/java/com/android/server/backup/BackupUtils.java
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -20,6 +20,7 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.util.Slog;
 
 import com.android.internal.util.ArrayUtils;
@@ -55,16 +56,16 @@
             return false;
         }
 
-        Signature[][] deviceHistorySigs = target.signingCertificateHistory;
-        if (ArrayUtils.isEmpty(deviceHistorySigs)) {
-            Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
+        SigningInfo signingInfo = target.signingInfo;
+        if (signingInfo == null) {
+            Slog.w(TAG, "signingInfo is empty, app was either unsigned or the flag" +
                     " PackageManager#GET_SIGNING_CERTIFICATES was not specified");
             return false;
         }
 
         if (DEBUG) {
             Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
-                    + " device=" + deviceHistorySigs);
+                    + " device=" + signingInfo.getApkContentsSigners());
         }
 
         final int nStored = storedSigHashes.size();
@@ -78,8 +79,9 @@
         } else {
             // the app couldn't have rotated keys, since it was signed with multiple sigs - do
             // a check to see if we find a match for all stored sigs
-            // since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
-            ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
+            // since app hasn't rotated key, we only need to check with current signers
+            ArrayList<byte[]> deviceHashes =
+                    hashSignatureArray(signingInfo.getApkContentsSigners());
             int nDevice = deviceHashes.size();
             // ensure that each stored sig matches an on-device sig
             for (int i = 0; i < nStored; i++) {
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 2a361a0..7aaac06 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -37,10 +37,10 @@
 import android.net.dns.ResolvUtil;
 import android.os.Binder;
 import android.os.INetworkManagementService;
-import android.os.Handler;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.util.Pair;
 import android.util.Slog;
 
 import com.android.server.connectivity.MockableSystemProperties;
@@ -50,8 +50,12 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.stream.Collectors;
+import java.util.Set;
 import java.util.StringJoiner;
 
 
@@ -110,6 +114,7 @@
  */
 public class DnsManager {
     private static final String TAG = DnsManager.class.getSimpleName();
+    private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig();
 
     /* Defaults for resolver parameters. */
     private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
@@ -183,11 +188,89 @@
         };
     }
 
+    public static class PrivateDnsValidationUpdate {
+        final public int netId;
+        final public InetAddress ipAddress;
+        final public String hostname;
+        final public boolean validated;
+
+        public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
+                String hostname, boolean validated) {
+            this.netId = netId;
+            this.ipAddress = ipAddress;
+            this.hostname = hostname;
+            this.validated = validated;
+        }
+    }
+
+    private static class PrivateDnsValidationStatuses {
+        enum ValidationStatus {
+            IN_PROGRESS,
+            FAILED,
+            SUCCEEDED
+        }
+
+        // Validation statuses of <hostname, ipAddress> pairs for a single netId
+        private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap;
+
+        private PrivateDnsValidationStatuses() {
+            mValidationMap = new HashMap<>();
+        }
+
+        private boolean hasValidatedServer() {
+            for (ValidationStatus status : mValidationMap.values()) {
+                if (status == ValidationStatus.SUCCEEDED) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private void updateTrackedDnses(String[] ipAddresses, String hostname) {
+            Set<Pair<String, InetAddress>> latestDnses = new HashSet<>();
+            for (String ipAddress : ipAddresses) {
+                try {
+                    latestDnses.add(new Pair(hostname,
+                            InetAddress.parseNumericAddress(ipAddress)));
+                } catch (IllegalArgumentException e) {}
+            }
+            // Remove <hostname, ipAddress> pairs that should not be tracked.
+            for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it =
+                    mValidationMap.entrySet().iterator(); it.hasNext(); ) {
+                Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next();
+                if (!latestDnses.contains(entry.getKey())) {
+                    it.remove();
+                }
+            }
+            // Add new <hostname, ipAddress> pairs that should be tracked.
+            for (Pair<String, InetAddress> p : latestDnses) {
+                if (!mValidationMap.containsKey(p)) {
+                    mValidationMap.put(p, ValidationStatus.IN_PROGRESS);
+                }
+            }
+        }
+
+        private void updateStatus(PrivateDnsValidationUpdate update) {
+            Pair<String, InetAddress> p = new Pair(update.hostname,
+                    update.ipAddress);
+            if (!mValidationMap.containsKey(p)) {
+                return;
+            }
+            if (update.validated) {
+                mValidationMap.put(p, ValidationStatus.SUCCEEDED);
+            } else {
+                mValidationMap.put(p, ValidationStatus.FAILED);
+            }
+        }
+    }
+
     private final Context mContext;
     private final ContentResolver mContentResolver;
     private final INetworkManagementService mNMS;
     private final MockableSystemProperties mSystemProperties;
+    // TODO: Replace these Maps with SparseArrays.
     private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+    private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
 
     private int mNumDnsEntries;
     private int mSampleValidity;
@@ -203,6 +286,7 @@
         mNMS = nms;
         mSystemProperties = sp;
         mPrivateDnsMap = new HashMap<>();
+        mPrivateDnsValidationMap = new HashMap<>();
 
         // TODO: Create and register ContentObservers to track every setting
         // used herein, posting messages to respond to changes.
@@ -214,6 +298,7 @@
 
     public void removeNetwork(Network network) {
         mPrivateDnsMap.remove(network.netId);
+        mPrivateDnsValidationMap.remove(network.netId);
     }
 
     public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -223,6 +308,40 @@
                 : mPrivateDnsMap.remove(network.netId);
     }
 
+    public void updatePrivateDnsStatus(int netId, LinkProperties lp) {
+        // Use the PrivateDnsConfig data pushed to this class instance
+        // from ConnectivityService.
+        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+                PRIVATE_DNS_OFF);
+
+        final boolean useTls = privateDnsCfg.useTls;
+        final boolean strictMode = privateDnsCfg.inStrictMode();
+        final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+        if (strictMode) {
+            lp.setUsePrivateDns(true);
+            lp.setPrivateDnsServerName(tlsHostname);
+        } else if (useTls) {
+            // We are in opportunistic mode. Private DNS should be used if there
+            // is a known DNS-over-TLS validated server.
+            boolean validated = mPrivateDnsValidationMap.containsKey(netId) &&
+                    mPrivateDnsValidationMap.get(netId).hasValidatedServer();
+            lp.setUsePrivateDns(validated);
+            lp.setPrivateDnsServerName(null);
+        } else {
+            // Private DNS is disabled.
+            lp.setUsePrivateDns(false);
+            lp.setPrivateDnsServerName(null);
+        }
+    }
+
+    public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) {
+        final PrivateDnsValidationStatuses statuses =
+                mPrivateDnsValidationMap.get(update.netId);
+        if (statuses == null) return;
+        statuses.updateStatus(update);
+    }
+
     public void setDnsConfigurationForNetwork(
             int netId, LinkProperties lp, boolean isDefaultNetwork) {
         final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
@@ -238,10 +357,11 @@
         //
         // At this time we do not attempt to enable Private DNS on non-Internet
         // networks like IMS.
-        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
+        final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+                PRIVATE_DNS_OFF);
 
-        final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
-        final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+        final boolean useTls = privateDnsCfg.useTls;
+        final boolean strictMode = privateDnsCfg.inStrictMode();
         final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
         final String[] tlsServers =
                 strictMode ? NetworkUtils.makeStrings(
@@ -251,6 +371,17 @@
                 : useTls ? assignedServers  // Opportunistic
                 : new String[0];            // Off
 
+        // Prepare to track the validation status of the DNS servers in the
+        // resolver config when private DNS is in opportunistic or strict mode.
+        if (useTls) {
+            if (!mPrivateDnsValidationMap.containsKey(netId)) {
+                mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
+            }
+            mPrivateDnsValidationMap.get(netId).updateTrackedDnses(tlsServers, tlsHostname);
+        } else {
+            mPrivateDnsValidationMap.remove(netId);
+        }
+
         Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
                 netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
                 Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 3868ea6..6fa999c 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -24,6 +24,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
 import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
 
 import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
@@ -55,11 +56,14 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
+import android.util.DataUnit;
 import android.util.DebugUtils;
 import android.util.Pair;
+import android.util.Range;
 import android.util.Slog;
 
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -70,6 +74,7 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.temporal.ChronoUnit;
+import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
 
@@ -95,9 +100,11 @@
     private final Clock mClock;
     private final Dependencies mDeps;
     private final ContentResolver mResolver;
-    private final SettingsObserver mSettingsObserver;
     private final ConfigChangeReceiver mConfigChangeReceiver;
 
+    @VisibleForTesting
+    final ContentObserver mSettingsObserver;
+
     private ConnectivityManager mCM;
     private NetworkPolicyManager mNPM;
     private NetworkStatsManager mStatsManager;
@@ -266,9 +273,9 @@
         }
 
         private long getRemainingDailyBudget(long limitBytes,
-                Pair<ZonedDateTime, ZonedDateTime> cycle) {
-            final long start = cycle.first.toInstant().toEpochMilli();
-            final long end = cycle.second.toInstant().toEpochMilli();
+                Range<ZonedDateTime> cycle) {
+            final long start = cycle.getLower().toInstant().toEpochMilli();
+            final long end = cycle.getUpper().toInstant().toEpochMilli();
             final long totalBytes = getNetworkTotalBytes(start, end);
             final long remainingBytes = totalBytes == -1 ? 0 : Math.max(0, limitBytes - totalBytes);
             // 1 + ((end - now - 1) / millisInDay with integers is equivalent to:
@@ -287,12 +294,16 @@
 
             final NetworkPolicy[] policies = mNPM.getNetworkPolicies();
             for (NetworkPolicy policy : policies) {
-                if (hasActiveCycle(policy) && policy.template.matches(identity)) {
+                if (policy.hasCycle() && policy.template.matches(identity)) {
+                    final long cycleStart = policy.cycleIterator().next().getLower()
+                            .toInstant().toEpochMilli();
                     // Prefer user-defined warning, otherwise use hard limit
-                    final long policyBytes = (policy.warningBytes == LIMIT_DISABLED)
-                            ? policy.limitBytes : policy.warningBytes;
+                    final long activeWarning = getActiveWarning(policy, cycleStart);
+                    final long policyBytes = (activeWarning == WARNING_DISABLED)
+                            ? getActiveLimit(policy, cycleStart)
+                            : activeWarning;
 
-                    if (policyBytes != LIMIT_DISABLED) {
+                    if (policyBytes != LIMIT_DISABLED && policyBytes != WARNING_DISABLED) {
                         final long policyBudget = getRemainingDailyBudget(policyBytes,
                                 policy.cycleIterator().next());
                         minQuota = Math.min(minQuota, policyBudget);
@@ -323,11 +334,11 @@
                 if (DBG) Slog.d(TAG, "Setting quota: " + quota + " bytes");
             }
 
+            // TODO: re-register if day changed: budget may have run out but should be refreshed.
             if (haveMultipathBudget() && quota == mQuota) {
-                // If we already have a usage callback pending , there's no need to re-register it
+                // If there is already a usage callback pending , there's no need to re-register it
                 // if the quota hasn't changed. The callback will simply fire as expected when the
-                // budget is spent. Also: if we re-register the callback when we're below the
-                // UsageCallback's minimum value of 2MB, we'll overshoot the budget.
+                // budget is spent.
                 if (DBG) Slog.d(TAG, "Quota still " + quota + ", not updating.");
                 return;
             }
@@ -337,7 +348,17 @@
             // ourselves any budget to work with.
             final long usage = getDailyNonDefaultDataUsage();
             final long budget = (usage == -1) ? 0 : Math.max(0, quota - usage);
-            if (budget > 0) {
+
+            // Only consider budgets greater than MIN_THRESHOLD_BYTES, otherwise the callback will
+            // fire late, after data usage went over budget. Also budget should be 0 if remaining
+            // data is close to 0.
+            // This is necessary because the usage callback does not accept smaller thresholds.
+            // Because it snaps everything to MIN_THRESHOLD_BYTES, the lesser of the two evils is
+            // to snap to 0 here.
+            // This will only be called if the total quota for the day changed, not if usage changed
+            // since last time, so even if this is called very often the budget will not snap to 0
+            // as soon as there are less than 2MB left for today.
+            if (budget > NetworkStatsManager.MIN_THRESHOLD_BYTES) {
                 if (DBG) Slog.d(TAG, "Setting callback for " + budget +
                         " bytes on network " + network);
                 registerUsageCallback(budget);
@@ -387,9 +408,16 @@
         }
     }
 
-    private static boolean hasActiveCycle(NetworkPolicy policy) {
-        return policy.hasCycle() && policy.lastLimitSnooze <
-                policy.cycleIterator().next().first.toInstant().toEpochMilli();
+    private static long getActiveWarning(NetworkPolicy policy, long cycleStart) {
+        return policy.lastWarningSnooze < cycleStart
+                ? policy.warningBytes
+                : WARNING_DISABLED;
+    }
+
+    private static long getActiveLimit(NetworkPolicy policy, long cycleStart) {
+        return policy.lastLimitSnooze < cycleStart
+                ? policy.limitBytes
+                : LIMIT_DISABLED;
     }
 
     // Only ever updated on the handler thread. Accessed from other binder threads to retrieve
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d37dd18..df6a6f8 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -249,6 +249,7 @@
                 "CarrierConfigChangeListener", mContext, smHandler, filter,
                 (Intent ignored) -> {
                     mLog.log("OBSERVED carrier config change");
+                    updateConfiguration();
                     reevaluateSimCardProvisioning();
                 });
         // TODO: Remove SimChangeListener altogether. For now, we retain it
@@ -261,28 +262,35 @@
                 });
 
         mStateReceiver = new StateReceiver();
-        filter = new IntentFilter();
+
+        // Load tethering configuration.
+        updateConfiguration();
+
+        startStateMachineUpdaters();
+    }
+
+    private void startStateMachineUpdaters() {
+        mCarrierConfigChange.startListening();
+
+        final Handler handler = mTetherMasterSM.getHandler();
+        IntentFilter filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_STATE);
         filter.addAction(CONNECTIVITY_ACTION);
         filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
-        mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+        mContext.registerReceiver(mStateReceiver, filter, null, handler);
 
         filter = new IntentFilter();
         filter.addAction(Intent.ACTION_MEDIA_SHARED);
         filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
         filter.addDataScheme("file");
-        mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+        mContext.registerReceiver(mStateReceiver, filter, null, handler);
 
-        UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
-
-        // this check is useful only for some unit tests; example: ConnectivityServiceTest
-        if (userManager != null) {
-            userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+        final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+        // This check is useful only for some unit tests; example: ConnectivityServiceTest.
+        if (umi != null) {
+            umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
         }
-
-        // load device config info
-        updateConfiguration();
     }
 
     private WifiManager getWifiManager() {
@@ -384,17 +392,15 @@
      */
     @VisibleForTesting
     protected boolean isTetherProvisioningRequired() {
-        String[] provisionApp = mContext.getResources().getStringArray(
-                com.android.internal.R.array.config_mobile_hotspot_provision_app);
+        final TetheringConfiguration cfg = mConfig;
         if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
-                || provisionApp == null) {
+                || cfg.provisioningApp.length == 0) {
             return false;
         }
-
         if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
             return false;
         }
-        return (provisionApp.length == 2);
+        return (cfg.provisioningApp.length == 2);
     }
 
     // The logic here is aimed solely at confirming that a CarrierConfig exists
@@ -417,20 +423,6 @@
         return !isEntitlementCheckRequired;
     }
 
-    // Used by the SIM card change observation code.
-    // TODO: De-duplicate above code.
-    private boolean hasMobileHotspotProvisionApp() {
-        try {
-            if (!mContext.getResources().getString(com.android.internal.R.string.
-                    config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
-                Log.d(TAG, "re-evaluate provisioning");
-                return true;
-            }
-        } catch (Resources.NotFoundException e) {}
-        Log.d(TAG, "no prov-check needed for new SIM");
-        return false;
-    }
-
     /**
      * Enables or disables tethering for the given type. This should only be called once
      * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
@@ -1187,7 +1179,7 @@
     }
 
     private void reevaluateSimCardProvisioning() {
-        if (!hasMobileHotspotProvisionApp()) return;
+        if (!mConfig.hasMobileHotspotProvisionApp()) return;
         if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
 
         ArrayList<Integer> tethered = new ArrayList<>();
@@ -1546,7 +1538,6 @@
                     return;
                 }
 
-                mCarrierConfigChange.startListening();
                 mSimChange.startListening();
                 mUpstreamNetworkMonitor.start();
 
@@ -1564,7 +1555,6 @@
                 mOffload.stop();
                 mUpstreamNetworkMonitor.stop();
                 mSimChange.stopListening();
-                mCarrierConfigChange.stopListening();
                 notifyDownstreamsOfNewUpstreamIface(null);
                 handleNewUpstreamNetworkState(null);
             }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 09bce7f..454c579 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,14 +21,23 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
 import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
 
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
-import android.telephony.TelephonyManager;
 import android.net.util.SharedLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.R;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -51,6 +60,8 @@
 public class TetheringConfiguration {
     private static final String TAG = TetheringConfiguration.class.getSimpleName();
 
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
     @VisibleForTesting
     public static final int DUN_NOT_REQUIRED = 0;
     public static final int DUN_REQUIRED = 1;
@@ -79,18 +90,18 @@
     public final String[] dhcpRanges;
     public final String[] defaultIPv4DNS;
 
+    public final String[] provisioningApp;
+    public final String provisioningAppNoUi;
+
     public TetheringConfiguration(Context ctx, SharedLog log) {
         final SharedLog configLog = log.forSubComponent("config");
 
-        tetherableUsbRegexs = ctx.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_usb_regexs);
+        tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
         // TODO: Evaluate deleting this altogether now that Wi-Fi always passes
         // us an interface name. Careful consideration needs to be given to
         // implications for Settings and for provisioning checks.
-        tetherableWifiRegexs = ctx.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_wifi_regexs);
-        tetherableBluetoothRegexs = ctx.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_bluetooth_regexs);
+        tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
+        tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
 
         dunCheck = checkDunRequired(ctx);
         configLog.log("DUN check returned: " + dunCheckString(dunCheck));
@@ -101,6 +112,9 @@
         dhcpRanges = getDhcpRanges(ctx);
         defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
 
+        provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
+        provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+
         configLog.log(toString());
     }
 
@@ -116,6 +130,10 @@
         return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
     }
 
+    public boolean hasMobileHotspotProvisionApp() {
+        return !TextUtils.isEmpty(provisioningAppNoUi);
+    }
+
     public void dump(PrintWriter pw) {
         dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
         dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -129,6 +147,10 @@
 
         dumpStringArray(pw, "dhcpRanges", dhcpRanges);
         dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
+
+        dumpStringArray(pw, "provisioningApp", provisioningApp);
+        pw.print("provisioningAppNoUi: ");
+        pw.println(provisioningAppNoUi);
     }
 
     public String toString() {
@@ -140,6 +162,8 @@
         sj.add(String.format("isDunRequired:%s", isDunRequired));
         sj.add(String.format("preferredUpstreamIfaceTypes:%s",
                 makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+        sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
+        sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
         return String.format("TetheringConfiguration{%s}", sj.toString());
     }
 
@@ -159,6 +183,7 @@
     }
 
     private static String makeString(String[] strings) {
+        if (strings == null) return "null";
         final StringJoiner sj = new StringJoiner(",", "[", "]");
         for (String s : strings) sj.add(s);
         return sj.toString();
@@ -195,8 +220,7 @@
     }
 
     private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
-        final int ifaceTypes[] = ctx.getResources().getIntArray(
-                com.android.internal.R.array.config_tether_upstream_types);
+        final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
         final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
         for (int i : ifaceTypes) {
             switch (i) {
@@ -247,14 +271,30 @@
     }
 
     private static String[] getDhcpRanges(Context ctx) {
-        final String[] fromResource = ctx.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_dhcp_range);
+        final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
         if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
             return fromResource;
         }
         return copy(DHCP_DEFAULT_RANGE);
     }
 
+    private static String getProvisioningAppNoUi(Context ctx) {
+        try {
+            return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+        } catch (Resources.NotFoundException e) {
+            return "";
+        }
+    }
+
+    private static String[] getResourceStringArray(Context ctx, int resId) {
+        try {
+            final String[] strArray = ctx.getResources().getStringArray(resId);
+            return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
+        } catch (Resources.NotFoundException e404) {
+            return EMPTY_STRING_ARRAY;
+        }
+    }
+
     private static String[] copy(String[] strarray) {
         return Arrays.copyOf(strarray, strarray.length);
     }
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index bfd34ac..f403953 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -222,6 +222,9 @@
     }
 
     public int getAutomaticScreenBrightness() {
+        if (!mAmbientLuxValid) {
+            return -1;
+        }
         if (mDisplayPolicy == DisplayPowerRequest.POLICY_DOZE) {
             return (int) (mScreenAutoBrightness * mDozeScaleFactor);
         }
@@ -346,6 +349,7 @@
         pw.println("  mLightSensorEnabled=" + mLightSensorEnabled);
         pw.println("  mLightSensorEnableTime=" + TimeUtils.formatUptime(mLightSensorEnableTime));
         pw.println("  mAmbientLux=" + mAmbientLux);
+        pw.println("  mAmbientLuxValid=" + mAmbientLuxValid);
         pw.println("  mAmbientLightHorizon=" + mAmbientLightHorizon);
         pw.println("  mBrighteningLuxThreshold=" + mBrighteningLuxThreshold);
         pw.println("  mDarkeningLuxThreshold=" + mDarkeningLuxThreshold);
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index f74daf2..f7439b9 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -42,7 +42,7 @@
  */
 public abstract class BrightnessMappingStrategy {
     private static final String TAG = "BrightnessMappingStrategy";
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     private static final float LUX_GRAD_SMOOTHING = 0.25f;
     private static final float MAX_GRAD = 1.0f;
@@ -352,9 +352,9 @@
             // current^gamma = desired => gamma = log[current](desired)
             gamma = MathUtils.log(desiredBrightness) / MathUtils.log(currentBrightness);
             // max^-adjustment = gamma => adjustment = -log[max](gamma)
-            adjustment = -MathUtils.constrain(
-                    MathUtils.log(gamma) / MathUtils.log(maxGamma), -1, 1);
+            adjustment = -MathUtils.log(gamma) / MathUtils.log(maxGamma);
         }
+        adjustment = MathUtils.constrain(adjustment, -1, +1);
         if (DEBUG) {
             Slog.d(TAG, "inferAutoBrightnessAdjustment: " + maxGamma + "^" + -adjustment + "=" +
                     MathUtils.pow(maxGamma, -adjustment) + " == " + gamma);
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a6ee507..ae84782 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -36,11 +36,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.res.Resources;
+import android.content.res.TypedArray;
 import android.graphics.Point;
 import android.hardware.SensorManager;
 import android.hardware.display.AmbientBrightnessDayStats;
 import android.hardware.display.BrightnessChangeEvent;
 import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.Curve;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayViewport;
@@ -72,8 +74,10 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.IntArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.Spline;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -276,6 +280,11 @@
 
     private final Injector mInjector;
 
+    // The minimum brightness curve, which guarantess that any brightness curve that dips below it
+    // is rejected by the system.
+    private final Curve mMinimumBrightnessCurve;
+    private final Spline mMinimumBrightnessSpline;
+
     public DisplayManagerService(Context context) {
         this(context, new Injector());
     }
@@ -289,8 +298,15 @@
         mUiHandler = UiThread.getHandler();
         mDisplayAdapterListener = new DisplayAdapterListener();
         mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false);
+        Resources resources = mContext.getResources();
         mDefaultDisplayDefaultColorMode = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_defaultDisplayDefaultColorMode);
+        float[] lux = getFloatArray(resources.obtainTypedArray(
+                com.android.internal.R.array.config_minimumBrightnessCurveLux));
+        float[] nits = getFloatArray(resources.obtainTypedArray(
+                com.android.internal.R.array.config_minimumBrightnessCurveNits));
+        mMinimumBrightnessCurve = new Curve(lux, nits);
+        mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
 
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         mGlobalDisplayBrightness = pm.getDefaultScreenBrightnessSetting();
@@ -1032,9 +1048,15 @@
         }
     }
 
+    @VisibleForTesting
+    Curve getMinimumBrightnessCurveInternal() {
+        return mMinimumBrightnessCurve;
+    }
+
     private void setBrightnessConfigurationForUserInternal(
             @Nullable BrightnessConfiguration c, @UserIdInt int userId,
             @Nullable String packageName) {
+        validateBrightnessConfiguration(c);
         final int userSerial = getUserManager().getUserSerialNumber(userId);
         synchronized (mSyncRoot) {
             try {
@@ -1049,6 +1071,28 @@
         }
     }
 
+    @VisibleForTesting
+    void validateBrightnessConfiguration(BrightnessConfiguration config) {
+        if (config == null) {
+            return;
+        }
+        if (isBrightnessConfigurationTooDark(config)) {
+            throw new IllegalArgumentException("brightness curve is too dark");
+        }
+    }
+
+    private boolean isBrightnessConfigurationTooDark(BrightnessConfiguration config) {
+        Pair<float[], float[]> curve = config.getCurve();
+        float[] lux = curve.first;
+        float[] nits = curve.second;
+        for (int i = 0; i < lux.length; i++) {
+            if (nits[i] < mMinimumBrightnessSpline.interpolate(lux[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void loadBrightnessConfiguration() {
         synchronized (mSyncRoot) {
             final int userSerial = getUserManager().getUserSerialNumber(mCurrentUserId);
@@ -1369,6 +1413,16 @@
         }
     }
 
+    private static float[] getFloatArray(TypedArray array) {
+        int length = array.length();
+        float[] floatArray = new float[length];
+        for (int i = 0; i < length; i++) {
+            floatArray[i] = array.getFloat(i, Float.NaN);
+        }
+        array.recycle();
+        return floatArray;
+    }
+
     /**
      * This is the object that everything in the display manager locks on.
      * We make it an inner class within the {@link DisplayManagerService} to so that it is
@@ -1999,6 +2053,16 @@
             }
         }
 
+        @Override // Binder call
+        public Curve getMinimumBrightnessCurve() {
+            final long token = Binder.clearCallingIdentity();
+            try {
+                return getMinimumBrightnessCurveInternal();
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         void setBrightness(int brightness) {
             Settings.System.putIntForUser(mContext.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS, brightness, UserHandle.USER_CURRENT);
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 88b2a36..0700ab3 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -534,17 +534,33 @@
 
     private void destroyWeaverSlot(long handle, int userId) {
         int slot = loadWeaverSlot(handle, userId);
+        destroyState(WEAVER_SLOT_NAME, handle, userId);
         if (slot != INVALID_WEAVER_SLOT) {
-            try {
-                weaverEnroll(slot, null, null);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to destroy slot", e);
+            Set<Integer> usedSlots = getUsedWeaverSlots();
+            if (!usedSlots.contains(slot)) {
+                Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId);
+                try {
+                    weaverEnroll(slot, null, null);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Failed to destroy slot", e);
+                }
+            } else {
+                Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId);
             }
         }
-        destroyState(WEAVER_SLOT_NAME, handle, userId);
     }
 
-    private int getNextAvailableWeaverSlot() {
+    /**
+     * Return the set of weaver slots that are currently in use by all users on the device.
+     * <p>
+     * <em>Note:</em> Users who are in the process of being deleted are not tracked here
+     * (due to them being marked as partial in UserManager so not visible from
+     * {@link UserManager#getUsers}). As a result their weaver slots will not be considered
+     * taken and can be reused by new users. Care should be taken when cleaning up the
+     * deleted user in {@link #removeUser}, to prevent a reused slot from being erased
+     * unintentionally.
+     */
+    private Set<Integer> getUsedWeaverSlots() {
         Map<Integer, List<Long>> slotHandles = mStorage.listSyntheticPasswordHandlesForAllUsers(
                 WEAVER_SLOT_NAME);
         HashSet<Integer> slots = new HashSet<>();
@@ -554,8 +570,13 @@
                 slots.add(slot);
             }
         }
+        return slots;
+    }
+
+    private int getNextAvailableWeaverSlot() {
+        Set<Integer> usedSlots = getUsedWeaverSlots();
         for (int i = 0; i < mWeaverConfig.slots; i++) {
-            if (!slots.contains(i)) {
+            if (!usedSlots.contains(i)) {
                 return i;
             }
         }
@@ -592,6 +613,7 @@
         if (isWeaverAvailable()) {
             // Weaver based user password
             int weaverSlot = getNextAvailableWeaverSlot();
+            Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId);
             byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null);
             if (weaverSecret == null) {
                 Log.e(TAG, "Fail to enroll user password under weaver " + userId);
@@ -749,6 +771,7 @@
         if (isWeaverAvailable()) {
             int slot = getNextAvailableWeaverSlot();
             try {
+                Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId);
                 weaverEnroll(slot, null, tokenData.weaverSecret);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to enroll weaver secret when activating token", e);
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 1782ae5..8e77373 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -39,6 +39,7 @@
 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
@@ -195,6 +196,7 @@
 import android.util.DataUnit;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -503,6 +505,10 @@
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseBooleanArray mNetworkMetered = new SparseBooleanArray();
 
+    /** Map from network ID to last observed roaming state */
+    @GuardedBy("mNetworkPoliciesSecondLock")
+    private final SparseBooleanArray mNetworkRoaming = new SparseBooleanArray();
+
     /** Map from netId to subId as of last update */
     @GuardedBy("mNetworkPoliciesSecondLock")
     private final SparseIntArray mNetIdToSubId = new SparseIntArray();
@@ -1018,6 +1024,16 @@
         }
     };
 
+    private static boolean updateCapabilityChange(SparseBooleanArray lastValues, boolean newValue,
+            Network network) {
+        final boolean lastValue = lastValues.get(network.netId, false);
+        final boolean changed = (lastValue != newValue) || lastValues.indexOfKey(network.netId) < 0;
+        if (changed) {
+            lastValues.put(network.netId, newValue);
+        }
+        return changed;
+    }
+
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
         public void onCapabilitiesChanged(Network network,
@@ -1025,13 +1041,18 @@
             if (network == null || networkCapabilities == null) return;
 
             synchronized (mNetworkPoliciesSecondLock) {
-                final boolean oldMetered = mNetworkMetered.get(network.netId, false);
                 final boolean newMetered = !networkCapabilities
                         .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+                final boolean meteredChanged = updateCapabilityChange(
+                        mNetworkMetered, newMetered, network);
 
-                if ((oldMetered != newMetered) || mNetworkMetered.indexOfKey(network.netId) < 0) {
+                final boolean newRoaming = !networkCapabilities
+                        .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+                final boolean roamingChanged = updateCapabilityChange(
+                        mNetworkRoaming, newRoaming, network);
+
+                if (meteredChanged || roamingChanged) {
                     mLogger.meterednessChanged(network.netId, newMetered);
-                    mNetworkMetered.put(network.netId, newMetered);
                     updateNetworkRulesNL();
                 }
             }
@@ -1770,18 +1791,21 @@
 
             final long quotaBytes;
             final long limitBytes = plan.getDataLimitBytes();
-            if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) {
+            if (!state.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING)) {
+                // Clamp to 0 when roaming
+                quotaBytes = 0;
+            } else if (limitBytes == SubscriptionPlan.BYTES_UNKNOWN) {
                 quotaBytes = OPPORTUNISTIC_QUOTA_UNKNOWN;
             } else if (limitBytes == SubscriptionPlan.BYTES_UNLIMITED) {
                 // Unlimited data; let's use 20MiB/day (600MiB/month)
                 quotaBytes = quotaUnlimited;
             } else {
                 // Limited data; let's only use 10% of remaining budget
-                final Pair<ZonedDateTime, ZonedDateTime> cycle = plan.cycleIterator().next();
-                final long start = cycle.first.toInstant().toEpochMilli();
-                final long end = cycle.second.toInstant().toEpochMilli();
+                final Range<ZonedDateTime> cycle = plan.cycleIterator().next();
+                final long start = cycle.getLower().toInstant().toEpochMilli();
+                final long end = cycle.getUpper().toInstant().toEpochMilli();
                 final Instant now = mClock.instant();
-                final long startOfDay = ZonedDateTime.ofInstant(now, cycle.first.getZone())
+                final long startOfDay = ZonedDateTime.ofInstant(now, cycle.getLower().getZone())
                         .truncatedTo(ChronoUnit.DAYS)
                         .toInstant().toEpochMilli();
                 final long totalBytes = getTotalBytes(
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 2ef754e..ab52523 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -47,7 +47,7 @@
 import android.util.AtomicFile;
 import android.util.IntArray;
 import android.util.MathUtils;
-import android.util.Pair;
+import android.util.Range;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
@@ -266,11 +266,11 @@
         long collectEnd = end;
 
         if (augmentEnd != SubscriptionPlan.TIME_UNKNOWN) {
-            final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = augmentPlan.cycleIterator();
+            final Iterator<Range<ZonedDateTime>> it = augmentPlan.cycleIterator();
             while (it.hasNext()) {
-                final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
-                final long cycleStart = cycle.first.toInstant().toEpochMilli();
-                final long cycleEnd = cycle.second.toInstant().toEpochMilli();
+                final Range<ZonedDateTime> cycle = it.next();
+                final long cycleStart = cycle.getLower().toInstant().toEpochMilli();
+                final long cycleEnd = cycle.getUpper().toInstant().toEpochMilli();
                 if (cycleStart <= augmentEnd && augmentEnd < cycleEnd) {
                     augmentStart = cycleStart;
                     collectStart = Long.min(collectStart, augmentStart);
diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java
index 741c206..d840873 100644
--- a/services/core/java/com/android/server/net/NetworkStatsObservers.java
+++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java
@@ -16,7 +16,7 @@
 
 package com.android.server.net;
 
-import static android.net.TrafficStats.MB_IN_BYTES;
+import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
 
 import static com.android.internal.util.Preconditions.checkArgument;
 
@@ -52,8 +52,6 @@
     private static final String TAG = "NetworkStatsObservers";
     private static final boolean LOGV = false;
 
-    private static final long MIN_THRESHOLD_BYTES = 2 * MB_IN_BYTES;
-
     private static final int MSG_REGISTER = 1;
     private static final int MSG_UNREGISTER = 2;
     private static final int MSG_UPDATE_STATS = 3;
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
index 17c5868..766d8ca 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
@@ -91,7 +91,7 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             // Reset last report time
-            if (!WatchlistConfig.getInstance().isConfigSecure()) {
+            if (WatchlistConfig.getInstance().isConfigSecure()) {
                 pw.println("Error: Cannot force generate report under production config");
                 return -1;
             }
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 36bc096..b61a27a 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -40,4 +40,6 @@
     void onNotificationExpansionChanged(String key, boolean userAction, boolean expanded);
     void onNotificationDirectReplied(String key);
     void onNotificationSettingsViewed(String key);
+    void onNotificationSmartRepliesAdded(String key, int replyCount);
+    void onNotificationSmartReplySent(String key, int replyIndex);
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7813e70..7254acf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -121,6 +121,7 @@
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
 import android.media.IRingtonePlayer;
+import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
@@ -395,6 +396,8 @@
     private GroupHelper mGroupHelper;
     private boolean mIsTelevision;
 
+    private MetricsLogger mMetricsLogger;
+
     private static class Archive {
         final int mBufferSize;
         final ArrayDeque<StatusBarNotification> mBuffer;
@@ -801,6 +804,18 @@
                         // Report to usage stats that notification was made visible
                         if (DBG) Slog.d(TAG, "Marking notification as visible " + nv.key);
                         reportSeen(r);
+
+                        // If the newly visible notification has smart replies
+                        // then log that the user has seen them.
+                        if (r.getNumSmartRepliesAdded() > 0
+                                && !r.hasSeenSmartReplies()) {
+                            r.setSeenSmartReplies(true);
+                            LogMaker logMaker = r.getLogMaker()
+                                    .setCategory(MetricsEvent.SMART_REPLY_VISIBLE)
+                                    .addTaggedData(MetricsEvent.NOTIFICATION_SMART_REPLY_COUNT,
+                                            r.getNumSmartRepliesAdded());
+                            mMetricsLogger.write(logMaker);
+                        }
                     }
                     r.setVisibility(true, nv.rank);
                     nv.recycle();
@@ -855,6 +870,31 @@
         }
 
         @Override
+        public void onNotificationSmartRepliesAdded(String key, int replyCount) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    r.setNumSmartRepliesAdded(replyCount);
+                }
+            }
+        }
+
+        @Override
+        public void onNotificationSmartReplySent(String key, int replyIndex) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    LogMaker logMaker = r.getLogMaker()
+                            .setCategory(MetricsEvent.SMART_REPLY_ACTION)
+                            .setSubtype(replyIndex);
+                    mMetricsLogger.write(logMaker);
+                    // Treat clicking on a smart reply as a user interaction.
+                    reportUserInteraction(r);
+                }
+            }
+        }
+
+        @Override
         public void onNotificationSettingsViewed(String key) {
             synchronized (mNotificationLock) {
                 NotificationRecord r = mNotificationsByKey.get(key);
@@ -1349,6 +1389,7 @@
             extractorNames = new String[0];
         }
         mUsageStats = usageStats;
+        mMetricsLogger = new MetricsLogger();
         mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
         mConditionProviders = conditionProviders;
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
@@ -2066,7 +2107,7 @@
 
         @Override
         public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
-            checkCallerIsSystem();
+            enforceSystemOrSystemUI("setNotificationsEnabledForPackage");
 
             mRankingHelper.setEnabled(pkg, uid, enabled);
             // Now, cancel any outstanding notifications that are part of a just-disabled app
@@ -3956,9 +3997,14 @@
                     + ", notificationUid=" + notificationUid
                     + ", notification=" + notification;
             Log.e(TAG, noChannelStr);
-            doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
-                    "Failed to post notification on channel \"" + channelId + "\"\n" +
-                    "See log for more details");
+            boolean appNotificationsOff = mRankingHelper.getImportance(pkg, notificationUid)
+                    == NotificationManager.IMPORTANCE_NONE;
+
+            if (!appNotificationsOff) {
+                doChannelWarningToast("Developer warning for package \"" + pkg + "\"\n" +
+                        "Failed to post notification on channel \"" + channelId + "\"\n" +
+                        "See log for more details");
+            }
             return;
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index c887085..9bd3e52 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -149,6 +149,8 @@
     private final NotificationStats mStats;
     private int mUserSentiment;
     private boolean mIsInterruptive;
+    private int mNumberOfSmartRepliesAdded;
+    private boolean mHasSeenSmartReplies;
 
     @VisibleForTesting
     public NotificationRecord(Context context, StatusBarNotification sbn,
@@ -962,6 +964,22 @@
         mStats.setViewedSettings();
     }
 
+    public void setNumSmartRepliesAdded(int noReplies) {
+        mNumberOfSmartRepliesAdded = noReplies;
+    }
+
+    public int getNumSmartRepliesAdded() {
+        return mNumberOfSmartRepliesAdded;
+    }
+
+    public boolean hasSeenSmartReplies() {
+        return mHasSeenSmartReplies;
+    }
+
+    public void setSeenSmartReplies(boolean hasSeenSmartReplies) {
+        mHasSeenSmartReplies = hasSeenSmartReplies;
+    }
+
     public Set<Uri> getNotificationUris() {
         Notification notification = getNotification();
         Set<Uri> uris = new ArraySet<>();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 586abc1..156f702 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -32,6 +32,7 @@
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
+import android.graphics.drawable.Icon;
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioManagerInternal;
@@ -1198,8 +1199,6 @@
 
     @VisibleForTesting
     protected Notification createZenUpgradeNotification() {
-        Intent intent = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS)
-                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         final Bundle extras = new Bundle();
         extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME,
                 mContext.getResources().getString(R.string.global_action_settings));
@@ -1210,15 +1209,20 @@
             title = R.string.zen_upgrade_notification_visd_title;
             content = R.string.zen_upgrade_notification_visd_content;
         }
+        Intent onboardingIntent = new Intent(Settings.ZEN_MODE_ONBOARDING);
+        onboardingIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
         return new Notification.Builder(mContext, SystemNotificationChannels.DO_NOT_DISTURB)
+                .setAutoCancel(true)
                 .setSmallIcon(R.drawable.ic_settings_24dp)
+                .setLargeIcon(Icon.createWithResource(mContext, R.drawable.ic_zen_24dp))
                 .setContentTitle(mContext.getResources().getString(title))
                 .setContentText(mContext.getResources().getString(content))
+                .setContentIntent(PendingIntent.getActivity(mContext, 0, onboardingIntent,
+                        PendingIntent.FLAG_UPDATE_CURRENT))
                 .setAutoCancel(true)
                 .setLocalOnly(true)
                 .addExtras(extras)
                 .setStyle(new Notification.BigTextStyle())
-                .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0, null))
                 .build();
     }
 
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index bc9fa4b..dbf0940 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -256,8 +256,6 @@
         int flags = origIntent.getFlags();
         final Intent intent = new Intent();
         intent.setFlags(flags
-                | Intent.FLAG_ACTIVITY_NEW_TASK
-                | Intent.FLAG_ACTIVITY_CLEAR_TASK
                 | Intent.FLAG_ACTIVITY_NO_HISTORY
                 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
         if (token != null) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e4849b..bfce33d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1523,7 +1523,6 @@
                                     Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
                                             params.traceMethod, params.traceCookie);
                                 }
-                                return;
                             }
                             mPendingInstalls.clear();
                         } else {
@@ -10198,20 +10197,10 @@
                 // The signature has changed, but this package is in the system
                 // image...  let's recover!
                 pkgSetting.signatures.mSigningDetails = pkg.mSigningDetails;
-                // However...  if this package is part of a shared user, but it
-                // doesn't match the signature of the shared user, let's fail.
-                // What this means is that you can't change the signatures
-                // associated with an overall shared user, which doesn't seem all
-                // that unreasonable.
+                // If the system app is part of a shared user we allow that shared user to change
+                // signatures as well in part as part of an OTA.
                 if (signatureCheckPs.sharedUser != null) {
-                    if (compareSignatures(
-                            signatureCheckPs.sharedUser.signatures.mSigningDetails.signatures,
-                            pkg.mSigningDetails.signatures) != PackageManager.SIGNATURE_MATCH) {
-                        throw new PackageManagerException(
-                                INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                                "Signature mismatch for shared user: "
-                                        + pkgSetting.sharedUser);
-                    }
+                    signatureCheckPs.sharedUser.signatures.mSigningDetails = pkg.mSigningDetails;
                 }
                 // File a report about this.
                 String msg = "System package " + pkg.packageName
@@ -14037,7 +14026,7 @@
                 "setPackagesSuspended for user " + userId);
         if (callingUid != Process.ROOT_UID &&
                 !UserHandle.isSameApp(getPackageUid(callingPackage, 0, userId), callingUid)) {
-            throw new IllegalArgumentException("callingPackage " + callingPackage + " does not"
+            throw new IllegalArgumentException("CallingPackage " + callingPackage + " does not"
                     + " belong to calling app id " + UserHandle.getAppId(callingUid));
         }
 
@@ -14061,20 +14050,18 @@
                     final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
                     if (pkgSetting == null
                             || filterAppAccessLPr(pkgSetting, callingUid, userId)) {
-                        Slog.w(TAG, "Could not find package setting for package \"" + packageName
-                                + "\". Skipping suspending/un-suspending.");
+                        Slog.w(TAG, "Could not find package setting for package: " + packageName
+                                + ". Skipping suspending/un-suspending.");
                         unactionedPackages.add(packageName);
                         continue;
                     }
-                    if (pkgSetting.getSuspended(userId) != suspended) {
-                        if (!canSuspendPackageForUserLocked(packageName, userId)) {
-                            unactionedPackages.add(packageName);
-                            continue;
-                        }
-                        pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
-                                launcherExtras, userId);
-                        changedPackagesList.add(packageName);
+                    if (!canSuspendPackageForUserLocked(packageName, userId)) {
+                        unactionedPackages.add(packageName);
+                        continue;
                     }
+                    pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
+                            launcherExtras, userId);
+                    changedPackagesList.add(packageName);
                 }
             }
         } finally {
@@ -14089,7 +14076,6 @@
                 scheduleWritePackageRestrictionsLocked(userId);
             }
         }
-
         return unactionedPackages.toArray(new String[unactionedPackages.size()]);
     }
 
@@ -14097,7 +14083,8 @@
     public PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
         if (getPackageUid(packageName, 0, userId) != callingUid) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
+            throw new SecurityException("Calling package " + packageName
+                    + " does not belong to calling uid " + callingUid);
         }
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
@@ -14112,25 +14099,6 @@
         }
     }
 
-    @Override
-    public void setSuspendedPackageAppExtras(String packageName, PersistableBundle appExtras,
-            int userId) {
-        final int callingUid = Binder.getCallingUid();
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
-        synchronized (mPackages) {
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
-                throw new IllegalArgumentException("Unknown target package: " + packageName);
-            }
-            final PackageUserState packageUserState = ps.readUserState(userId);
-            if (packageUserState.suspended) {
-                packageUserState.suspendedAppExtras = appExtras;
-                sendMyPackageSuspendedOrUnsuspended(new String[] {packageName}, true, appExtras,
-                        userId);
-            }
-        }
-    }
-
     private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
             PersistableBundle appExtras, int userId) {
         final String action;
@@ -14173,9 +14141,6 @@
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, false /* checkShell */,
                 "isPackageSuspendedForUser for user " + userId);
-        if (getPackageUid(packageName, 0, userId) != callingUid) {
-            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, null);
-        }
         synchronized (mPackages) {
             final PackageSetting ps = mSettings.mPackages.get(packageName);
             if (ps == null || filterAppAccessLPr(ps, callingUid, userId)) {
@@ -14185,18 +14150,26 @@
         }
     }
 
-    void onSuspendingPackageRemoved(String packageName, int userId) {
-        final int[] userIds = (userId == UserHandle.USER_ALL) ? sUserManager.getUserIds()
-                : new int[] {userId};
-        synchronized (mPackages) {
-            for (PackageSetting ps : mSettings.mPackages.values()) {
-                for (int user : userIds) {
-                    final PackageUserState pus = ps.readUserState(user);
+    void onSuspendingPackageRemoved(String packageName, int removedForUser) {
+        final int[] userIds = (removedForUser == UserHandle.USER_ALL) ? sUserManager.getUserIds()
+                : new int[] {removedForUser};
+        for (int userId : userIds) {
+            List<String> affectedPackages = new ArrayList<>();
+            synchronized (mPackages) {
+                for (PackageSetting ps : mSettings.mPackages.values()) {
+                    final PackageUserState pus = ps.readUserState(userId);
                     if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
-                        ps.setSuspended(false, null, null, null, null, user);
+                        ps.setSuspended(false, null, null, null, null, userId);
+                        affectedPackages.add(ps.name);
                     }
                 }
             }
+            if (!affectedPackages.isEmpty()) {
+                final String[] packageArray = affectedPackages.toArray(
+                        new String[affectedPackages.size()]);
+                sendMyPackageSuspendedOrUnsuspended(packageArray, false, null, userId);
+                sendPackagesSuspendedForUser(packageArray, userId, false, null);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index fd4c5e9..138594c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -20,8 +20,6 @@
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
 
-import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
-
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.PackageManager;
diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java
index 407ceb7..b6b94f5 100644
--- a/services/core/java/com/android/server/pm/SharedUserSetting.java
+++ b/services/core/java/com/android/server/pm/SharedUserSetting.java
@@ -52,6 +52,7 @@
         uidFlags =  _pkgFlags;
         uidPrivateFlags = _pkgPrivateFlags;
         name = _name;
+        seInfoTargetSdkVersion = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index eeaa333..8c7871f 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -21,6 +21,7 @@
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -164,12 +165,13 @@
             ShortcutService s, String packageName, @UserIdInt int packageUserId) {
         final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
         // retrieve the newest sigs
-        Signature[][] signingHistory = pi.signingCertificateHistory;
-        if (signingHistory == null || signingHistory.length == 0) {
+        SigningInfo signingInfo = pi.signingInfo;
+        if (signingInfo == null) {
             Slog.e(TAG, "Can't get signatures: package=" + packageName);
             return null;
         }
-        Signature[] signatures = signingHistory[signingHistory.length - 1];
+        // TODO (b/73988180) use entire signing history in case of rollbacks
+        Signature[] signatures = signingInfo.getApkContentsSigners();
         final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.getLongVersionCode(),
                 pi.lastUpdateTime, BackupUtils.hashSignatureArray(signatures), /* shadow=*/ false);
 
@@ -192,13 +194,14 @@
             return;
         }
         // retrieve the newest sigs
-        Signature[][] signingHistory = pi.signingCertificateHistory;
-        if (signingHistory == null || signingHistory.length == 0) {
+        SigningInfo signingInfo = pi.signingInfo;
+        if (signingInfo == null) {
             Slog.w(TAG, "Not refreshing signature for " + pkg.getPackageName()
-                    + " since it appears to have no signature history.");
+                    + " since it appears to have no signing info.");
             return;
         }
-        Signature[] signatures = signingHistory[signingHistory.length - 1];
+        // TODO (b/73988180) use entire signing history in case of rollbacks
+        Signature[] signatures = signingInfo.getApkContentsSigners();
         mSigHashes = BackupUtils.hashSignatureArray(signatures);
     }
 
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 79f80e6..b1e9134 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -87,6 +87,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -5411,6 +5412,12 @@
         final boolean attachedInParent = attached != null && !layoutInScreen;
         final boolean requestedHideNavigation =
                 (requestedSysUiFl & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
+
+        // TYPE_BASE_APPLICATION windows are never considered floating here because they don't get
+        // cropped / shifted to the displayFrame in WindowState.
+        final boolean floatingInScreenWindow = !attrs.isFullscreen() && layoutInScreen
+                && type != TYPE_BASE_APPLICATION;
+
         // Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
         // the cutout safe zone.
         if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
@@ -5445,7 +5452,10 @@
             }
             // Windows that are attached to a parent and laid out in said parent already avoid
             // the cutout according to that parent and don't need to be further constrained.
-            if (!attachedInParent) {
+            // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
+            // They will later be cropped or shifted using the displayFrame in WindowState,
+            // which prevents overlap with the DisplayCutout.
+            if (!attachedInParent && !floatingInScreenWindow) {
                 mTmpRect.set(pf);
                 pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
                 parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
diff --git a/services/core/java/com/android/server/slice/DirtyTracker.java b/services/core/java/com/android/server/slice/DirtyTracker.java
new file mode 100644
index 0000000..4288edc
--- /dev/null
+++ b/services/core/java/com/android/server/slice/DirtyTracker.java
@@ -0,0 +1,35 @@
+/*
+ * 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.slice;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+
+/**
+ * A parent object that cares when a Persistable changes and will schedule a serialization
+ * in response to the onPersistableDirty callback.
+ */
+public interface DirtyTracker {
+    void onPersistableDirty(Persistable obj);
+
+    /**
+     * An object that can be written to XML.
+     */
+    interface Persistable {
+        String getFileName();
+        void writeTo(XmlSerializer out) throws IOException;
+    }
+}
diff --git a/services/core/java/com/android/server/slice/SliceClientPermissions.java b/services/core/java/com/android/server/slice/SliceClientPermissions.java
new file mode 100644
index 0000000..e461e0d
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceClientPermissions.java
@@ -0,0 +1,354 @@
+/*
+ * 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.slice;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.slice.DirtyTracker.Persistable;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class SliceClientPermissions implements DirtyTracker, Persistable {
+
+    private static final String TAG = "SliceClientPermissions";
+
+    static final String TAG_CLIENT = "client";
+    private static final String TAG_AUTHORITY = "authority";
+    private static final String TAG_PATH = "path";
+    private static final String NAMESPACE = null;
+
+    private static final String ATTR_PKG = "pkg";
+    private static final String ATTR_AUTHORITY = "authority";
+    private static final String ATTR_FULL_ACCESS = "fullAccess";
+
+    private final PkgUser mPkg;
+    // Keyed off (authority, userId) rather than the standard (pkg, userId)
+    private final ArrayMap<PkgUser, SliceAuthority> mAuths = new ArrayMap<>();
+    private final DirtyTracker mTracker;
+    private boolean mHasFullAccess;
+
+    public SliceClientPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
+        mPkg = pkg;
+        mTracker = tracker;
+    }
+
+    public PkgUser getPkg() {
+        return mPkg;
+    }
+
+    public synchronized Collection<SliceAuthority> getAuthorities() {
+        return new ArrayList<>(mAuths.values());
+    }
+
+    public synchronized SliceAuthority getOrCreateAuthority(PkgUser authority, PkgUser provider) {
+        SliceAuthority ret = mAuths.get(authority);
+        if (ret == null) {
+            ret = new SliceAuthority(authority.getPkg(), provider, this);
+            mAuths.put(authority, ret);
+            onPersistableDirty(ret);
+        }
+        return ret;
+    }
+
+    public synchronized SliceAuthority getAuthority(PkgUser authority) {
+        return mAuths.get(authority);
+    }
+
+    public boolean hasFullAccess() {
+        return mHasFullAccess;
+    }
+
+    public void setHasFullAccess(boolean hasFullAccess) {
+        if (mHasFullAccess == hasFullAccess) return;
+        mHasFullAccess = hasFullAccess;
+        mTracker.onPersistableDirty(this);
+    }
+
+    public void removeAuthority(String authority, int userId) {
+        if (mAuths.remove(new PkgUser(authority, userId)) != null) {
+            mTracker.onPersistableDirty(this);
+        }
+    }
+
+    public synchronized boolean hasPermission(Uri uri, int userId) {
+        if (!Objects.equals(ContentResolver.SCHEME_CONTENT, uri.getScheme())) return false;
+        SliceAuthority authority = getAuthority(new PkgUser(uri.getAuthority(), userId));
+        return authority != null && authority.hasPermission(uri.getPathSegments());
+    }
+
+    public void grantUri(Uri uri, PkgUser providerPkg) {
+        SliceAuthority authority = getOrCreateAuthority(
+                new PkgUser(uri.getAuthority(), providerPkg.getUserId()),
+                providerPkg);
+        authority.addPath(uri.getPathSegments());
+    }
+
+    public void revokeUri(Uri uri, PkgUser providerPkg) {
+        SliceAuthority authority = getOrCreateAuthority(
+                new PkgUser(uri.getAuthority(), providerPkg.getUserId()),
+                providerPkg);
+        authority.removePath(uri.getPathSegments());
+    }
+
+    public void clear() {
+        if (!mHasFullAccess && mAuths.isEmpty()) return;
+        mHasFullAccess = false;
+        mAuths.clear();
+        onPersistableDirty(this);
+    }
+
+    @Override
+    public void onPersistableDirty(Persistable obj) {
+        mTracker.onPersistableDirty(this);
+    }
+
+    @Override
+    public String getFileName() {
+        return getFileName(mPkg);
+    }
+
+    public synchronized void writeTo(XmlSerializer out) throws IOException {
+        out.startTag(NAMESPACE, TAG_CLIENT);
+        out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString());
+        out.attribute(NAMESPACE, ATTR_FULL_ACCESS, mHasFullAccess ? "1" : "0");
+
+        final int N = mAuths.size();
+        for (int i = 0; i < N; i++) {
+            out.startTag(NAMESPACE, TAG_AUTHORITY);
+            out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority);
+            out.attribute(NAMESPACE, ATTR_PKG, mAuths.valueAt(i).mPkg.toString());
+
+            mAuths.valueAt(i).writeTo(out);
+
+            out.endTag(NAMESPACE, TAG_AUTHORITY);
+        }
+
+        out.endTag(NAMESPACE, TAG_CLIENT);
+    }
+
+    public static SliceClientPermissions createFrom(XmlPullParser parser, DirtyTracker tracker)
+            throws XmlPullParserException, IOException {
+        // Get to the beginning of the provider.
+        while (parser.getEventType() != XmlPullParser.START_TAG
+                || !TAG_CLIENT.equals(parser.getName())) {
+            parser.next();
+        }
+        int depth = parser.getDepth();
+        PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+        SliceClientPermissions provider = new SliceClientPermissions(pkgUser, tracker);
+        String fullAccess = parser.getAttributeValue(NAMESPACE, ATTR_FULL_ACCESS);
+        if (fullAccess == null) {
+            fullAccess = "0";
+        }
+        provider.mHasFullAccess = Integer.parseInt(fullAccess) != 0;
+        parser.next();
+
+        while (parser.getDepth() > depth) {
+            if (parser.getEventType() == XmlPullParser.START_TAG
+                    && TAG_AUTHORITY.equals(parser.getName())) {
+                try {
+                    PkgUser pkg = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+                    SliceAuthority authority = new SliceAuthority(
+                            parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), pkg, provider);
+                    authority.readFrom(parser);
+                    provider.mAuths.put(new PkgUser(authority.getAuthority(), pkg.getUserId()),
+                            authority);
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Couldn't read PkgUser", e);
+                }
+            }
+
+            parser.next();
+        }
+        return provider;
+    }
+
+    public static String getFileName(PkgUser pkg) {
+        return String.format("client_%s", pkg.toString());
+    }
+
+    public static class SliceAuthority implements Persistable {
+        public static final String DELIMITER = "/";
+        private final String mAuthority;
+        private final DirtyTracker mTracker;
+        private final PkgUser mPkg;
+        private final ArraySet<String[]> mPaths = new ArraySet<>();
+
+        public SliceAuthority(String authority, PkgUser pkg, DirtyTracker tracker) {
+            mAuthority = authority;
+            mPkg = pkg;
+            mTracker = tracker;
+        }
+
+        public String getAuthority() {
+            return mAuthority;
+        }
+
+        public PkgUser getPkg() {
+            return mPkg;
+        }
+
+        void addPath(List<String> path) {
+            String[] pathSegs = path.toArray(new String[path.size()]);
+            for (int i = mPaths.size() - 1; i >= 0; i--) {
+                String[] existing = mPaths.valueAt(i);
+                if (isPathPrefixMatch(existing, pathSegs)) {
+                    // Nothing to add here.
+                    return;
+                }
+                if (isPathPrefixMatch(pathSegs, existing)) {
+                    mPaths.removeAt(i);
+                }
+            }
+            mPaths.add(pathSegs);
+            mTracker.onPersistableDirty(this);
+        }
+
+        void removePath(List<String> path) {
+            boolean changed = false;
+            String[] pathSegs = path.toArray(new String[path.size()]);
+            for (int i = mPaths.size() - 1; i >= 0; i--) {
+                String[] existing = mPaths.valueAt(i);
+                if (isPathPrefixMatch(pathSegs, existing)) {
+                    changed = true;
+                    mPaths.removeAt(i);
+                }
+            }
+            if (changed) {
+                mTracker.onPersistableDirty(this);
+            }
+        }
+
+        public synchronized Collection<String[]> getPaths() {
+            return new ArraySet<>(mPaths);
+        }
+
+        public boolean hasPermission(List<String> path) {
+            for (String[] p : mPaths) {
+                if (isPathPrefixMatch(p, path.toArray(new String[path.size()]))) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        private boolean isPathPrefixMatch(String[] prefix, String[] path) {
+            final int prefixSize = prefix.length;
+            if (path.length < prefixSize) return false;
+
+            for (int i = 0; i < prefixSize; i++) {
+                if (!Objects.equals(path[i], prefix[i])) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public String getFileName() {
+            return null;
+        }
+
+        public synchronized void writeTo(XmlSerializer out) throws IOException {
+            final int N = mPaths.size();
+            for (int i = 0; i < N; i++) {
+                out.startTag(NAMESPACE, TAG_PATH);
+                out.text(encodeSegments(mPaths.valueAt(i)));
+                out.endTag(NAMESPACE, TAG_PATH);
+            }
+        }
+
+        public synchronized void readFrom(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            parser.next();
+            int depth = parser.getDepth();
+            while (parser.getDepth() >= depth) {
+                if (parser.getEventType() == XmlPullParser.START_TAG
+                        && TAG_PATH.equals(parser.getName())) {
+                    mPaths.add(decodeSegments(parser.nextText()));
+                }
+                parser.next();
+            }
+        }
+
+        private String encodeSegments(String[] s) {
+            String[] out = new String[s.length];
+            for (int i = 0; i < s.length; i++) {
+                out[i] = Uri.encode(s[i]);
+            }
+            return TextUtils.join(DELIMITER, out);
+        }
+
+        private String[] decodeSegments(String s) {
+            String[] sets = s.split(DELIMITER, -1);
+            for (int i = 0; i < sets.length; i++) {
+                sets[i] = Uri.decode(sets[i]);
+            }
+            return sets;
+        }
+
+        /**
+         * Only for testing, no deep equality of these are done normally.
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+            SliceAuthority other = (SliceAuthority) obj;
+            if (mPaths.size() != other.mPaths.size()) return false;
+            ArrayList<String[]> p1 = new ArrayList<>(mPaths);
+            ArrayList<String[]> p2 = new ArrayList<>(other.mPaths);
+            p1.sort(Comparator.comparing(o -> TextUtils.join(",", o)));
+            p2.sort(Comparator.comparing(o -> TextUtils.join(",", o)));
+            for (int i = 0; i < p1.size(); i++) {
+                String[] a1 = p1.get(i);
+                String[] a2 = p2.get(i);
+                if (a1.length != a2.length) return false;
+                for (int j = 0; j < a1.length; j++) {
+                    if (!Objects.equals(a1[j], a2[j])) return false;
+                }
+            }
+            return Objects.equals(mAuthority, other.mAuthority)
+                    && Objects.equals(mPkg, other.mPkg);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s, %s: %s)", mAuthority, mPkg.toString(), pathToString(mPaths));
+        }
+
+        private String pathToString(ArraySet<String[]> paths) {
+            return TextUtils.join(", ", paths.stream().map(s -> TextUtils.join("/", s))
+                    .collect(Collectors.toList()));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index fd0b6f1..b7b9612 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -31,14 +31,15 @@
 import android.app.ContentProviderHolder;
 import android.app.IActivityManager;
 import android.app.slice.ISliceManager;
-import android.app.slice.SliceManager;
 import android.app.slice.SliceSpec;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
 import android.net.Uri;
@@ -51,7 +52,6 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
-import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml.Encoding;
@@ -72,7 +72,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -91,13 +90,9 @@
 
     @GuardedBy("mLock")
     private final ArrayMap<Uri, PinnedSliceState> mPinnedSlicesByUri = new ArrayMap<>();
-    @GuardedBy("mLock")
-    private final ArraySet<SliceGrant> mUserGrants = new ArraySet<>();
     private final Handler mHandler;
-    @GuardedBy("mSliceAccessFile")
-    private final AtomicFile mSliceAccessFile;
-    @GuardedBy("mAccessList")
-    private final SliceFullAccessList mAccessList;
+
+    private final SlicePermissionManager mPermissions;
     private final UsageStatsManagerInternal mAppUsageStats;
 
     public SliceManagerService(Context context) {
@@ -113,24 +108,9 @@
         mAssistUtils = new AssistUtils(context);
         mHandler = new Handler(looper);
 
-        final File systemDir = new File(Environment.getDataDirectory(), "system");
-        mSliceAccessFile = new AtomicFile(new File(systemDir, "slice_access.xml"));
-        mAccessList = new SliceFullAccessList(mContext);
         mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
 
-        synchronized (mSliceAccessFile) {
-            if (!mSliceAccessFile.exists()) return;
-            try {
-                InputStream input = mSliceAccessFile.openRead();
-                XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
-                parser.setInput(input, Encoding.UTF_8.name());
-                synchronized (mAccessList) {
-                    mAccessList.readXml(parser);
-                }
-            } catch (IOException | XmlPullParserException e) {
-                Slog.d(TAG, "Can't read slice access file", e);
-            }
-        }
+        mPermissions = new SlicePermissionManager(mContext, looper);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
@@ -211,26 +191,58 @@
     }
 
     @Override
+    public void grantSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
+        verifyCaller(pkg);
+        int user = Binder.getCallingUserHandle().getIdentifier();
+        enforceOwner(pkg, uri, user);
+        mPermissions.grantSliceAccess(toPkg, user, pkg, user, uri);
+    }
+
+    @Override
+    public void revokeSlicePermission(String pkg, String toPkg, Uri uri) throws RemoteException {
+        verifyCaller(pkg);
+        int user = Binder.getCallingUserHandle().getIdentifier();
+        enforceOwner(pkg, uri, user);
+        mPermissions.revokeSliceAccess(toPkg, user, pkg, user, uri);
+    }
+
+    @Override
     public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
-            String[] autoGrantPermissions) throws RemoteException {
+            String[] autoGrantPermissions) {
+        int userId = UserHandle.getUserId(uid);
+        if (pkg == null) {
+            for (String p : mContext.getPackageManager().getPackagesForUid(uid)) {
+                if (checkSlicePermission(uri, p, pid, uid, autoGrantPermissions)
+                        == PERMISSION_GRANTED) {
+                    return PERMISSION_GRANTED;
+                }
+            }
+            return PERMISSION_DENIED;
+        }
+        if (hasFullSliceAccess(pkg, userId)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        if (mPermissions.hasPermission(pkg, userId, uri)) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
+        if (autoGrantPermissions != null) {
+            // Need to own the Uri to call in with permissions to grant.
+            enforceOwner(pkg, uri, userId);
+            for (String perm : autoGrantPermissions) {
+                if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
+                    int providerUser = ContentProvider.getUserIdFromUri(uri, userId);
+                    String providerPkg = getProviderPkg(uri, providerUser);
+                    mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, uri);
+                    return PackageManager.PERMISSION_GRANTED;
+                }
+            }
+        }
+        // Fallback to allowing uri permissions through.
         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                 == PERMISSION_GRANTED) {
-            return SliceManager.PERMISSION_GRANTED;
+            return PackageManager.PERMISSION_GRANTED;
         }
-        if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) {
-            return SliceManager.PERMISSION_GRANTED;
-        }
-        for (String perm : autoGrantPermissions) {
-            if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) {
-                return SliceManager.PERMISSION_USER_GRANTED;
-            }
-        }
-        synchronized (mLock) {
-            if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) {
-                return SliceManager.PERMISSION_USER_GRANTED;
-            }
-        }
-        return SliceManager.PERMISSION_DENIED;
+        return PackageManager.PERMISSION_DENIED;
     }
 
     @Override
@@ -238,16 +250,17 @@
         verifyCaller(callingPkg);
         getContext().enforceCallingOrSelfPermission(permission.MANAGE_SLICE_PERMISSIONS,
                 "Slice granting requires MANAGE_SLICE_PERMISSIONS");
+        int userId = Binder.getCallingUserHandle().getIdentifier();
         if (allSlices) {
-            synchronized (mAccessList) {
-                mAccessList.grantFullAccess(pkg, Binder.getCallingUserHandle().getIdentifier());
-            }
-            mHandler.post(mSaveAccessList);
+            mPermissions.grantFullAccess(pkg, userId);
         } else {
-            synchronized (mLock) {
-                mUserGrants.add(new SliceGrant(uri, pkg,
-                        Binder.getCallingUserHandle().getIdentifier()));
-            }
+            // When granting, grant to all slices in the provider.
+            Uri grantUri = uri.buildUpon()
+                    .path("")
+                    .build();
+            int providerUser = ContentProvider.getUserIdFromUri(grantUri, userId);
+            String providerPkg = getProviderPkg(grantUri, providerUser);
+            mPermissions.grantSliceAccess(pkg, userId, providerPkg, providerUser, grantUri);
         }
         long ident = Binder.clearCallingIdentity();
         try {
@@ -268,19 +281,17 @@
             Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
             return null;
         }
-        synchronized(mSliceAccessFile) {
-            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            try {
-                XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
-                out.setOutput(baos, Encoding.UTF_8.name());
-                synchronized (mAccessList) {
-                    mAccessList.writeXml(out, user);
-                }
-                out.flush();
-                return baos.toByteArray();
-            } catch (IOException | XmlPullParserException e) {
-                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
-            }
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+            out.setOutput(baos, Encoding.UTF_8.name());
+
+            mPermissions.writeBackup(out);
+
+            out.flush();
+            return baos.toByteArray();
+        } catch (IOException | XmlPullParserException e) {
+            Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
         }
         return null;
     }
@@ -299,27 +310,21 @@
             Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
             return;
         }
-        synchronized(mSliceAccessFile) {
-            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
-            try {
-                XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
-                parser.setInput(bais, Encoding.UTF_8.name());
-                synchronized (mAccessList) {
-                    mAccessList.readXml(parser);
-                }
-                mHandler.post(mSaveAccessList);
-            } catch (NumberFormatException | XmlPullParserException | IOException e) {
-                Slog.w(TAG, "applyRestore: error reading payload", e);
-            }
+        final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+        try {
+            XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+            parser.setInput(bais, Encoding.UTF_8.name());
+            mPermissions.readRestore(parser);
+        } catch (NumberFormatException | XmlPullParserException | IOException e) {
+            Slog.w(TAG, "applyRestore: error reading payload", e);
         }
     }
 
     ///  ----- internal code -----
-    private void removeFullAccess(String pkg, int userId) {
-        synchronized (mAccessList) {
-            mAccessList.removeGrant(pkg, userId);
+    private void enforceOwner(String pkg, Uri uri, int user) {
+        if (!Objects.equals(getProviderPkg(uri, user), pkg) || pkg == null) {
+            throw new SecurityException("Caller must own " + uri);
         }
-        mHandler.post(mSaveAccessList);
     }
 
     protected void removePinnedSlice(Uri uri) {
@@ -368,19 +373,7 @@
     }
 
     protected int checkAccess(String pkg, Uri uri, int uid, int pid) {
-        int user = UserHandle.getUserId(uid);
-        // Check for default launcher/assistant.
-        if (!hasFullSliceAccess(pkg, user)) {
-            // Also allow things with uri access.
-            if (getContext().checkUriPermission(uri, pid, uid,
-                    Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != PERMISSION_GRANTED) {
-                // Last fallback (if the calling app owns the authority, then it can have access).
-                if (!Objects.equals(getProviderPkg(uri, user), pkg)) {
-                    return PERMISSION_DENIED;
-                }
-            }
-        }
-        return PERMISSION_GRANTED;
+        return checkSlicePermission(uri, pkg, uid, pid, null);
     }
 
     private String getProviderPkg(Uri uri, int user) {
@@ -425,15 +418,11 @@
     private void enforceAccess(String pkg, Uri uri) throws RemoteException {
         if (checkAccess(pkg, uri, Binder.getCallingUid(), Binder.getCallingPid())
                 != PERMISSION_GRANTED) {
-            throw new SecurityException("Access to slice " + uri + " is required");
-        }
-        enforceCrossUser(pkg, uri);
-    }
-
-    private void enforceFullAccess(String pkg, String name, Uri uri) {
-        int user = Binder.getCallingUserHandle().getIdentifier();
-        if (!hasFullSliceAccess(pkg, user)) {
-            throw new SecurityException(String.format("Call %s requires full slice access", name));
+            int userId = ContentProvider.getUserIdFromUri(uri,
+                    Binder.getCallingUserHandle().getIdentifier());
+            if (!Objects.equals(pkg, getProviderPkg(uri, userId))) {
+                throw new SecurityException("Access to slice " + uri + " is required");
+            }
         }
         enforceCrossUser(pkg, uri);
     }
@@ -513,9 +502,7 @@
     }
 
     private boolean isGrantedFullAccess(String pkg, int userId) {
-        synchronized (mAccessList) {
-            return mAccessList.hasFullAccess(pkg, userId);
-        }
+        return mPermissions.hasFullAccess(pkg, userId);
     }
 
     private static ServiceThread createHandler() {
@@ -525,34 +512,6 @@
         return handlerThread;
     }
 
-    private final Runnable mSaveAccessList = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mSliceAccessFile) {
-                final FileOutputStream stream;
-                try {
-                    stream = mSliceAccessFile.startWrite();
-                } catch (IOException e) {
-                    Slog.w(TAG, "Failed to save access file", e);
-                    return;
-                }
-
-                try {
-                    XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
-                    out.setOutput(stream, Encoding.UTF_8.name());
-                    synchronized (mAccessList) {
-                        mAccessList.writeXml(out, UserHandle.USER_ALL);
-                    }
-                    out.flush();
-                    mSliceAccessFile.finishWrite(stream);
-                } catch (IOException | XmlPullParserException e) {
-                    Slog.w(TAG, "Failed to save access file, restoring backup", e);
-                    mSliceAccessFile.failWrite(stream);
-                }
-            }
-        }
-    };
-
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -572,11 +531,11 @@
                     final boolean replacing =
                             intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
                     if (!replacing) {
-                        removeFullAccess(pkg, userId);
+                        mPermissions.removePkg(pkg, userId);
                     }
                     break;
                 case Intent.ACTION_PACKAGE_DATA_CLEARED:
-                    removeFullAccess(pkg, userId);
+                    mPermissions.removePkg(pkg, userId);
                     break;
             }
         }
diff --git a/services/core/java/com/android/server/slice/SlicePermissionManager.java b/services/core/java/com/android/server/slice/SlicePermissionManager.java
new file mode 100644
index 0000000..d25ec89
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SlicePermissionManager.java
@@ -0,0 +1,432 @@
+/*
+ * 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.slice;
+
+import android.content.ContentProvider;
+import android.content.Context;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.format.DateUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.Slog;
+import android.util.Xml.Encoding;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+import com.android.server.slice.SliceProviderPermissions.SliceAuthority;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Objects;
+
+public class SlicePermissionManager implements DirtyTracker {
+
+    private static final String TAG = "SlicePermissionManager";
+
+    /**
+     * The amount of time we'll cache a SliceProviderPermissions or SliceClientPermissions
+     * in case they are used again.
+     */
+    private static final long PERMISSION_CACHE_PERIOD = 5 * DateUtils.MINUTE_IN_MILLIS;
+
+    /**
+     * The amount of time we delay flushing out permission changes to disk because they usually
+     * come in short bursts.
+     */
+    private static final long WRITE_GRACE_PERIOD = 500;
+
+    private static final String SLICE_DIR = "slice";
+
+    // If/when this bumps again we'll need to write it out in the disk somewhere.
+    // Currently we don't have a central file for this in version 2 and there is no
+    // reason to add one until we actually have incompatible version bumps.
+    // This does however block us from reading backups from P-DP1 which may contain
+    // a very different XML format for perms.
+    static final int DB_VERSION = 2;
+
+    private static final String TAG_LIST = "slice-access-list";
+    private final String ATT_VERSION = "version";
+
+    private final File mSliceDir;
+    private final Context mContext;
+    private final Handler mHandler;
+    private final ArrayMap<PkgUser, SliceProviderPermissions> mCachedProviders = new ArrayMap<>();
+    private final ArrayMap<PkgUser, SliceClientPermissions> mCachedClients = new ArrayMap<>();
+    private final ArraySet<Persistable> mDirty = new ArraySet<>();
+
+    @VisibleForTesting
+    SlicePermissionManager(Context context, Looper looper, File sliceDir) {
+        mContext = context;
+        mHandler = new H(looper);
+        mSliceDir = sliceDir;
+    }
+
+    public SlicePermissionManager(Context context, Looper looper) {
+        this(context, looper, new File(Environment.getDataDirectory(), "system/" + SLICE_DIR));
+    }
+
+    public void grantFullAccess(String pkg, int userId) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        SliceClientPermissions client = getClient(pkgUser);
+        client.setHasFullAccess(true);
+    }
+
+    public void grantSliceAccess(String pkg, int userId, String providerPkg, int providerUser,
+            Uri uri) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        PkgUser providerPkgUser = new PkgUser(providerPkg, providerUser);
+
+        SliceClientPermissions client = getClient(pkgUser);
+        client.grantUri(uri, providerPkgUser);
+
+        SliceProviderPermissions provider = getProvider(providerPkgUser);
+        provider.getOrCreateAuthority(ContentProvider.getUriWithoutUserId(uri).getAuthority())
+                .addPkg(pkgUser);
+    }
+
+    public void revokeSliceAccess(String pkg, int userId, String providerPkg, int providerUser,
+            Uri uri) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        PkgUser providerPkgUser = new PkgUser(providerPkg, providerUser);
+
+        SliceClientPermissions client = getClient(pkgUser);
+        client.revokeUri(uri, providerPkgUser);
+    }
+
+    public void removePkg(String pkg, int userId) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        SliceProviderPermissions provider = getProvider(pkgUser);
+
+        for (SliceAuthority authority : provider.getAuthorities()) {
+            for (PkgUser p : authority.getPkgs()) {
+                getClient(p).removeAuthority(authority.getAuthority(), userId);
+            }
+        }
+        SliceClientPermissions client = getClient(pkgUser);
+        client.clear();
+        mHandler.obtainMessage(H.MSG_REMOVE, pkgUser);
+    }
+
+    public boolean hasFullAccess(String pkg, int userId) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        return getClient(pkgUser).hasFullAccess();
+    }
+
+    public boolean hasPermission(String pkg, int userId, Uri uri) {
+        PkgUser pkgUser = new PkgUser(pkg, userId);
+        SliceClientPermissions client = getClient(pkgUser);
+        int providerUserId = ContentProvider.getUserIdFromUri(uri, userId);
+        return client.hasFullAccess()
+                || client.hasPermission(ContentProvider.getUriWithoutUserId(uri), providerUserId);
+    }
+
+    @Override
+    public void onPersistableDirty(Persistable obj) {
+        mHandler.removeMessages(H.MSG_PERSIST);
+        mHandler.obtainMessage(H.MSG_ADD_DIRTY, obj).sendToTarget();
+        mHandler.sendEmptyMessageDelayed(H.MSG_PERSIST, WRITE_GRACE_PERIOD);
+    }
+
+    public void writeBackup(XmlSerializer out) throws IOException, XmlPullParserException {
+        synchronized (this) {
+            out.startTag(null, TAG_LIST);
+            out.attribute(null, ATT_VERSION, String.valueOf(DB_VERSION));
+
+            // Don't do anything with changes from the backup, because there shouldn't be any.
+            DirtyTracker tracker = obj -> { };
+            if (mHandler.hasMessages(H.MSG_PERSIST)) {
+                mHandler.removeMessages(H.MSG_PERSIST);
+                handlePersist();
+            }
+            for (String file : new File(mSliceDir.getAbsolutePath()).list()) {
+                if (file.isEmpty()) continue;
+                try (ParserHolder parser = getParser(file)) {
+                    Persistable p;
+                    while (parser.parser.getEventType() != XmlPullParser.START_TAG) {
+                        parser.parser.next();
+                    }
+                    if (SliceClientPermissions.TAG_CLIENT.equals(parser.parser.getName())) {
+                        p = SliceClientPermissions.createFrom(parser.parser, tracker);
+                    } else {
+                        p = SliceProviderPermissions.createFrom(parser.parser, tracker);
+                    }
+                    p.writeTo(out);
+                }
+            }
+
+            out.endTag(null, TAG_LIST);
+        }
+    }
+
+    public void readRestore(XmlPullParser parser) throws IOException, XmlPullParserException {
+        synchronized (this) {
+            while ((parser.getEventType() != XmlPullParser.START_TAG
+                    || !TAG_LIST.equals(parser.getName()))
+                    && parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                parser.next();
+            }
+            int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
+            if (xmlVersion < DB_VERSION) {
+                // No conversion support right now.
+                return;
+            }
+            while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+                if (parser.getEventType() == XmlPullParser.START_TAG) {
+                    if (SliceClientPermissions.TAG_CLIENT.equals(parser.getName())) {
+                        SliceClientPermissions client = SliceClientPermissions.createFrom(parser,
+                                this);
+                        synchronized (mCachedClients) {
+                            mCachedClients.put(client.getPkg(), client);
+                        }
+                        onPersistableDirty(client);
+                        mHandler.sendMessageDelayed(
+                                mHandler.obtainMessage(H.MSG_CLEAR_CLIENT, client.getPkg()),
+                                PERMISSION_CACHE_PERIOD);
+                    } else if (SliceProviderPermissions.TAG_PROVIDER.equals(parser.getName())) {
+                        SliceProviderPermissions provider = SliceProviderPermissions.createFrom(
+                                parser, this);
+                        synchronized (mCachedProviders) {
+                            mCachedProviders.put(provider.getPkg(), provider);
+                        }
+                        onPersistableDirty(provider);
+                        mHandler.sendMessageDelayed(
+                                mHandler.obtainMessage(H.MSG_CLEAR_PROVIDER, provider.getPkg()),
+                                PERMISSION_CACHE_PERIOD);
+                    } else {
+                        parser.next();
+                    }
+                } else {
+                    parser.next();
+                }
+            }
+        }
+    }
+
+    private SliceClientPermissions getClient(PkgUser pkgUser) {
+        SliceClientPermissions client;
+        synchronized (mCachedClients) {
+            client = mCachedClients.get(pkgUser);
+        }
+        if (client == null) {
+            try (ParserHolder parser = getParser(SliceClientPermissions.getFileName(pkgUser))) {
+                client = SliceClientPermissions.createFrom(parser.parser, this);
+                synchronized (mCachedClients) {
+                    mCachedClients.put(pkgUser, client);
+                }
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(H.MSG_CLEAR_CLIENT, pkgUser),
+                        PERMISSION_CACHE_PERIOD);
+                return client;
+            } catch (FileNotFoundException e) {
+                // No client exists yet.
+            } catch (IOException e) {
+                Log.e(TAG, "Can't read client", e);
+            } catch (XmlPullParserException e) {
+                Log.e(TAG, "Can't read client", e);
+            }
+            // Can't read or no permissions exist, create a clean object.
+            client = new SliceClientPermissions(pkgUser, this);
+        }
+        return client;
+    }
+
+    private SliceProviderPermissions getProvider(PkgUser pkgUser) {
+        SliceProviderPermissions provider;
+        synchronized (mCachedProviders) {
+            provider = mCachedProviders.get(pkgUser);
+        }
+        if (provider == null) {
+            try (ParserHolder parser = getParser(SliceProviderPermissions.getFileName(pkgUser))) {
+                provider = SliceProviderPermissions.createFrom(parser.parser, this);
+                synchronized (mCachedProviders) {
+                    mCachedProviders.put(pkgUser, provider);
+                }
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(H.MSG_CLEAR_PROVIDER, pkgUser),
+                        PERMISSION_CACHE_PERIOD);
+                return provider;
+            } catch (FileNotFoundException e) {
+                // No provider exists yet.
+            } catch (IOException e) {
+                Log.e(TAG, "Can't read provider", e);
+            } catch (XmlPullParserException e) {
+                Log.e(TAG, "Can't read provider", e);
+            }
+            // Can't read or no permissions exist, create a clean object.
+            provider = new SliceProviderPermissions(pkgUser, this);
+        }
+        return provider;
+    }
+
+    private ParserHolder getParser(String fileName)
+            throws FileNotFoundException, XmlPullParserException {
+        AtomicFile file = getFile(fileName);
+        ParserHolder holder = new ParserHolder();
+        holder.input = file.openRead();
+        holder.parser = XmlPullParserFactory.newInstance().newPullParser();
+        holder.parser.setInput(holder.input, Encoding.UTF_8.name());
+        return holder;
+    }
+
+    private AtomicFile getFile(String fileName) {
+        if (!mSliceDir.exists()) {
+            mSliceDir.mkdir();
+        }
+        return new AtomicFile(new File(mSliceDir, fileName));
+    }
+
+    private void handlePersist() {
+        synchronized (this) {
+            for (Persistable persistable : mDirty) {
+                AtomicFile file = getFile(persistable.getFileName());
+                final FileOutputStream stream;
+                try {
+                    stream = file.startWrite();
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to save access file", e);
+                    return;
+                }
+
+                try {
+                    XmlSerializer out = XmlPullParserFactory.newInstance().newSerializer();
+                    out.setOutput(stream, Encoding.UTF_8.name());
+
+                    persistable.writeTo(out);
+
+                    out.flush();
+                    file.finishWrite(stream);
+                } catch (IOException | XmlPullParserException e) {
+                    Slog.w(TAG, "Failed to save access file, restoring backup", e);
+                    file.failWrite(stream);
+                }
+            }
+            mDirty.clear();
+        }
+    }
+
+    private void handleRemove(PkgUser pkgUser) {
+        getFile(SliceClientPermissions.getFileName(pkgUser)).delete();
+        getFile(SliceProviderPermissions.getFileName(pkgUser)).delete();
+        mDirty.remove(mCachedClients.remove(pkgUser));
+        mDirty.remove(mCachedProviders.remove(pkgUser));
+    }
+
+    private final class H extends Handler {
+        private static final int MSG_ADD_DIRTY = 1;
+        private static final int MSG_PERSIST = 2;
+        private static final int MSG_REMOVE = 3;
+        private static final int MSG_CLEAR_CLIENT = 4;
+        private static final int MSG_CLEAR_PROVIDER = 5;
+
+        public H(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_ADD_DIRTY:
+                    mDirty.add((Persistable) msg.obj);
+                    break;
+                case MSG_PERSIST:
+                    handlePersist();
+                    break;
+                case MSG_REMOVE:
+                    handleRemove((PkgUser) msg.obj);
+                    break;
+                case MSG_CLEAR_CLIENT:
+                    synchronized (mCachedClients) {
+                        mCachedClients.remove(msg.obj);
+                    }
+                    break;
+                case MSG_CLEAR_PROVIDER:
+                    synchronized (mCachedProviders) {
+                        mCachedProviders.remove(msg.obj);
+                    }
+                    break;
+            }
+        }
+    }
+
+    public static class PkgUser {
+        private static final String SEPARATOR = "@";
+        private static final String FORMAT = "%s" + SEPARATOR + "%d";
+        private final String mPkg;
+        private final int mUserId;
+
+        public PkgUser(String pkg, int userId) {
+            mPkg = pkg;
+            mUserId = userId;
+        }
+
+        public PkgUser(String pkgUserStr) throws IllegalArgumentException {
+            try {
+                String[] vals = pkgUserStr.split(SEPARATOR, 2);
+                mPkg = vals[0];
+                mUserId = Integer.parseInt(vals[1]);
+            } catch (Exception e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        public String getPkg() {
+            return mPkg;
+        }
+
+        public int getUserId() {
+            return mUserId;
+        }
+
+        @Override
+        public int hashCode() {
+            return mPkg.hashCode() + mUserId;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+            PkgUser other = (PkgUser) obj;
+            return Objects.equals(other.mPkg, mPkg) && other.mUserId == mUserId;
+        }
+
+        @Override
+        public String toString() {
+            return String.format(FORMAT, mPkg, mUserId);
+        }
+    }
+
+    private class ParserHolder implements AutoCloseable {
+
+        private InputStream input;
+        private XmlPullParser parser;
+
+        @Override
+        public void close() throws IOException {
+            input.close();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/slice/SliceProviderPermissions.java b/services/core/java/com/android/server/slice/SliceProviderPermissions.java
new file mode 100644
index 0000000..6e602d5
--- /dev/null
+++ b/services/core/java/com/android/server/slice/SliceProviderPermissions.java
@@ -0,0 +1,204 @@
+/*
+ * 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.slice;
+
+import android.annotation.NonNull;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.slice.DirtyTracker.Persistable;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+
+public class SliceProviderPermissions implements DirtyTracker, Persistable {
+
+    private static final String TAG = "SliceProviderPermissions";
+
+    static final String TAG_PROVIDER = "provider";
+    private static final String TAG_AUTHORITY = "authority";
+    private static final String TAG_PKG = "pkg";
+    private static final String NAMESPACE = null;
+
+    private static final String ATTR_PKG = "pkg";
+    private static final String ATTR_AUTHORITY = "authority";
+
+    private final PkgUser mPkg;
+    private final ArrayMap<String, SliceAuthority> mAuths = new ArrayMap<>();
+    private final DirtyTracker mTracker;
+
+    public SliceProviderPermissions(@NonNull PkgUser pkg, @NonNull DirtyTracker tracker) {
+        mPkg = pkg;
+        mTracker = tracker;
+    }
+
+    public PkgUser getPkg() {
+        return mPkg;
+    }
+
+    public synchronized Collection<SliceAuthority> getAuthorities() {
+        return new ArrayList<>(mAuths.values());
+    }
+
+    public synchronized SliceAuthority getOrCreateAuthority(String authority) {
+        SliceAuthority ret = mAuths.get(authority);
+        if (ret == null) {
+            ret = new SliceAuthority(authority, this);
+            mAuths.put(authority, ret);
+            onPersistableDirty(ret);
+        }
+        return ret;
+    }
+
+    @Override
+    public void onPersistableDirty(Persistable obj) {
+        mTracker.onPersistableDirty(this);
+    }
+
+    @Override
+    public String getFileName() {
+        return getFileName(mPkg);
+    }
+
+    public synchronized void writeTo(XmlSerializer out) throws IOException {
+        out.startTag(NAMESPACE, TAG_PROVIDER);
+        out.attribute(NAMESPACE, ATTR_PKG, mPkg.toString());
+
+        final int N = mAuths.size();
+        for (int i = 0; i < N; i++) {
+            out.startTag(NAMESPACE, TAG_AUTHORITY);
+            out.attribute(NAMESPACE, ATTR_AUTHORITY, mAuths.valueAt(i).mAuthority);
+
+            mAuths.valueAt(i).writeTo(out);
+
+            out.endTag(NAMESPACE, TAG_AUTHORITY);
+        }
+
+        out.endTag(NAMESPACE, TAG_PROVIDER);
+    }
+
+    public static SliceProviderPermissions createFrom(XmlPullParser parser, DirtyTracker tracker)
+            throws XmlPullParserException, IOException {
+        // Get to the beginning of the provider.
+        while (parser.getEventType() != XmlPullParser.START_TAG
+                || !TAG_PROVIDER.equals(parser.getName())) {
+            parser.next();
+        }
+        int depth = parser.getDepth();
+        PkgUser pkgUser = new PkgUser(parser.getAttributeValue(NAMESPACE, ATTR_PKG));
+        SliceProviderPermissions provider = new SliceProviderPermissions(pkgUser, tracker);
+        parser.next();
+
+        while (parser.getDepth() > depth) {
+            if (parser.getEventType() == XmlPullParser.START_TAG
+                    && TAG_AUTHORITY.equals(parser.getName())) {
+                try {
+                    SliceAuthority authority = new SliceAuthority(
+                            parser.getAttributeValue(NAMESPACE, ATTR_AUTHORITY), provider);
+                    authority.readFrom(parser);
+                    provider.mAuths.put(authority.getAuthority(), authority);
+                } catch (IllegalArgumentException e) {
+                    Slog.e(TAG, "Couldn't read PkgUser", e);
+                }
+            }
+
+            parser.next();
+        }
+        return provider;
+    }
+
+    public static String getFileName(PkgUser pkg) {
+        return String.format("provider_%s", pkg.toString());
+    }
+
+    public static class SliceAuthority implements Persistable {
+        private final String mAuthority;
+        private final DirtyTracker mTracker;
+        private final ArraySet<PkgUser> mPkgs = new ArraySet<>();
+
+        public SliceAuthority(String authority, DirtyTracker tracker) {
+            mAuthority = authority;
+            mTracker = tracker;
+        }
+
+        public String getAuthority() {
+            return mAuthority;
+        }
+
+        public synchronized void addPkg(PkgUser pkg) {
+            if (mPkgs.add(pkg)) {
+                mTracker.onPersistableDirty(this);
+            }
+        }
+
+        public synchronized void removePkg(PkgUser pkg) {
+            if (mPkgs.remove(pkg)) {
+                mTracker.onPersistableDirty(this);
+            }
+        }
+
+        public synchronized Collection<PkgUser> getPkgs() {
+            return new ArraySet<>(mPkgs);
+        }
+
+        @Override
+        public String getFileName() {
+            return null;
+        }
+
+        public synchronized void writeTo(XmlSerializer out) throws IOException {
+            final int N = mPkgs.size();
+            for (int i = 0; i < N; i++) {
+                out.startTag(NAMESPACE, TAG_PKG);
+                out.text(mPkgs.valueAt(i).toString());
+                out.endTag(NAMESPACE, TAG_PKG);
+            }
+        }
+
+        public synchronized void readFrom(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            parser.next();
+            int depth = parser.getDepth();
+            while (parser.getDepth() >= depth) {
+                if (parser.getEventType() == XmlPullParser.START_TAG
+                        && TAG_PKG.equals(parser.getName())) {
+                    mPkgs.add(new PkgUser(parser.nextText()));
+                }
+                parser.next();
+            }
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!getClass().equals(obj != null ? obj.getClass() : null)) return false;
+            SliceAuthority other = (SliceAuthority) obj;
+            return Objects.equals(mAuthority, other.mAuthority)
+                    && Objects.equals(mPkgs, other.mPkgs);
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s: %s)", mAuthority, mPkgs.toString());
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2db8039..36fa868 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -1096,6 +1096,30 @@
     }
 
     @Override
+    public void onNotificationSmartRepliesAdded(String key, int replyCount)
+            throws RemoteException {
+        enforceStatusBarService();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.onNotificationSmartRepliesAdded(key, replyCount);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void onNotificationSmartReplySent(String key, int replyIndex)
+            throws RemoteException {
+        enforceStatusBarService();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.onNotificationSmartReplySent(key, replyIndex);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void onNotificationSettingsViewed(String key) throws RemoteException {
         enforceStatusBarService();
         long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 1575694..165a409 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -113,63 +113,73 @@
         mListener.onWindowsGone();
     };
 
-    private final Runnable mAddStartingWindow = () -> {
-        final StartingData startingData;
-        final AppWindowToken container;
+    private final Runnable mAddStartingWindow = new Runnable() {
 
-        synchronized (mWindowMap) {
-            if (mContainer == null) {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
-                        + " add starting window");
+        @Override
+        public void run() {
+            final StartingData startingData;
+            final AppWindowToken container;
+
+            synchronized (mWindowMap) {
+                if (mContainer == null) {
+                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "mContainer was null while trying to"
+                            + " add starting window");
+                    return;
+                }
+
+                // There can only be one adding request, silly caller!
+                mService.mAnimationHandler.removeCallbacks(this);
+
+                startingData = mContainer.startingData;
+                container = mContainer;
+            }
+
+            if (startingData == null) {
+                // Animation has been canceled... do nothing.
+                if (DEBUG_STARTING_WINDOW)
+                    Slog.v(TAG_WM, "startingData was nulled out before handling"
+                            + " mAddStartingWindow: " + mContainer);
                 return;
             }
-            startingData = mContainer.startingData;
-            container = mContainer;
-        }
 
-        if (startingData == null) {
-            // Animation has been canceled... do nothing.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "startingData was nulled out before handling"
-                    + " mAddStartingWindow: " + mContainer);
-            return;
-        }
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
+                    + AppWindowContainerController.this + ": startingData="
+                    + container.startingData);
 
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                + this + ": startingData=" + container.startingData);
-
-        StartingSurface surface = null;
-        try {
-            surface = startingData.createStartingSurface(container);
-        } catch (Exception e) {
-            Slog.w(TAG_WM, "Exception when adding starting window", e);
-        }
-        if (surface != null) {
-            boolean abort = false;
-            synchronized(mWindowMap) {
-                // If the window was successfully added, then
-                // we need to remove it.
-                if (container.removed || container.startingData == null) {
-                    if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
-                            "Aborted starting " + container
-                                    + ": removed=" + container.removed
-                                    + " startingData=" + container.startingData);
-                    container.startingWindow = null;
-                    container.startingData = null;
-                    abort = true;
-                } else {
-                    container.startingSurface = surface;
+            StartingSurface surface = null;
+            try {
+                surface = startingData.createStartingSurface(container);
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when adding starting window", e);
+            }
+            if (surface != null) {
+                boolean abort = false;
+                synchronized (mWindowMap) {
+                    // If the window was successfully added, then
+                    // we need to remove it.
+                    if (container.removed || container.startingData == null) {
+                        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM,
+                                "Aborted starting " + container
+                                        + ": removed=" + container.removed
+                                        + " startingData=" + container.startingData);
+                        container.startingWindow = null;
+                        container.startingData = null;
+                        abort = true;
+                    } else {
+                        container.startingSurface = surface;
+                    }
+                    if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
+                            "Added starting " + mContainer
+                                    + ": startingWindow="
+                                    + container.startingWindow + " startingView="
+                                    + container.startingSurface);
                 }
-                if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
-                        "Added starting " + mContainer
-                                + ": startingWindow="
-                                + container.startingWindow + " startingView="
-                                + container.startingSurface);
+                if (abort) {
+                    surface.remove();
+                }
+            } else if (DEBUG_STARTING_WINDOW) {
+                Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
             }
-            if (abort) {
-                surface.remove();
-            }
-        } else if (DEBUG_STARTING_WINDOW) {
-            Slog.v(TAG_WM, "Surface returned was null: " + mContainer);
         }
     };
 
@@ -558,8 +568,10 @@
         // Note: we really want to do sendMessageAtFrontOfQueue() because we
         // want to process the message ASAP, before any other queued
         // messages.
-        if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
-        mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+        if (!mService.mAnimationHandler.hasCallbacks(mAddStartingWindow)) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Enqueueing ADD_STARTING");
+            mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow);
+        }
     }
 
     private boolean createSnapshot(TaskSnapshot snapshot) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 9a4db65..5676f58 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1312,7 +1312,8 @@
         if (prevWinMode != WINDOWING_MODE_UNDEFINED && winMode == WINDOWING_MODE_PINNED) {
             // Entering PiP from fullscreen, reset the snap fraction
             mDisplayContent.mPinnedStackControllerLocked.resetReentrySnapFraction(this);
-        } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED) {
+        } else if (prevWinMode == WINDOWING_MODE_PINNED && winMode != WINDOWING_MODE_UNDEFINED
+                && !isHidden()) {
             // Leaving PiP to fullscreen, save the snap fraction based on the pre-animation bounds
             // for the next re-entry into PiP (assuming the activity is not hidden or destroyed)
             final TaskStack pinnedStack = mDisplayContent.getPinnedStack();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 318b3d2..1ee642a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -17,19 +17,16 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
-import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
 
 import android.annotation.IntDef;
 import android.app.ActivityManager.TaskSnapshot;
@@ -41,24 +38,22 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
-import android.util.Log;
-import android.util.Slog;import android.util.proto.ProtoOutputStream;
+import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationController;
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.view.SurfaceControl.Transaction;
-
 import com.android.internal.annotations.VisibleForTesting;
-import com.google.android.collect.Sets;
-
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
 import com.android.server.wm.utils.InsetUtils;
-
+import com.google.android.collect.Sets;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+
 /**
  * Controls a single instance of the remote driven recents animation. In particular, this allows
  * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
@@ -67,8 +62,7 @@
  * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
  */
 public class RecentsAnimationController implements DeathRecipient {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "RecentsAnimationController" : TAG_WM;
-    private static final boolean DEBUG = false;
+    private static final String TAG = RecentsAnimationController.class.getSimpleName();
     private static final long FAILSAFE_DELAY = 1000;
 
     public static final int REORDER_KEEP_IN_PLACE = 0;
@@ -88,7 +82,7 @@
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
     private final int mDisplayId;
     private final Runnable mFailsafeRunnable = () -> {
-        cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
     };
 
     // The recents component app token that is shown behind the visibile tasks
@@ -111,7 +105,9 @@
     // minimized
     private boolean mSplitScreenMinimized;
 
-    private Rect mTmpRect = new Rect();
+    private final Rect mTmpRect = new Rect();
+
+    private boolean mLinkedToDeathOfRunner;
 
     public interface RecentsAnimationCallbacks {
         void onAnimationFinished(@ReorderMode int reorderMode);
@@ -122,7 +118,8 @@
 
         @Override
         public TaskSnapshot screenshotTask(int taskId) {
-            if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled);
+            if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):"
+                    + " mCanceled=" + mCanceled);
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
@@ -151,7 +148,8 @@
 
         @Override
         public void finish(boolean moveHomeToTop) {
-            if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled);
+            if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
+                    + " mCanceled=" + mCanceled);
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
@@ -188,8 +186,8 @@
 
         @Override
         public void setInputConsumerEnabled(boolean enabled) {
-            if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled="
-                    + mCanceled);
+            if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):"
+                    + " mCanceled=" + mCanceled);
             final long token = Binder.clearCallingIdentity();
             try {
                 synchronized (mService.getWindowManagerLock()) {
@@ -262,14 +260,14 @@
 
         // Skip the animation if there is nothing to animate
         if (mPendingAnimations.isEmpty()) {
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
             return;
         }
 
         try {
-            mRunner.asBinder().linkToDeath(this, 0);
+            linkToDeathOfRunner();
         } catch (RemoteException e) {
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
             return;
         }
 
@@ -277,7 +275,8 @@
         final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED,
                 targetActivityType).getTopChild().getTopFullscreenAppToken();
         if (recentsComponentAppToken != null) {
-            if (DEBUG) Log.d(TAG, "setHomeApp(" + recentsComponentAppToken.getName() + ")");
+            if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
+                    + recentsComponentAppToken.getName() + ")");
             mTargetAppToken = recentsComponentAppToken;
             if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
                 dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -293,7 +292,7 @@
 
     @VisibleForTesting
     AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
-        if (DEBUG) Log.d(TAG, "addAnimation(" + task.getName() + ")");
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
         // TODO: Refactor this to use the task's animator
         final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
                 mService);
@@ -307,14 +306,15 @@
 
     @VisibleForTesting
     void removeAnimation(TaskAnimationAdapter taskAdapter) {
-        if (DEBUG) Log.d(TAG, "removeAnimation(" + taskAdapter.mTask.getName() + ")");
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation("
+                + taskAdapter.mTask.mTaskId + ")");
         taskAdapter.mTask.setCanAffectSystemUiFlags(true);
         taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
         mPendingAnimations.remove(taskAdapter);
     }
 
     void startAnimation() {
-        if (DEBUG) Log.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
                 + " mCanceled=" + mCanceled);
         if (!mPendingStart || mCanceled) {
             // Skip starting if we've already started or canceled the animation
@@ -334,7 +334,7 @@
 
             // Skip the animation if there is nothing to animate
             if (appAnimations.isEmpty()) {
-                cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+                cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
                 return;
             }
 
@@ -352,6 +352,13 @@
                             : null;
             mRunner.onAnimationStart_New(mController, appTargets, contentInsets,
                     minimizedHomeBounds);
+            if (DEBUG_RECENTS_ANIMATIONS) {
+                Slog.d(TAG, "startAnimation(): Notify animation start:");
+                for (int i = 0; i < mPendingAnimations.size(); i++) {
+                    final Task task = mPendingAnimations.get(i).mTask;
+                    Slog.d(TAG, "\t" + task.mTaskId);
+                }
+            }
         } catch (RemoteException e) {
             Slog.e(TAG, "Failed to start recents animation", e);
         }
@@ -361,8 +368,8 @@
                 reasons).sendToTarget();
     }
 
-    void cancelAnimation(@ReorderMode int reorderMode) {
-        if (DEBUG) Log.d(TAG, "cancelAnimation()");
+    void cancelAnimation(@ReorderMode int reorderMode, String reason) {
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation()");
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
                 // We've already canceled the animation
@@ -383,8 +390,9 @@
     }
 
     void cleanupAnimation(@ReorderMode int reorderMode) {
-        if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
-                + mPendingAnimations.size());
+        if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG,
+                "cleanupAnimation(): Notify animation finished mPendingAnimations="
+                        + mPendingAnimations.size() + " reorderMode=" + reorderMode);
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
@@ -393,7 +401,7 @@
             removeAnimation(taskAdapter);
         }
 
-        mRunner.asBinder().unlinkToDeath(this, 0);
+        unlinkToDeathOfRunner();
         // Clear associated input consumers
         mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
         mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
@@ -403,9 +411,23 @@
         mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
     }
 
+    private void linkToDeathOfRunner() throws RemoteException {
+        if (!mLinkedToDeathOfRunner) {
+            mRunner.asBinder().linkToDeath(this, 0);
+            mLinkedToDeathOfRunner = true;
+        }
+    }
+
+    private void unlinkToDeathOfRunner() {
+        if (mLinkedToDeathOfRunner) {
+            mRunner.asBinder().unlinkToDeath(this, 0);
+            mLinkedToDeathOfRunner = false;
+        }
+    }
+
     @Override
     public void binderDied() {
-        cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+        cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
     }
 
     void checkAnimationReady(WallpaperController wallpaperController) {
@@ -534,7 +556,7 @@
 
         @Override
         public void onAnimationCancelled(SurfaceControl animationLeash) {
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
+            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
         }
 
         @Override
@@ -556,6 +578,10 @@
             } else {
                 pw.print(prefix); pw.println("Target: null");
             }
+            pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
+            pw.println("mPosition=" + mPosition);
+            pw.println("mBounds=" + mBounds);
+            pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
         }
 
         @Override
@@ -572,6 +598,10 @@
         final String innerPrefix = prefix + "  ";
         pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
         pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
+        pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
+        pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
+        pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
         pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
+        pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
     }
 }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index 3be7b235..1b06b2f 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -19,6 +19,7 @@
 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
@@ -49,7 +50,9 @@
  * Helper class to run app animations in a remote process.
  */
 class RemoteAnimationController implements DeathRecipient {
-    private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
+    private static final String TAG = TAG_WITH_CLASS_NAME
+            || (DEBUG_REMOTE_ANIMATIONS && !DEBUG_APP_TRANSITIONS)
+                    ? "RemoteAnimationController" : TAG_WM;
     private static final long TIMEOUT_MS = 2000;
 
     private final WindowManagerService mService;
@@ -57,10 +60,11 @@
     private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
     private final Rect mTmpRect = new Rect();
     private final Handler mHandler;
-    private final Runnable mTimeoutRunnable = this::cancelAnimation;
+    private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
 
     private FinishedCallback mFinishedCallback;
     private boolean mCanceled;
+    private boolean mLinkedToDeathOfRunner;
 
     RemoteAnimationController(WindowManagerService service,
             RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -79,6 +83,7 @@
      */
     AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
             Rect stackBounds) {
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken);
         final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
                 appWindowToken, position, stackBounds);
         mPendingAnimations.add(adapter);
@@ -89,7 +94,10 @@
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
     void goodToGo() {
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
         if (mPendingAnimations.isEmpty() || mCanceled) {
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled="
+                    + mCanceled + " mPendingAnimations=" + mPendingAnimations.size());
             onAnimationFinished();
             return;
         }
@@ -101,25 +109,32 @@
 
         final RemoteAnimationTarget[] animations = createAnimations();
         if (animations.length == 0) {
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
             onAnimationFinished();
             return;
         }
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
-                mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
+                linkToDeathOfRunner();
                 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to start remote animation", e);
                 onAnimationFinished();
             }
+            if (DEBUG_REMOTE_ANIMATIONS) {
+                Slog.d(TAG, "startAnimation(): Notify animation start:");
+                for (int i = 0; i < mPendingAnimations.size(); i++) {
+                    Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken);
+                }
+            } else if (DEBUG_APP_TRANSITIONS) {
+                writeStartDebugStatement();
+            }
         });
         sendRunningRemoteAnimation(true);
-        if (DEBUG_APP_TRANSITIONS) {
-            writeStartDebugStatement();
-        }
     }
 
-    private void cancelAnimation() {
+    private void cancelAnimation(String reason) {
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
         synchronized (mService.getWindowManagerLock()) {
             if (mCanceled) {
                 return;
@@ -142,14 +157,16 @@
     }
 
     private RemoteAnimationTarget[] createAnimations() {
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
         final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
-            final RemoteAnimationTarget target =
-                    mPendingAnimations.get(i).createRemoteAppAnimation();
+            final RemoteAnimationTarget target = wrapper.createRemoteAppAnimation();
             if (target != null) {
+                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
                 targets.add(target);
             } else {
+                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken);
 
                 // We can't really start an animation but we still need to make sure to finish the
                 // pending animation that was started by SurfaceAnimator
@@ -163,22 +180,29 @@
     }
 
     private void onAnimationFinished() {
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
+                + mPendingAnimations.size());
         mHandler.removeCallbacks(mTimeoutRunnable);
-        mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
         synchronized (mService.mWindowMap) {
+            unlinkToDeathOfRunner();
             releaseFinishedCallback();
             mService.openSurfaceTransaction();
             try {
+                if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:");
                 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
                     final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
                     adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
+                    if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapter.mAppWindowToken);
                 }
+            } catch (Exception e) {
+                Slog.e(TAG, "Failed to finish remote animation", e);
+                throw e;
             } finally {
                 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
             }
         }
         sendRunningRemoteAnimation(false);
-        if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
+        if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation");
     }
 
     private void invokeAnimationCancelled() {
@@ -204,9 +228,23 @@
         mService.sendSetRunningRemoteAnimation(pid, running);
     }
 
+    private void linkToDeathOfRunner() throws RemoteException {
+        if (!mLinkedToDeathOfRunner) {
+            mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
+            mLinkedToDeathOfRunner = true;
+        }
+    }
+
+    private void unlinkToDeathOfRunner() {
+        if (mLinkedToDeathOfRunner) {
+            mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
+            mLinkedToDeathOfRunner = false;
+        }
+    }
+
     @Override
     public void binderDied() {
-        cancelAnimation();
+        cancelAnimation("binderDied");
     }
 
     private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
@@ -219,6 +257,7 @@
 
         @Override
         public void onAnimationFinished() throws RemoteException {
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter);
             final long token = Binder.clearCallingIdentity();
             try {
                 if (mOuter != null) {
@@ -238,6 +277,7 @@
          * to prevent memory leak.
          */
         void release() {
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter);
             mOuter = null;
         }
     };
@@ -301,6 +341,7 @@
         @Override
         public void startAnimation(SurfaceControl animationLeash, Transaction t,
                 OnAnimationFinishedCallback finishCallback) {
+            if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
 
             // Restore z-layering, position and stack crop until client has a chance to modify it.
             t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 662d51d..c808c91 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -45,6 +45,7 @@
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayCutout;
+import android.view.DisplayCutout.ParcelableWrapper;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
@@ -218,7 +219,7 @@
             int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets) {
         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                 new Rect() /* outFrame */, outContentInsets, outStableInsets, null /* outOutsets */,
-                null /* cutout */, null /* outInputChannel */);
+                new ParcelableWrapper() /* cutout */, null /* outInputChannel */);
     }
 
     @Override
@@ -233,16 +234,16 @@
 
     @Override
     public int relayout(IWindow window, int seq, WindowManager.LayoutParams attrs,
-            int requestedWidth, int requestedHeight, int viewFlags,
-            int flags, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
-            Rect outVisibleInsets, Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
-            DisplayCutout.ParcelableWrapper cutout,
-            MergedConfiguration mergedConfiguration, Surface outSurface) {
+            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
+            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets, Rect outVisibleInsets,
+            Rect outStableInsets, Rect outsets, Rect outBackdropFrame,
+            DisplayCutout.ParcelableWrapper cutout, MergedConfiguration mergedConfiguration,
+            Surface outSurface) {
         if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                 + Binder.getCallingPid());
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
         int res = mService.relayoutWindow(this, window, seq, attrs,
-                requestedWidth, requestedHeight, viewFlags, flags,
+                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                 outFrame, outOverscanInsets, outContentInsets, outVisibleInsets,
                 outStableInsets, outsets, outBackdropFrame, cutout,
                 mergedConfiguration, outSurface);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index b895cf2..67d2be8 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -215,8 +215,8 @@
                 currentOrientation);
         window.setOuter(snapshotSurface);
         try {
-            session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
-                    tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
+            session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, -1,
+                    tmpFrame, tmpRect, tmpContentInsets, tmpRect, tmpStableInsets, tmpRect, tmpRect,
                     tmpCutout, tmpMergedConfiguration, surface);
         } catch (RemoteException e) {
             // Local call.
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 2175c6b..ae9e802 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -37,6 +37,7 @@
 import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME;
 import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT;
 import static com.android.server.wm.StackProto.ADJUST_IME_AMOUNT;
+import static com.android.server.wm.StackProto.ANIMATING_BOUNDS;
 import static com.android.server.wm.StackProto.ANIMATION_BACKGROUND_SURFACE_IS_DIMMING;
 import static com.android.server.wm.StackProto.BOUNDS;
 import static com.android.server.wm.StackProto.DEFER_REMOVAL;
@@ -827,6 +828,14 @@
             }
         }
 
+        if (inSplitScreenSecondaryWindowingMode()) {
+            // When the stack is resized due to entering split screen secondary, offset the
+            // windows to compensate for the new stack position.
+            forAllWindows(w -> {
+                w.mWinAnimator.setOffsetPositionForStackResize(true);
+            }, true);
+        }
+
         updateDisplayInfo(bounds);
         updateSurfaceBounds();
     }
@@ -1363,6 +1372,7 @@
         proto.write(ADJUST_IME_AMOUNT, mAdjustImeAmount);
         proto.write(ADJUST_DIVIDER_AMOUNT, mAdjustDividerAmount);
         mAdjustedBounds.writeToProto(proto, ADJUSTED_BOUNDS);
+        proto.write(ANIMATING_BOUNDS, mBoundsAnimating);
         proto.end(token);
     }
 
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7eaca5d..c63da77 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -620,7 +620,7 @@
             // If there was a recents animation in progress, cancel that animation
             if (mService.getRecentsAnimationController() != null) {
                 mService.getRecentsAnimationController().cancelAnimation(
-                        REORDER_MOVE_TO_ORIGINAL_POSITION);
+                        REORDER_MOVE_TO_ORIGINAL_POSITION, "wallpaperDrawPendingTimeout");
             }
             return true;
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
index 9d9805a..990eb97 100644
--- a/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
+++ b/services/core/java/com/android/server/wm/WindowManagerDebugConfig.java
@@ -74,6 +74,9 @@
     static final boolean SHOW_STACK_CRAWLS = false;
     static final boolean DEBUG_WINDOW_CROP = false;
     static final boolean DEBUG_UNKNOWN_APP_VISIBILITY = false;
+    // TODO (b/73188263): Reset debugging flags
+    static final boolean DEBUG_RECENTS_ANIMATIONS = true;
+    static final boolean DEBUG_REMOTE_ANIMATIONS = DEBUG_APP_TRANSITIONS || true;
 
     static final String TAG_KEEP_SCREEN_ON = "DebugKeepScreenOn";
     static final boolean DEBUG_KEEP_SCREEN_ON = false;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fa7eff2..407312a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1243,6 +1243,10 @@
                     Slog.w(TAG_WM, "Attempted to add window with exiting application token "
                           + token + ".  Aborting.");
                     return WindowManagerGlobal.ADD_APP_EXITING;
+                } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {
+                    Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"
+                            + " starting window");
+                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
                 }
             } else if (rootType == TYPE_INPUT_METHOD) {
                 if (token.windowType != TYPE_INPUT_METHOD) {
@@ -1831,10 +1835,9 @@
         }
     }
 
-    public int relayoutWindow(Session session, IWindow client, int seq,
-            LayoutParams attrs, int requestedWidth,
-            int requestedHeight, int viewVisibility, int flags,
-            Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
+    public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewVisibility, int flags,
+            long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
             Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
             DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
             Surface outSurface) {
@@ -1861,6 +1864,7 @@
                 win.setRequestedSize(requestedWidth, requestedHeight);
             }
 
+            win.setFrameNumber(frameNumber);
             int attrChanges = 0;
             int flagChanges = 0;
             if (attrs != null) {
@@ -1930,7 +1934,15 @@
                 winAnimator.setOpaqueLocked(false);
             }
 
-            boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
+            final int oldVisibility = win.mViewVisibility;
+
+            // If the window is becoming visible, visibleOrAdding may change which may in turn
+            // change the IME target.
+            final boolean becameVisible =
+                    (oldVisibility == View.INVISIBLE || oldVisibility == View.GONE)
+                            && viewVisibility == View.VISIBLE;
+            boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
+                    || becameVisible;
             final boolean isDefaultDisplay = win.isDefaultDisplay();
             boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
                     || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
@@ -1946,7 +1958,6 @@
             win.mRelayoutCalled = true;
             win.mInRelayout = true;
 
-            final int oldVisibility = win.mViewVisibility;
             win.mViewVisibility = viewVisibility;
             if (DEBUG_SCREEN_ON) {
                 RuntimeException stack = new RuntimeException();
@@ -2718,13 +2729,19 @@
         }
     }
 
-    public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+    /**
+     * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
+     * this method, as it can call back into AM, and locking will be done in the animation
+     * controller itself.
+     */
+    public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode,
+            String reason) {
         // Note: Do not hold the WM lock, this will lock appropriately in the call which also
         // calls through to AM/RecentsAnimation.onAnimationFinished()
         if (mRecentsAnimationController != null) {
             // This call will call through to cleanupAnimation() below after the animation is
             // canceled
-            mRecentsAnimationController.cancelAnimation(reorderMode);
+            mRecentsAnimationController.cancelAnimation(reorderMode, reason);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d10f005..9f2915b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -636,6 +636,11 @@
     private PowerManagerWrapper mPowerManagerWrapper;
 
     /**
+     * A frame number in which changes requested in this layout will be rendered.
+     */
+    private long mFrameNumber = -1;
+
+    /**
      * Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
      * of z-order and 1 otherwise.
      */
@@ -4655,7 +4660,7 @@
                 mLastSurfaceInsets.set(mAttrs.surfaceInsets);
                 t.deferTransactionUntil(mSurfaceControl,
                         mWinAnimator.mSurfaceController.mSurfaceControl.getHandle(),
-                        mAttrs.frameNumber);
+                        getFrameNumber());
             }
         }
     }
@@ -4771,6 +4776,14 @@
         return mService.mInputMethodTarget == this;
     }
 
+    long getFrameNumber() {
+        return mFrameNumber;
+    }
+
+    void setFrameNumber(long frameNumber) {
+        mFrameNumber = frameNumber;
+    }
+
     private final class MoveAnimationSpec implements AnimationSpec {
 
         private final long mDuration;
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e92d460..ab5e24a 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -67,7 +67,6 @@
 
 import com.android.server.policy.WindowManagerPolicy;
 
-import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
 /**
@@ -217,6 +216,12 @@
     int mXOffset = 0;
     int mYOffset = 0;
 
+    /**
+     * A flag to determine if the WSA needs to offset its position to compensate for the stack's
+     * position update before the WSA surface has resized.
+     */
+    private boolean mOffsetPositionForStackResize;
+
     private final Rect mTmpSize = new Rect();
 
     private final SurfaceControl.Transaction mReparentTransaction = new SurfaceControl.Transaction();
@@ -230,6 +235,8 @@
     // once per animation.
     boolean mPipAnimationStarted = false;
 
+    private final Point mTmpPos = new Point();
+
     WindowStateAnimator(final WindowState win) {
         final WindowManagerService service = win.mService;
 
@@ -498,6 +505,8 @@
             mSurfaceController = new WindowSurfaceController(mSession.mSurfaceSession,
                     attrs.getTitle().toString(), width, height, format, flags, this,
                     windowType, ownerUid);
+
+            setOffsetPositionForStackResize(false);
             mSurfaceFormat = format;
 
             w.setHasSurface(true);
@@ -859,7 +868,8 @@
         // However, this would be unsafe, as the client may be in the middle
         // of producing a frame at the old size, having just completed layout
         // to find the surface size changed underneath it.
-        if (!w.mRelayoutCalled || w.mInRelayout) {
+        final boolean relayout = !w.mRelayoutCalled || w.mInRelayout;
+        if (relayout) {
             mSurfaceResized = mSurfaceController.setSizeInTransaction(
                     mTmpSize.width(), mTmpSize.height(), recoveringMemory);
         } else {
@@ -996,7 +1006,38 @@
             mPipAnimationStarted = false;
 
             if (!w.mSeamlesslyRotated) {
-                mSurfaceController.setPositionInTransaction(mXOffset, mYOffset, recoveringMemory);
+                // Used to offset the WSA when stack position changes before a resize.
+                int xOffset = mXOffset;
+                int yOffset = mYOffset;
+                if (mOffsetPositionForStackResize) {
+                    if (relayout) {
+                        // Once a relayout is called, reset the offset back to 0 and defer
+                        // setting it until a new frame with the updated size. This ensures that
+                        // the WS position is reset (so the stack position is shown) at the same
+                        // time that the buffer size changes.
+                        setOffsetPositionForStackResize(false);
+                        mSurfaceController.deferTransactionUntil(mSurfaceController.getHandle(),
+                                mWin.getFrameNumber());
+                    } else {
+                        final TaskStack stack = mWin.getStack();
+                        mTmpPos.x = 0;
+                        mTmpPos.y = 0;
+                        if (stack != null) {
+                            stack.getRelativePosition(mTmpPos);
+                        }
+
+                        xOffset = -mTmpPos.x;
+                        yOffset = -mTmpPos.y;
+
+                        // Crop also needs to be extended so the bottom isn't cut off when the WSA
+                        // position is moved.
+                        if (clipRect != null) {
+                            clipRect.right += mTmpPos.x;
+                            clipRect.bottom += mTmpPos.y;
+                        }
+                    }
+                }
+                mSurfaceController.setPositionInTransaction(xOffset, yOffset, recoveringMemory);
             }
         }
 
@@ -1499,4 +1540,8 @@
     int getLayer() {
         return mLastLayer;
     }
+
+    void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) {
+        mOffsetPositionForStackResize = offsetPositionForStackResize;
+    }
 }
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 0ec16b5..8f989df 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -28,5 +28,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.servicestests" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index d908b8e..cecc94b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
@@ -30,6 +31,7 @@
 import static android.net.NetworkStats.SET_ALL;
 import static android.net.NetworkStats.TAG_ALL;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
+import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
@@ -43,6 +45,7 @@
 
 import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS;
 import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
+import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
 import static com.android.server.net.NetworkPolicyManagerService.TYPE_RAPID;
@@ -65,6 +68,7 @@
 import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -121,7 +125,7 @@
 import android.text.format.Time;
 import android.util.DataUnit;
 import android.util.Log;
-import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 
 import com.android.internal.telephony.PhoneConstants;
@@ -201,7 +205,8 @@
     private static final int TEST_SUB_ID = 42;
     private static final int TEST_NET_ID = 24;
 
-    private static NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi(TEST_SSID);
+    private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
+    private static NetworkTemplate sTemplateMobileAll = buildTemplateMobileAll(TEST_IMSI);
 
     /**
      * Path on assets where files used by {@link NetPolicyXml} are located.
@@ -221,6 +226,7 @@
     private @Mock IActivityManager mActivityManager;
     private @Mock INetworkManagementService mNetworkManager;
     private @Mock IConnectivityManager mConnManager;
+    private @Mock ConnectivityManager mConnectivityManager;
     private @Mock NotificationManager mNotifManager;
     private @Mock PackageManager mPackageManager;
     private @Mock IPackageManager mIpm;
@@ -228,6 +234,9 @@
     private @Mock CarrierConfigManager mCarrierConfigManager;
     private @Mock TelephonyManager mTelephonyManager;
 
+    private ArgumentCaptor<ConnectivityManager.NetworkCallback> mNetworkCallbackCaptor =
+            ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+
     private ActivityManagerInternal mActivityManagerInternal;
     private NetworkStatsManagerInternal mStatsService;
 
@@ -331,6 +340,8 @@
                         return mTelephonyManager;
                     case Context.NOTIFICATION_SERVICE:
                         return mNotifManager;
+                    case Context.CONNECTIVITY_SERVICE:
+                        return mConnectivityManager;
                     default:
                         return super.getSystemService(name);
                 }
@@ -394,6 +405,8 @@
                 .thenReturn(buildApplicationInfo(PKG_NAME_C));
         when(mNetworkManager.isBandwidthControlEnabled()).thenReturn(true);
         when(mNetworkManager.setDataSaverModeEnabled(anyBoolean())).thenReturn(true);
+        doNothing().when(mConnectivityManager)
+                .registerNetworkCallback(any(), mNetworkCallbackCaptor.capture());
 
         // Prepare NPMS.
         mService.systemReady(mService.networkScoreAndNetworkManagementServiceReady());
@@ -821,11 +834,11 @@
     private static long computeLastCycleBoundary(long currentTime, NetworkPolicy policy) {
         RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
                 ZoneId.systemDefault());
-        final Iterator<Pair<ZonedDateTime, ZonedDateTime>> it = policy.cycleIterator();
+        final Iterator<Range<ZonedDateTime>> it = policy.cycleIterator();
         while (it.hasNext()) {
-            final Pair<ZonedDateTime, ZonedDateTime> cycle = it.next();
-            if (cycle.first.toInstant().toEpochMilli() < currentTime) {
-                return cycle.first.toInstant().toEpochMilli();
+            final Range<ZonedDateTime> cycle = it.next();
+            if (cycle.getLower().toInstant().toEpochMilli() < currentTime) {
+                return cycle.getLower().toInstant().toEpochMilli();
             }
         }
         throw new IllegalStateException(
@@ -835,7 +848,7 @@
     private static long computeNextCycleBoundary(long currentTime, NetworkPolicy policy) {
         RecurrenceRule.sClock = Clock.fixed(Instant.ofEpochMilli(currentTime),
                 ZoneId.systemDefault());
-        return policy.cycleIterator().next().second.toInstant().toEpochMilli();
+        return policy.cycleIterator().next().getUpper().toInstant().toEpochMilli();
     }
 
     @Test
@@ -1015,10 +1028,8 @@
         mService.updateNetworks();
 
         // Define simple data plan
-        final SubscriptionPlan plan = SubscriptionPlan.Builder
-                .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
-                .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
-                .build();
+        final SubscriptionPlan plan = buildMonthlyDataPlan(
+                ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
         mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
@@ -1122,10 +1133,8 @@
         mService.updateNetworks();
 
         // Define simple data plan which gives us effectively 60MB/day
-        final SubscriptionPlan plan = SubscriptionPlan.Builder
-                .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
-                .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
-                .build();
+        final SubscriptionPlan plan = buildMonthlyDataPlan(
+                ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
         mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
@@ -1497,21 +1506,13 @@
         final NetworkStatsHistory history = new NetworkStatsHistory(TimeUnit.HOURS.toMillis(1));
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
         when(mStatsService.getNetworkTotalBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<Long>() {
-                    @Override
-                    public Long answer(InvocationOnMock invocation) throws Throwable {
-                        final NetworkStatsHistory.Entry entry = history.getValues(
-                                invocation.getArgument(1), invocation.getArgument(2), null);
-                        return entry.rxBytes + entry.txBytes;
-                    }
+                .thenAnswer(invocation -> {
+                    final NetworkStatsHistory.Entry entry = history.getValues(
+                            invocation.getArgument(1), invocation.getArgument(2), null);
+                    return entry.rxBytes + entry.txBytes;
                 });
         when(mStatsService.getNetworkUidBytes(any(), anyLong(), anyLong()))
-                .thenAnswer(new Answer<NetworkStats>() {
-                    @Override
-                    public NetworkStats answer(InvocationOnMock invocation) throws Throwable {
-                        return stats;
-                    }
-                });
+                .thenReturn(stats);
 
         // Get active mobile network in place
         expectMobileDefaults();
@@ -1535,17 +1536,18 @@
             mService.updateNetworks();
 
             // No quotas
-            assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
-            assertEquals(-1, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+            assertEquals(OPPORTUNISTIC_QUOTA_UNKNOWN,
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(OPPORTUNISTIC_QUOTA_UNKNOWN,
+                    internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
         }
 
         // Limited data plan
         {
-            final SubscriptionPlan plan = SubscriptionPlan.Builder
-                    .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
-                    .setDataLimit(DataUnit.MEGABYTES.toBytes(1800), LIMIT_BEHAVIOR_DISABLED)
-                    .build();
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+            final SubscriptionPlan plan = buildMonthlyDataPlan(
+                    ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
+                    DataUnit.MEGABYTES.toBytes(1800));
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1561,13 +1563,45 @@
                     internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
         }
 
+        // Limited data plan, over quota
+        {
+            final SubscriptionPlan plan = buildMonthlyDataPlan(
+                    ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
+                    DataUnit.MEGABYTES.toBytes(100));
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+                    mServiceContext.getOpPackageName());
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+
+            mService.updateNetworks();
+
+            assertEquals(0L, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(0L, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+        }
+
+        // Roaming
+        {
+            final SubscriptionPlan plan = buildMonthlyDataPlan(
+                    ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+                    mServiceContext.getOpPackageName());
+
+            reset(mTelephonyManager, mNetworkManager, mNotifManager);
+            expectMobileDefaults();
+            expectNetworkState(true /* roaming */);
+
+            mService.updateNetworks();
+
+            assertEquals(0L, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
+            assertEquals(0L, internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+        }
+
         // Unlimited data plan
         {
-            final SubscriptionPlan plan = SubscriptionPlan.Builder
-                    .createRecurringMonthly(ZonedDateTime.parse("2015-11-01T00:00:00.00Z"))
-                    .setDataLimit(BYTES_UNLIMITED, LIMIT_BEHAVIOR_DISABLED)
-                    .build();
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+            final SubscriptionPlan plan = buildMonthlyDataPlan(
+                    ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
+            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1580,9 +1614,27 @@
                     internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_JOBS));
             assertEquals(DataUnit.MEBIBYTES.toBytes(10),
                     internal.getSubscriptionOpportunisticQuota(net, QUOTA_TYPE_MULTIPATH));
+
+            // Capabilities change to roaming
+            final ConnectivityManager.NetworkCallback callback = mNetworkCallbackCaptor.getValue();
+            assertNotNull(callback);
+            expectNetworkState(true /* roaming */);
+            callback.onCapabilitiesChanged(
+                    new Network(TEST_NET_ID),
+                    buildNetworkCapabilities(TEST_SUB_ID, true /* roaming */));
+
+            assertEquals(0, internal.getSubscriptionOpportunisticQuota(
+                    new Network(TEST_NET_ID), NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH));
         }
     }
 
+    private SubscriptionPlan buildMonthlyDataPlan(ZonedDateTime start, long limitBytes) {
+        return SubscriptionPlan.Builder
+                .createRecurringMonthly(start)
+                .setDataLimit(limitBytes, LIMIT_BEHAVIOR_DISABLED)
+                .build();
+    }
+
     private ApplicationInfo buildApplicationInfo(String label) {
         final ApplicationInfo ai = new ApplicationInfo();
         ai.nonLocalizedLabel = label;
@@ -1602,9 +1654,12 @@
         return lp;
     }
 
-    private NetworkCapabilities buildNetworkCapabilities(int subId) {
+    private NetworkCapabilities buildNetworkCapabilities(int subId, boolean roaming) {
         final NetworkCapabilities nc = new NetworkCapabilities();
         nc.addTransportType(TRANSPORT_CELLULAR);
+        if (!roaming) {
+            nc.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
         nc.setNetworkSpecifier(new StringNetworkSpecifier(String.valueOf(subId)));
         return nc;
     }
@@ -1658,16 +1713,20 @@
                 hasIt ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED);
     }
 
+    private void expectNetworkState(boolean roaming) throws Exception {
+        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
+                new NetworkState(buildNetworkInfo(),
+                        buildLinkProperties(TEST_IFACE),
+                        buildNetworkCapabilities(TEST_SUB_ID, roaming),
+                        new Network(TEST_NET_ID), TEST_IMSI, null)
+        });
+    }
+
     private void expectMobileDefaults() throws Exception {
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
                 new int[] { TEST_SUB_ID });
         when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
-        when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[] {
-                new NetworkState(buildNetworkInfo(),
-                        buildLinkProperties(TEST_IFACE),
-                        buildNetworkCapabilities(TEST_SUB_ID),
-                        new Network(TEST_NET_ID), TEST_IMSI, null)
-        });
+        expectNetworkState(false /* roaming */);
     }
 
     private void verifyAdvisePersistThreshold() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 9663a1b..08b8af2 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -23,6 +23,9 @@
 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.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -287,4 +290,43 @@
         // Verify that the stack was removed.
         assertEquals(originalStackCount, defaultDisplay.getChildCount());
     }
+
+    @Test
+    public void testFocusability() throws Exception {
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(stack).build();
+
+        // Under split screen primary we should be focusable when not minimized
+        mService.mStackSupervisor.setDockedStackMinimized(false);
+        assertTrue(stack.isFocusable());
+        assertTrue(activity.isFocusable());
+
+        // Under split screen primary we should not be focusable when minimized
+        mService.mStackSupervisor.setDockedStackMinimized(true);
+        assertFalse(stack.isFocusable());
+        assertFalse(activity.isFocusable());
+
+        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(pinnedStack).build();
+
+        // We should not be focusable when in pinned mode
+        assertFalse(pinnedStack.isFocusable());
+        assertFalse(pinnedActivity.isFocusable());
+
+        // Add flag forcing focusability.
+        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
+
+        // We should not be focusable when in pinned mode
+        assertTrue(pinnedStack.isFocusable());
+        assertTrue(pinnedActivity.isFocusable());
+
+        // Without the overridding activity, stack should not be focusable.
+        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
+                REMOVE_TASK_MODE_DESTROYING);
+        assertFalse(pinnedStack.isFocusable());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index c78fcd3..4b8dcc1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -103,7 +103,42 @@
         assertEquals(mStack.getResumedActivity(), r);
         r.setState(PAUSING, "testResumedActivity");
         assertEquals(mStack.getResumedActivity(), null);
+    }
 
+    @Test
+    public void testResumedActivityFromTaskReparenting() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        // Ensure moving task between two stacks updates resumed activity
+        r.setState(RESUMED, "testResumedActivityFromTaskReparenting");
+        assertEquals(mStack.getResumedActivity(), r);
+
+        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+
+        mTask.reparent(destStack, true /* toTop */, TaskRecord.REPARENT_KEEP_STACK_AT_FRONT,
+                false /* animate */, true /* deferResume*/,
+                "testResumedActivityFromTaskReparenting");
+
+        assertEquals(mStack.getResumedActivity(), null);
+        assertEquals(destStack.getResumedActivity(), r);
+    }
+
+    @Test
+    public void testResumedActivityFromActivityReparenting() {
+        final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
+        // Ensure moving task between two stacks updates resumed activity
+        r.setState(RESUMED, "testResumedActivityFromActivityReparenting");
+        assertEquals(mStack.getResumedActivity(), r);
+
+        final ActivityStack destStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
+
+        mTask.removeActivity(r);
+        destTask.addActivityToTop(r);
+
+        assertEquals(mStack.getResumedActivity(), null);
+        assertEquals(destStack.getResumedActivity(), r);
     }
 
     @Test
@@ -543,5 +578,4 @@
 
         assertEquals(expected, mStack.shouldSleepActivities());
     }
-
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
index d012bba..18e842f 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStarterTests.java
@@ -23,6 +23,7 @@
 import static android.app.ActivityManager.START_INTENT_NOT_RESOLVED;
 import static android.app.ActivityManager.START_NOT_VOICE_COMPATIBLE;
 import static android.app.ActivityManager.START_PERMISSION_DENIED;
+import static android.app.ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_SWITCHES_CANCELED;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
@@ -34,6 +35,7 @@
 
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ActivityInfo.WindowLayout;
@@ -74,6 +76,8 @@
 import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
 import com.android.server.am.TaskRecord.TaskRecordFactory;
 
+import java.util.ArrayList;
+
 /**
  * Tests for the {@link ActivityStarter} class.
  *
@@ -301,13 +305,14 @@
                 anyBoolean(), any(), any(), any());
 
         // instrument the stack and task used.
-        final ActivityStack stack = spy(mService.mStackSupervisor.getDefaultDisplay().createStack(
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */));
-        final TaskRecord task =
-                spy(new TaskBuilder(mService.mStackSupervisor).setStack(stack).build());
+        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor)
+                .setCreateStack(false)
+                .build();
 
         // supervisor needs a focused stack.
-        mService.mStackSupervisor.mFocusedStack = task.getStack();
+        mService.mStackSupervisor.mFocusedStack = stack;
 
         // use factory that only returns spy task.
         final TaskRecordFactory factory = mock(TaskRecordFactory.class);
@@ -322,14 +327,6 @@
         doReturn(stack).when(mService.mStackSupervisor)
                 .getLaunchStack(any(), any(), any(), anyBoolean(), anyInt());
 
-        // ignore the start request.
-        doNothing().when(stack)
-                .startActivityLocked(any(), any(), anyBoolean(), anyBoolean(), any());
-
-        // ignore requests to create window container.
-        doNothing().when(task).createWindowContainer(anyBoolean(), anyBoolean());
-
-
         final Intent intent = new Intent();
         intent.addFlags(launchFlags);
         intent.setComponent(ActivityBuilder.getDefaultComponent());
@@ -448,4 +445,30 @@
         // Ensure result is moving task to front.
         assertEquals(result, START_TASK_TO_FRONT);
     }
+
+    /**
+     * Tests activity is cleaned up properly in a task mode violation.
+     */
+    @Test
+    public void testTaskModeViolation() {
+        final ActivityDisplay display = mService.mStackSupervisor.getDefaultDisplay();
+        assertNoTasks(display);
+
+        final ActivityStarter starter = prepareStarter(0);
+
+        final LockTaskController lockTaskController = mService.getLockTaskController();
+        doReturn(true).when(lockTaskController).isLockTaskModeViolation(any());
+
+        final int result = starter.setReason("testTaskModeViolation").execute();
+
+        assertEquals(START_RETURN_LOCK_TASK_MODE_VIOLATION, result);
+        assertNoTasks(display);
+    }
+
+    private void assertNoTasks(ActivityDisplay display) {
+        for (int i = display.getChildCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = display.getChildAt(i);
+            assertTrue(stack.getAllTasks().isEmpty());
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 741901d..f5e61a1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -30,6 +30,7 @@
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.spy;
 
+import android.app.ActivityOptions;
 import com.android.server.wm.DisplayWindowController;
 
 import org.junit.Rule;
@@ -51,6 +52,9 @@
 import android.support.test.InstrumentationRegistry;
 import android.testing.DexmakerShareClassLoaderRule;
 
+
+import com.android.internal.app.IVoiceInteractor;
+
 import com.android.server.AttributeCache;
 import com.android.server.wm.AppWindowContainerController;
 import com.android.server.wm.PinnedStackWindowController;
@@ -62,6 +66,7 @@
 import org.junit.Before;
 import org.mockito.MockitoAnnotations;
 
+
 /**
  * A base class to handle common operations in activity related unit tests.
  */
@@ -215,6 +220,7 @@
         private int mTaskId = 0;
         private int mUserId = 0;
         private IVoiceInteractionSession mVoiceSession;
+        private boolean mCreateStack = true;
 
         private ActivityStack mStack;
 
@@ -232,6 +238,15 @@
             return this;
         }
 
+        /**
+         * Set to {@code true} by default, set to {@code false} to prevent the task from
+         * automatically creating a parent stack.
+         */
+        TaskBuilder setCreateStack(boolean createStack) {
+            mCreateStack = createStack;
+            return this;
+        }
+
         TaskBuilder setVoiceSession(IVoiceInteractionSession session) {
             mVoiceSession = session;
             return this;
@@ -258,7 +273,7 @@
         }
 
         TaskRecord build() {
-            if (mStack == null) {
+            if (mStack == null && mCreateStack) {
                 mStack = mSupervisor.getDefaultDisplay().createStack(
                         WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
             }
@@ -276,17 +291,38 @@
             intent.setComponent(mComponent);
             intent.setFlags(mFlags);
 
-            final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
+            final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
                     intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
             task.userId = mUserId;
-            mSupervisor.setFocusStackUnchecked("test", mStack);
-            mStack.addTask(task, true, "creating test task");
-            task.setStack(mStack);
-            task.setWindowContainerController(mock(TaskWindowContainerController.class));
+
+            if (mStack != null) {
+                mSupervisor.setFocusStackUnchecked("test", mStack);
+                mStack.addTask(task, true, "creating test task");
+                task.setStack(mStack);
+                task.setWindowContainerController();
+            }
+
             task.touchActiveTime();
 
             return task;
         }
+
+        private static class TestTaskRecord extends TaskRecord {
+            TestTaskRecord(ActivityManagerService service, int _taskId, ActivityInfo info,
+                       Intent _intent, IVoiceInteractionSession _voiceSession,
+                       IVoiceInteractor _voiceInteractor) {
+                super(service, _taskId, info, _intent, _voiceSession, _voiceInteractor);
+            }
+
+            @Override
+            void createWindowContainer(boolean onTop, boolean showForAllUsers) {
+                setWindowContainerController();
+            }
+
+            private void setWindowContainerController() {
+                setWindowContainerController(mock(TaskWindowContainerController.class));
+            }
+        }
     }
 
     /**
@@ -295,6 +331,7 @@
      */
     protected static class TestActivityManagerService extends ActivityManagerService {
         private ClientLifecycleManager mLifecycleManager;
+        private LockTaskController mLockTaskController;
 
         TestActivityManagerService(Context context) {
             super(context);
@@ -314,6 +351,14 @@
             return mLifecycleManager;
         }
 
+        public LockTaskController getLockTaskController() {
+            if (mLockTaskController == null) {
+                mLockTaskController = spy(super.getLockTaskController());
+            }
+
+            return mLockTaskController;
+        }
+
         void setLifecycleManager(ClientLifecycleManager manager) {
             mLifecycleManager = manager;
         }
@@ -444,7 +489,7 @@
     }
 
     /**
-     * Overrided of {@link ActivityStack} that tracks test metrics, such as the number of times a
+     * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
      * method is called. Note that its functionality depends on the implementations of the
      * construction arguments.
      */
@@ -530,5 +575,11 @@
                     return super.supportsSplitScreenWindowingMode();
             }
         }
+
+        @Override
+        void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
+                                 boolean newTask, boolean keepCurTransition,
+                                 ActivityOptions options) {
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 1192114..592f7b1 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -39,6 +39,7 @@
 
 import static java.lang.Integer.MAX_VALUE;
 
+import android.annotation.TestApi;
 import android.app.ActivityManager;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -594,6 +595,23 @@
     }
 
     @Test
+    public void testRemovePackageByName() throws Exception {
+        // Add a number of tasks with the same package name
+        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task1").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg2", ".Task2").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg3", ".Task3").build());
+        mRecentTasks.add(createTaskBuilder("com.android.pkg1", ".Task4").build());
+        mRecentTasks.removeTasksByPackageName("com.android.pkg1", TEST_USER_0_ID);
+
+        final ArrayList<TaskRecord> tasks = mRecentTasks.getRawTasks();
+        for (int i = 0; i < tasks.size(); i++) {
+            if (tasks.get(i).intent.getComponent().getPackageName().equals("com.android.pkg1")) {
+                fail("Expected com.android.pkg1 tasks to be removed");
+            }
+        }
+    }
+
+    @Test
     public void testNotRecentsComponent_denyApiAccess() throws Exception {
         doReturn(PackageManager.PERMISSION_DENIED).when(mService).checkPermission(anyString(),
                 anyInt(), anyInt());
@@ -694,8 +712,12 @@
     }
 
     private TaskBuilder createTaskBuilder(String className) {
+        return createTaskBuilder(mContext.getPackageName(), className);
+    }
+
+    private TaskBuilder createTaskBuilder(String packageName, String className) {
         return new TaskBuilder(mService.mStackSupervisor)
-                .setComponent(new ComponentName(mContext.getPackageName(), className))
+                .setComponent(new ComponentName(packageName, className))
                 .setStack(mStack)
                 .setTaskId(LAST_TASK_ID++)
                 .setUserId(TEST_USER_0_ID);
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index d37db20..4f18be7 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -26,7 +26,9 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Process;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -395,7 +397,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
@@ -409,7 +417,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
@@ -425,7 +439,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[0][0];
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
@@ -440,7 +454,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = null;
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
@@ -453,7 +467,7 @@
     public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signingCertificateHistory = null;
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
@@ -467,7 +481,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[0][0];
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
@@ -484,9 +498,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -503,9 +521,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -522,9 +544,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {signature1Copy, signature2Copy}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {signature1Copy, signature2Copy},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -541,9 +567,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -560,7 +590,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
@@ -579,7 +615,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
+                        new int[] {0, 0}));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know signature1Copy is in history, and we want to assume it has
@@ -601,7 +643,13 @@
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
+                        new int[] {0, 0}));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know signature1Copy is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 5f052ce..2830a74 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -41,7 +41,9 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.os.Bundle;
 import android.os.Process;
 import android.platform.test.annotations.Presubmit;
@@ -371,7 +373,13 @@
         packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
         RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
@@ -402,7 +410,13 @@
                 ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
         packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
         packageInfo.applicationInfo.backupAgentName = "backup.agent";
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
         doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
@@ -434,7 +448,13 @@
                 ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
         doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
@@ -469,7 +489,13 @@
         packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.versionCode = 2;
         PackageManagerStub.sPackageInfo = packageInfo;
 
@@ -507,7 +533,13 @@
         packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.versionCode = 1;
         PackageManagerStub.sPackageInfo = packageInfo;
 
@@ -541,7 +573,13 @@
         packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
         packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
         packageInfo.applicationInfo.backupAgentName = null;
-        packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {FAKE_SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.versionCode = 1;
         PackageManagerStub.sPackageInfo = packageInfo;
 
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index fb25cf3..284d443 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -32,7 +32,9 @@
 import android.os.PowerManager;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.MathUtils;
 import android.util.Spline;
+import android.util.Slog;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -91,6 +93,29 @@
     private static final float[] EMPTY_FLOAT_ARRAY = new float[0];
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
+    private static final float MAXIMUM_GAMMA = 3.0f;
+    private static final int[] GAMMA_CORRECTION_LUX = {
+        0,
+        100,
+        1000,
+        2500,
+        4000,
+        4900,
+        5000
+    };
+    private static final float[] GAMMA_CORRECTION_NITS = {
+        1.0f,
+        10.55f,
+        96.5f,
+        239.75f,
+        383.0f,
+        468.95f,
+        478.5f,
+    };
+    private static final Spline GAMMA_CORRECTION_SPLINE = Spline.createSpline(
+            new float[] { 0.0f, 100.0f, 1000.0f, 2500.0f, 4000.0f, 4900.0f, 5000.0f },
+            new float[] { 0.035f, 0.035f, 0.221f, 0.523f, 0.797f, 0.980f, 1.0f });
+
     @Test
     public void testSimpleStrategyMappingAtControlPoints() {
         Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
@@ -325,7 +350,7 @@
         }
 
         // Now set the middle of the lux range to something just above the minimum.
-        final float minBrightness = strategy.getBrightness(LUX_LEVELS[0]);
+        float minBrightness = strategy.getBrightness(LUX_LEVELS[0]);
         strategy.addUserDataPoint(LUX_LEVELS[idx], minBrightness + 0.01f);
 
         // Then make sure the curve is still monotonic.
@@ -340,7 +365,8 @@
         // And that the lowest lux level still gives the absolute minimum brightness. This should
         // be true assuming that there are more than two lux levels in the curve since we picked a
         // brightness just barely above the minimum for the middle of the curve.
-        assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.001 /*tolerance*/);
+        minBrightness = (float) MathUtils.pow(minBrightness, MAXIMUM_GAMMA); // Gamma correction.
+        assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.01 /*tolerance*/);
     }
 
     private static float[] toFloatArray(int[] vals) {
@@ -396,6 +422,9 @@
         when(mockResources.getInteger(
                 com.android.internal.R.integer.config_screenBrightnessSettingMaximum))
                 .thenReturn(255);
+        when(mockResources.getFraction(
+                com.android.internal.R.fraction.config_autoBrightnessAdjustmentMaxGamma, 1, 1))
+                .thenReturn(MAXIMUM_GAMMA);
         return mockResources;
     }
 
@@ -419,4 +448,121 @@
         return mockArray;
     }
 
+    // Gamma correction tests.
+    // x0 = 100   y0 = ~0.01
+    // x1 = 1000  y1 = ~0.20
+    // x2 = 2500  y2 = ~0.50
+    // x3 = 4000  y3 = ~0.80
+    // x4 = 4900  y4 = ~0.99
+
+    @Test
+    public void testGammaCorrectionLowChangeAtCenter() {
+        // If we set a user data point at (x2, y2^0.5), i.e. gamma = 0.5, it should bump the rest
+        // of the spline accordingly.
+        final int x1 = 1000;
+        final int x2 = 2500;
+        final int x3 = 4000;
+        final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
+        final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+        final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        // Let's start with a sanity check:
+        assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+        // OK, let's roll:
+        float gamma = 0.5f;
+        strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
+        assertEquals(MathUtils.pow(y1, gamma), strategy.getBrightness(x1), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y3, gamma), strategy.getBrightness(x3), 0.01f /* tolerance */);
+        // The adjustment should be +0.63 (manual calculation).
+        assertEquals(+0.63f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+    }
+
+    @Test
+    public void testGammaCorrectionHighChangeAtCenter() {
+        // This time we set a user data point at (x2, y2^0.25), i.e. gamma = 0.3 (the minimum),
+        // which should bump the rest of the spline accordingly, and further correct x2 to hit
+        // y2^0.25 (not y2^0.3).
+        final int x1 = 1000;
+        final int x2 = 2500;
+        final int x3 = 4000;
+        final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
+        final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+        final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        // Sanity check:
+        assertEquals(y1, strategy.getBrightness(x1), 0.01f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(y3, strategy.getBrightness(x3), 0.01f /* tolerance */);
+        // Let's roll:
+        float gamma = 0.25f;
+        final float minGamma = 1.0f / MAXIMUM_GAMMA;
+        strategy.addUserDataPoint(x2, (float) MathUtils.pow(y2, gamma));
+        assertEquals(MathUtils.pow(y1, minGamma), strategy.getBrightness(x1),
+                0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma),    strategy.getBrightness(x2),
+                0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y3, minGamma), strategy.getBrightness(x3),
+                0.01f /* tolerance */);
+        // The adjustment should be +1.0 (maximum adjustment).
+        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+    }
+
+    @Test
+    public void testGammaCorrectionExtremeChangeAtCenter() {
+        // Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
+        // just make sure the adjustment reflects the change.
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        strategy.addUserDataPoint(2500, 1.0f);
+        assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        strategy.addUserDataPoint(2500, 0.0f);
+        assertEquals(-1.0f, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+    }
+
+    @Test
+    public void testGammaCorrectionChangeAtEdges() {
+        // The algorithm behaves differently at the edges, because gamma correction there tends to
+        // be extreme. If we add a user data point at (x0, y0+0.3), the adjustment should be
+        // 0.3*2 = 0.6, resulting in a gamma of 3**-0.6 = ~0.52.
+        final int x0 = 100;
+        final int x2 = 2500;
+        final int x4 = 4900;
+        final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
+        final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
+        final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
+        Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS,
+                DISPLAY_LEVELS_NITS, DISPLAY_LEVELS_BACKLIGHT);
+        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources);
+        // Sanity, as per tradition:
+        assertEquals(y0, strategy.getBrightness(x0), 0.01f /* tolerance */);
+        assertEquals(y2, strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(y4, strategy.getBrightness(x4), 0.01f /* tolerance */);
+        // Rollin':
+        float increase = 0.3f;
+        float adjustment = increase * 2;
+        float gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
+        strategy.addUserDataPoint(x0, y0 + increase);
+        assertEquals(y0 + increase, strategy.getBrightness(x0), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y4, gamma), strategy.getBrightness(x4), 0.01f /* tolerance */);
+        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+        // Similarly, if we set a user data point at (x4, 1.0), the adjustment should be (1-y4)*2.
+        increase = 1.0f - y4;
+        adjustment = increase * 2;
+        gamma = (float) MathUtils.pow(MAXIMUM_GAMMA, -adjustment);
+        strategy.addUserDataPoint(x4, 1.0f);
+        assertEquals(MathUtils.pow(y0, gamma), strategy.getBrightness(x0), 0.01f /* tolerance */);
+        assertEquals(MathUtils.pow(y2, gamma), strategy.getBrightness(x2), 0.01f /* tolerance */);
+        assertEquals(1.0f, strategy.getBrightness(x4), 0.01f /* tolerance */);
+        assertEquals(adjustment, strategy.getAutoBrightnessAdjustment(), 0.01f /* tolerance */);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 2f2afd7..ceee60c 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -17,12 +17,15 @@
 package com.android.server.display;
 
 import android.content.Context;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.Curve;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayViewport;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.input.InputManagerInternal;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.view.SurfaceControl;
@@ -226,6 +229,62 @@
                 + " virtual display adapter");
     }
 
+    /**
+     * Tests that an exception is raised for too dark a brightness configuration.
+     */
+    public void testTooDarkBrightnessConfigurationThrowException() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        Curve minimumBrightnessCurve = displayManager.getMinimumBrightnessCurveInternal();
+        float[] lux = minimumBrightnessCurve.getX();
+        float[] minimumNits = minimumBrightnessCurve.getY();
+        float[] nits = new float[minimumNits.length];
+        // For every control point, assert that making it slightly lower than the minimum throws an
+        // exception.
+        for (int i = 0; i < nits.length; i++) {
+            for (int j = 0; j < nits.length; j++) {
+                nits[j] = minimumNits[j];
+                if (j == i) {
+                    nits[j] -= 0.1f;
+                }
+                if (nits[j] < 0) {
+                    nits[j] = 0;
+                }
+            }
+            BrightnessConfiguration config =
+                    new BrightnessConfiguration.Builder(lux, nits).build();
+            Exception thrown = null;
+            try {
+                displayManager.validateBrightnessConfiguration(config);
+            } catch (IllegalArgumentException e) {
+                thrown = e;
+            }
+            assertNotNull("Building too dark a brightness configuration must throw an exception");
+        }
+    }
+
+    /**
+     * Tests that no exception is raised for not too dark a brightness configuration.
+     */
+    public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        Curve minimumBrightnessCurve = displayManager.getMinimumBrightnessCurveInternal();
+        float[] lux = minimumBrightnessCurve.getX();
+        float[] nits = minimumBrightnessCurve.getY();
+        BrightnessConfiguration config = new BrightnessConfiguration.Builder(lux, nits).build();
+        displayManager.validateBrightnessConfiguration(config);
+    }
+
+    /**
+     * Tests that null brightness configurations are alright.
+     */
+    public void testNullBrightnessConfiguration() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        displayManager.validateBrightnessConfiguration(null);
+    }
+
     private void registerDefaultDisplays(DisplayManagerService displayManager) {
         Handler handler = displayManager.getDisplayHandler();
         // Would prefer to call displayManager.onStart() directly here but it performs binderService
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 ee83b62..998ffa0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -58,11 +58,13 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -102,6 +104,8 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1039,8 +1043,13 @@
         pi.versionCode = version;
         pi.applicationInfo.setVersionCode(version);
         pi.signatures = null;
-        pi.signingCertificateHistory = new Signature[][] {genSignatures(signatures)};
-
+        pi.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        genSignatures(signatures),
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         return pi;
     }
 
@@ -1128,7 +1137,7 @@
 
         if (getSignatures) {
             ret.signatures = null;
-            ret.signingCertificateHistory = pi.signingCertificateHistory;
+            ret.signingInfo = pi.signingInfo;
         }
 
         return ret;
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
index cd7feea..203b2ca 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest5.java
@@ -76,13 +76,13 @@
                 mMyPackage, mMyUserId, /*signature*/ false);
         assertEquals(mMyPackage, pi.packageName);
         assertNull(pi.signatures);
-        assertNull(pi.signingCertificateHistory);
+        assertNull(pi.signingInfo);
 
         pi = mShortcutService.getPackageInfo(
                 mMyPackage, mMyUserId, /*signature*/ true);
         assertEquals(mMyPackage, pi.packageName);
         assertNull(pi.signatures);
-        assertNotNull(pi.signingCertificateHistory);
+        assertNotNull(pi.signingInfo);
 
         pi = mShortcutService.getPackageInfo(
                 "no.such.package", mMyUserId, /*signature*/ 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 36e4753..c5cd0a3 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -242,7 +242,7 @@
     }
 
     @Test
-    public void testIsPackageSuspended() {
+    public void testIsPackageSuspended() throws Exception {
         suspendTestPackage(null, null, null);
         assertTrue("isPackageSuspended is false",
                 mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
@@ -297,7 +297,7 @@
                 intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
         final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
                 "2", 0.2);
-        mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
+        suspendTestPackage(extras2, null, null);
         intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
         assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
                 ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
@@ -370,8 +370,8 @@
             }
 
             @Override
-            public void onPackagesSuspended(String[] packageNames, Bundle launcherExtras,
-                    UserHandle user) {
+            public void onPackagesSuspended(String[] packageNames, UserHandle user,
+                    Bundle launcherExtras) {
                 final StringBuilder errorString = new StringBuilder();
                 if (!Arrays.equals(packageNames, PACKAGES_TO_SUSPEND)) {
                     errorString.append("Received unexpected packageNames in onPackagesSuspended:");
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 1ac7cb8..caa1d02 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -27,8 +27,10 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManagerInternal;
+import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.Package;
 import android.content.pm.Signature;
+import android.content.pm.SigningInfo;
 import android.test.MoreAsserts;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
@@ -94,7 +96,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = BackupUtils.signaturesMatch(null, packageInfo,
@@ -108,7 +116,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -125,7 +139,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[0][0];
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -142,7 +156,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = null;
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -158,7 +172,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = null;
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = BackupUtils.signaturesMatch(null, packageInfo,
@@ -172,7 +186,7 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[0][0];
+        packageInfo.signingInfo = null;
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -186,9 +200,13 @@
     public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -205,9 +223,13 @@
     public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -223,7 +245,13 @@
     public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1, SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -240,9 +268,13 @@
     public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {
-                {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
-        };
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         ArrayList<byte[]> storedSigHashes = new ArrayList<>();
@@ -260,7 +292,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
@@ -279,7 +317,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know SIGNATURE_1 is in history, and we want to assume it has
@@ -301,7 +345,13 @@
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.packageName = "test";
-        packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
+        packageInfo.signingInfo = new SigningInfo(
+                new PackageParser.SigningDetails(
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
+                        PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
+                        null,
+                        null,
+                        null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know SIGNATURE_1 is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
index b6be3a4..97a716f 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerLayoutTest.java
@@ -22,6 +22,7 @@
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -29,6 +30,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 
 import static org.hamcrest.Matchers.equalTo;
 import static org.hamcrest.Matchers.is;
@@ -260,6 +262,23 @@
     }
 
     @Test
+    public void layoutWindowLw_withDisplayCutout_floatingInScreen() {
+        addDisplayCutout();
+
+        mAppWindow.attrs.flags = FLAG_LAYOUT_IN_SCREEN;
+        mAppWindow.attrs.type = TYPE_APPLICATION_OVERLAY;
+        mAppWindow.attrs.width = DISPLAY_WIDTH;
+        mAppWindow.attrs.height = DISPLAY_HEIGHT;
+        mPolicy.addWindow(mAppWindow);
+
+        mPolicy.beginLayoutLw(mFrames, 0 /* UI mode */);
+        mPolicy.layoutWindowLw(mAppWindow, null, mFrames);
+
+        assertInsetByTopBottom(mAppWindow.parentFrame, 0, NAV_BAR_HEIGHT);
+        assertInsetByTopBottom(mAppWindow.displayFrame, STATUS_BAR_HEIGHT, NAV_BAR_HEIGHT);
+    }
+
+    @Test
     public void layoutWindowLw_withDisplayCutout_fullscreenInCutout_landscape() {
         addDisplayCutout();
         setRotation(ROTATION_90);
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index 195dd39..2c47a94 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -229,7 +229,8 @@
         void addWindow(WindowState state) {
             if (state instanceof FakeWindowState) {
                 ((FakeWindowState) state).surfaceLayer =
-                        getWindowLayerFromTypeLw(state.getAttrs().type);
+                        getWindowLayerFromTypeLw(state.getAttrs().type,
+                                true /* canAddInternalSystemWindow */);
             }
             adjustWindowParamsLw(state, state.getAttrs(), true /* hasStatusBarPermission */);
             assertEquals(WindowManagerGlobal.ADD_OKAY, prepareAddWindowLw(state, state.getAttrs()));
diff --git a/services/tests/uiservicestests/AndroidTest.xml b/services/tests/uiservicestests/AndroidTest.xml
index d3b9d4a..11e8f09 100644
--- a/services/tests/uiservicestests/AndroidTest.xml
+++ b/services/tests/uiservicestests/AndroidTest.xml
@@ -24,5 +24,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.tests.uiservices" />
         <option name="runner" value="android.testing.TestableInstrumentation" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 3acc277..d02a983 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -201,12 +201,12 @@
                 AudioAttributes.USAGE_MEDIA);
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
                 AudioAttributes.USAGE_GAME);
+        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(false,
+                AudioAttributes.USAGE_UNKNOWN);
 
         // Alarms only will silence system noises (but not vibrations)
         verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
                 AudioAttributes.USAGE_ASSISTANCE_SONIFICATION, AppOpsManager.OP_PLAY_AUDIO);
-        verify(mZenModeHelperSpy, atLeastOnce()).applyRestrictions(true,
-                AudioAttributes.USAGE_UNKNOWN);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
new file mode 100644
index 0000000..1efa415
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceClientPermissionsTest.java
@@ -0,0 +1,256 @@
+/*
+ * 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.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+import com.android.server.slice.SliceClientPermissions.SliceAuthority;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SliceClientPermissionsTest extends UiServiceTestCase {
+
+    @Test
+    public void testRemoveBasic() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+        Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("com.android.pkg.slices").build();
+
+        PkgUser testPkg = new PkgUser("other", 2);
+
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .build(), testPkg);
+        client.revokeUri(base.buildUpon()
+                .appendPath("first")
+                .build(), testPkg);
+
+        assertFalse(client.hasPermission(base.buildUpon()
+                .appendPath("first")
+                .appendPath("third")
+                .build(), testPkg.getUserId()));
+
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+        assertEquals(0, authorities.get(0).getPaths().size());
+    }
+
+    @Test
+    public void testRemoveSubtrees() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+        Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("com.android.pkg.slices").build();
+
+        PkgUser testPkg = new PkgUser("other", 2);
+
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .appendPath("second")
+                .build(), testPkg);
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .appendPath("third")
+                .build(), testPkg);
+        client.revokeUri(base.buildUpon()
+                .appendPath("first")
+                .build(), testPkg);
+
+        assertFalse(client.hasPermission(base.buildUpon()
+                .appendPath("first")
+                .appendPath("fourth")
+                .build(), testPkg.getUserId()));
+
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+        assertEquals(0, authorities.get(0).getPaths().size());
+    }
+
+    @Test
+    public void testAddConsolidate_addFirst() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+        Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("com.android.pkg.slices").build();
+
+        PkgUser testPkg = new PkgUser("other", 2);
+
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .build(), testPkg);
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .appendPath("second")
+                .build(), testPkg);
+
+        assertTrue(client.hasPermission(base.buildUpon()
+                .appendPath("first")
+                .appendPath("third")
+                .build(), testPkg.getUserId()));
+
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+        assertEquals(1, authorities.get(0).getPaths().size());
+    }
+
+    @Test
+    public void testAddConsolidate_addSecond() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+        Uri base = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("com.android.pkg.slices").build();
+
+        PkgUser testPkg = new PkgUser("other", 2);
+
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .appendPath("second")
+                .build(), testPkg);
+        client.grantUri(base.buildUpon()
+                .appendPath("first")
+                .build(), testPkg);
+
+        assertTrue(client.hasPermission(base.buildUpon()
+                .appendPath("first")
+                .appendPath("third")
+                .build(), testPkg.getUserId()));
+
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+        assertEquals(1, authorities.get(0).getPaths().size());
+    }
+
+    @Test
+    public void testDirty_addAuthority() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+        client.getOrCreateAuthority(new PkgUser("some_auth", 2), new PkgUser("com.pkg", 2));
+
+        verify(tracker).onPersistableDirty(eq(client));
+    }
+
+    @Test
+    public void testDirty_addPkg() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+        SliceAuthority auth = client.getOrCreateAuthority(
+                new PkgUser("some_auth", 2),
+                new PkgUser("com.pkg", 2));
+        clearInvocations(tracker);
+
+        auth.addPath(Arrays.asList("/something/"));
+
+        verify(tracker).onPersistableDirty(eq(client));
+    }
+
+    @Test
+    public void testCreation() {
+        SliceClientPermissions client = createClient();
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(client.getAuthorities());
+        authorities.sort(Comparator.comparing(SliceAuthority::getAuthority));
+
+        assertEquals(2, authorities.size());
+        assertEquals("com.android.pkg", authorities.get(0).getAuthority());
+        assertEquals("com.android.pkg.slices", authorities.get(1).getAuthority());
+
+        assertEquals(1, authorities.get(0).getPaths().size());
+        assertEquals(2, authorities.get(1).getPaths().size());
+    }
+
+    @Test
+    public void testSerialization() throws XmlPullParserException, IOException {
+        SliceClientPermissions client = createClient();
+        client.setHasFullAccess(true);
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+        serializer.setOutput(output, Encoding.UTF_8.name());
+
+        client.writeTo(serializer);
+        serializer.flush();
+
+        ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+        XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+        parser.setInput(input, Encoding.UTF_8.name());
+
+        SliceClientPermissions deser = SliceClientPermissions.createFrom(parser,
+                mock(DirtyTracker.class));
+
+        assertEquivalent(client, deser);
+    }
+
+    private void assertEquivalent(SliceClientPermissions o1, SliceClientPermissions o2) {
+        assertEquals(o1.getPkg(), o2.getPkg());
+        ArrayList<SliceAuthority> a1 = new ArrayList<>(o1.getAuthorities());
+        ArrayList<SliceAuthority> a2 = new ArrayList<>(o2.getAuthorities());
+        a1.sort(Comparator.comparing(SliceAuthority::getAuthority));
+        a2.sort(Comparator.comparing(SliceAuthority::getAuthority));
+        assertEquals(a1, a2);
+    }
+
+    private static SliceClientPermissions createClient() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 2);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceClientPermissions client = new SliceClientPermissions(pkg, tracker);
+
+        SliceAuthority auth = client.getOrCreateAuthority(
+                new PkgUser("com.android.pkg.slices", 3),
+                new PkgUser("com.android.pkg", 3));
+        auth.addPath(Arrays.asList("/something/"));
+        auth.addPath(Arrays.asList("/something/else"));
+
+        auth = client.getOrCreateAuthority(
+                new PkgUser("com.android.pkg", 3),
+                new PkgUser("com.pkg", 1));
+        auth.addPath(Arrays.asList("/somewhere"));
+        return client;
+    }
+
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
new file mode 100644
index 0000000..5443e73
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SlicePermissionManagerTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.slice;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.net.Uri;
+import android.net.Uri.Builder;
+import android.os.FileUtils;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Log;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SlicePermissionManagerTest extends UiServiceTestCase {
+
+    @Test
+    public void testBackup() throws XmlPullParserException, IOException {
+        File sliceDir = new File(mContext.getDataDir(), "system/slices");
+        Uri uri = new Builder().scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("authority")
+                .path("something").build();
+        SlicePermissionManager permissions = new SlicePermissionManager(mContext,
+                TestableLooper.get(this).getLooper(), sliceDir);
+
+        permissions.grantFullAccess("com.android.mypkg", 10);
+        permissions.grantSliceAccess("com.android.otherpkg", 0, "com.android.lastpkg", 1, uri);
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+        serializer.setOutput(output, Encoding.UTF_8.name());
+
+
+        TestableLooper.get(this).processAllMessages();
+        permissions.writeBackup(serializer);
+        serializer.flush();
+
+        ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+        XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+        parser.setInput(input, Encoding.UTF_8.name());
+
+        permissions = new SlicePermissionManager(mContext,
+                TestableLooper.get(this).getLooper());
+        permissions.readRestore(parser);
+
+        assertTrue(permissions.hasFullAccess("com.android.mypkg", 10));
+        assertTrue(permissions.hasPermission("com.android.otherpkg", 0,
+                ContentProvider.maybeAddUserId(uri, 1)));
+        permissions.removePkg("com.android.lastpkg", 1);
+        assertFalse(permissions.hasPermission("com.android.otherpkg", 0,
+                ContentProvider.maybeAddUserId(uri, 1)));
+
+        // Cleanup.
+        assertTrue(FileUtils.deleteContentsAndDir(sliceDir));
+    }
+
+}
\ No newline at end of file
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.java
new file mode 100644
index 0000000..5775991
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceProviderPermissionsTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.slice;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+import android.util.Xml.Encoding;
+
+import com.android.server.UiServiceTestCase;
+import com.android.server.slice.SlicePermissionManager.PkgUser;
+import com.android.server.slice.SliceProviderPermissions.SliceAuthority;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Comparator;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+public class SliceProviderPermissionsTest extends UiServiceTestCase {
+
+    @Test
+    public void testDirty_addAuthority() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+        provider.getOrCreateAuthority("some_auth");
+
+        verify(tracker).onPersistableDirty(eq(provider));
+    }
+
+    @Test
+    public void testDirty_addPkg() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 0);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+        SliceAuthority auth = provider.getOrCreateAuthority("some_auth");
+        clearInvocations(tracker);
+
+        auth.addPkg(new PkgUser("pkg", 0));
+
+        verify(tracker).onPersistableDirty(eq(provider));
+    }
+
+    @Test
+    public void testCreation() {
+        SliceProviderPermissions provider = createProvider();
+        ArrayList<SliceAuthority> authorities = new ArrayList<>(provider.getAuthorities());
+        authorities.sort(Comparator.comparing(SliceAuthority::getAuthority));
+
+        assertEquals(2, authorities.size());
+        assertEquals("com.android.pkg", authorities.get(0).getAuthority());
+        assertEquals("com.android.pkg.slices", authorities.get(1).getAuthority());
+
+        assertEquals(1, authorities.get(0).getPkgs().size());
+        assertEquals(2, authorities.get(1).getPkgs().size());
+    }
+
+    @Test
+    public void testSerialization() throws XmlPullParserException, IOException {
+        SliceProviderPermissions provider = createProvider();
+
+        ByteArrayOutputStream output = new ByteArrayOutputStream();
+        XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+        serializer.setOutput(output, Encoding.UTF_8.name());
+
+        provider.writeTo(serializer);
+        serializer.flush();
+
+        ByteArrayInputStream input = new ByteArrayInputStream(output.toByteArray());
+        XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
+        parser.setInput(input, Encoding.UTF_8.name());
+
+        SliceProviderPermissions deser = SliceProviderPermissions.createFrom(parser,
+                mock(DirtyTracker.class));
+
+        assertEquivalent(provider, deser);
+    }
+
+    private void assertEquivalent(SliceProviderPermissions o1, SliceProviderPermissions o2) {
+        assertEquals(o1.getPkg(), o2.getPkg());
+        assertEquals(o1.getAuthorities(), o2.getAuthorities());
+    }
+
+    private static SliceProviderPermissions createProvider() {
+        PkgUser pkg = new PkgUser("com.android.pkg", 2);
+        DirtyTracker tracker = mock(DirtyTracker.class);
+        SliceProviderPermissions provider = new SliceProviderPermissions(pkg, tracker);
+
+        SliceAuthority auth = provider.getOrCreateAuthority("com.android.pkg.slices");
+        auth.addPkg(new PkgUser("com.example.pkg", 0));
+        auth.addPkg(new PkgUser("example.pkg.com", 10));
+
+        auth = provider.getOrCreateAuthority("com.android.pkg");
+        auth.addPkg(new PkgUser("com.example.pkg", 2));
+        return provider;
+    }
+
+}
\ No newline at end of file
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 1160943..cd524a5 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -926,25 +926,24 @@
                             Slog.w(TAG, mPuuid + ": Dropped operation as too many operations were "
                                     + "run in last 24 hours");
                         }
-                        return;
-                    }
+                    } else {
+                        mNumOps.addOp(currentTime);
 
-                    mNumOps.addOp(currentTime);
+                        // Find a free opID
+                        int opId = mNumTotalOpsPerformed;
+                        do {
+                            mNumTotalOpsPerformed++;
+                        } while (mRunningOpIds.contains(opId));
 
-                    // Find a free opID
-                    int opId = mNumTotalOpsPerformed;
-                    do {
-                        mNumTotalOpsPerformed++;
-                    } while (mRunningOpIds.contains(opId));
+                        // Run OP
+                        try {
+                            if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
 
-                    // Run OP
-                    try {
-                        if (DEBUG) Slog.v(TAG, mPuuid + ": runOp " + opId);
-
-                        op.run(opId, mService);
-                        mRunningOpIds.add(opId);
-                    } catch (Exception e) {
-                        Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+                            op.run(opId, mService);
+                            mRunningOpIds.add(opId);
+                        } catch (Exception e) {
+                            Slog.e(TAG, mPuuid + ": Could not run operation " + opId, e);
+                        }
                     }
 
                     // Unbind from service if no operations are left (i.e. if the operation failed)
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 754fe68..a9389be 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -477,6 +477,9 @@
      * <p>
      * Contains {@link #EXTRA_SUBSCRIPTION_INDEX} to indicate which subscription
      * the user is interested in.
+     * <p>
+     * Receivers should protect themselves by checking that the sender holds the
+     * {@code android.permission.MANAGE_SUBSCRIPTION_PLANS} permission.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     @SystemApi
@@ -1719,6 +1722,8 @@
      * </ul>
      *
      * @param subId the subscriber this relationship applies to
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public @NonNull List<SubscriptionPlan> getSubscriptionPlans(int subId) {
@@ -1744,10 +1749,13 @@
      * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
      * </ul>
      *
-     * @param subId the subscriber this relationship applies to
+     * @param subId the subscriber this relationship applies to. An empty list
+     *            may be sent to clear any existing plans.
      * @param plans the list of plans. The first plan is always the primary and
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
@@ -1788,6 +1796,8 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@@ -1822,6 +1832,8 @@
      *            be automatically cleared, or {@code 0} to leave in the
      *            requested state until explicitly cleared, or the next reboot,
      *            whichever happens first.
+     * @throws SecurityException if the caller doesn't meet the requirements
+     *             outlined above.
      */
     @SystemApi
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index 4ffb70b..ef2a364 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -24,7 +24,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.util.Pair;
+import android.util.Range;
 import android.util.RecurrenceRule;
 
 import com.android.internal.util.Preconditions;
@@ -209,7 +209,7 @@
      * any recurrence rules. The iterator starts from the currently active cycle
      * and walks backwards through time.
      */
-    public Iterator<Pair<ZonedDateTime, ZonedDateTime>> cycleIterator() {
+    public Iterator<Range<ZonedDateTime>> cycleIterator() {
         return cycleRule.cycleIterator();
     }
 
@@ -227,6 +227,9 @@
         /**
          * Start defining a {@link SubscriptionPlan} that covers a very specific
          * window of time, and never automatically recurs.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param end The exact time at which the plan ends.
          */
         public static Builder createNonrecurring(ZonedDateTime start, ZonedDateTime end) {
             if (!end.isAfter(start)) {
@@ -237,28 +240,40 @@
         }
 
         /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every month. It will always recur on the same day of a
-         * particular month. When a particular month ends before the defined
-         * recurrence day, the plan will recur on the last instant of that
-         * month.
+         * Start defining a {@link SubscriptionPlan} that starts at a specific
+         * time, and automatically recurs after each specific period of time,
+         * repeating indefinitely.
+         * <p>
+         * When the given period is set to exactly one month, the plan will
+         * always recur on the day of the month defined by
+         * {@link ZonedDateTime#getDayOfMonth()}. When a particular month ends
+         * before this day, the plan will recur on the last possible instant of
+         * that month.
+         *
+         * @param start The exact time at which the plan starts.
+         * @param period The period after which the plan automatically recurs.
          */
+        public static Builder createRecurring(ZonedDateTime start, Period period) {
+            return new Builder(start, null, period);
+        }
+
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringMonthly(ZonedDateTime start) {
             return new Builder(start, null, Period.ofMonths(1));
         }
 
-        /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every week.
-         */
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringWeekly(ZonedDateTime start) {
             return new Builder(start, null, Period.ofDays(7));
         }
 
-        /**
-         * Start defining a {@link SubscriptionPlan} that will recur
-         * automatically every day.
-         */
+        /** {@hide} */
+        @SystemApi
+        @Deprecated
         public static Builder createRecurringDaily(ZonedDateTime start) {
             return new Builder(start, null, Period.ofDays(1));
         }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 7cb5ce4..3b9006c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5183,7 +5183,12 @@
      * {@link #AUTHTYPE_EAP_SIM}
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
-     * @return the response of authentication, or null if not available
+     * @return the response of authentication. This value will be null in the following cases:
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
      */
     // TODO(b/73660190): This should probably require MODIFY_PHONE_STATE, not
     // READ_PRIVILEGED_PHONE_STATE. It certainly shouldn't reference the permission in Javadoc since
@@ -5204,7 +5209,13 @@
      * {@link #AUTHTYPE_EAP_SIM}
      * @param data authentication challenge data, base64 encoded.
      * See 3GPP TS 31.102 7.1.2 for more details.
-     * @return the response of authentication, or null if not available
+     * @return the response of authentication. This value will be null in the following cases only
+     * (see 3GPP TS 31.102 7.3.1):
+     *   Authentication error, incorrect MAC
+     *   Authentication error, security context not supported
+     *   Key freshness failure
+     *   Authentication error, no memory space available
+     *   Authentication error, no memory space available in EFMUK
      * @hide
      */
     public String getIccAuthentication(int subId, int appType, int authType, String data) {
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 602c796..9e3302b 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Parcel;
@@ -184,6 +185,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public Builder setServiceId(String serviceId) {
             fileServiceId = serviceId;
             return this;
diff --git a/tests/DexLoggerIntegrationTests/AndroidTest.xml b/tests/DexLoggerIntegrationTests/AndroidTest.xml
index 8ed19f8..fb1bef6 100644
--- a/tests/DexLoggerIntegrationTests/AndroidTest.xml
+++ b/tests/DexLoggerIntegrationTests/AndroidTest.xml
@@ -25,5 +25,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.frameworks.dexloggertest"/>
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index a7bdabd..0787d82 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -106,7 +106,7 @@
             mPorterDuffColor = porterDuffColor;
             final PorterDuffColorFilter filter =
                     (PorterDuffColorFilter) mBlendPaint.getColorFilter();
-            filter.setColor(mPorterDuffColor);
+            mBlendPaint.setColorFilter(new PorterDuffColorFilter(porterDuffColor, filter.getMode()));
             invalidate();
         }
 
diff --git a/tests/UsbTests/AndroidTest.xml b/tests/UsbTests/AndroidTest.xml
index 0b623fb..4affad3 100644
--- a/tests/UsbTests/AndroidTest.xml
+++ b/tests/UsbTests/AndroidTest.xml
@@ -25,5 +25,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest">
         <option name="package" value="com.android.server.usb"/>
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="hidden-api-checks" value="false"/>
     </test>
-</configuration>
\ No newline at end of file
+</configuration>
diff --git a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
index 9174014..ae3914e 100644
--- a/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
+++ b/tests/WindowManagerStressTest/src/test/windowmanagerstresstest/MainActivity.java
@@ -102,7 +102,7 @@
                 Thread t = new Thread(() -> {
                     try {
                         WindowManagerGlobal.getWindowSession().relayout(window,
-                                window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, mTmpRect,
+                                window.mSeq, mLayoutParams, -1, -1, View.VISIBLE, 0, -1, mTmpRect,
                                 mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect, mTmpRect,
                                 new DisplayCutout.ParcelableWrapper(), new MergedConfiguration(),
                                 new Surface());
diff --git a/tests/net/AndroidTest.xml b/tests/net/AndroidTest.xml
index f8ecc6b..6e020a3 100644
--- a/tests/net/AndroidTest.xml
+++ b/tests/net/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.tests.net" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b0e11c4..482d6e1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -161,6 +161,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -879,6 +880,10 @@
             return mMetricsService;
         }
 
+        @Override
+        protected void registerNetdEventCallback() {
+        }
+
         public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
             return mLastCreatedNetworkMonitor;
         }
@@ -3777,6 +3782,11 @@
         // The default on Android is opportunistic mode ("Automatic").
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
 
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
         mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
         waitForIdle();
         // CS tells netd about the empty DNS config for this network.
@@ -3812,6 +3822,14 @@
         assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
         reset(mNetworkManagementService);
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+                mCellNetworkAgent);
+        CallbackInfo cbi = cellNetworkCallback.expectCallback(
+                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
 
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
         verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
@@ -3821,6 +3839,7 @@
         assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
         reset(mNetworkManagementService);
+        cellNetworkCallback.assertNoCallback();
 
         setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
         verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
@@ -3833,8 +3852,112 @@
         assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
                 new String[]{"2001:db8::1", "192.0.2.1"}));
         reset(mNetworkManagementService);
+        cellNetworkCallback.assertNoCallback();
 
-        // Can't test strict mode without properly mocking out the DNS lookups.
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
+        // Can't test dns configuration for strict mode without properly mocking
+        // out the DNS lookups, but can test that LinkProperties is updated.
+        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+                mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
+    }
+
+    @Test
+    public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception {
+        // The default on Android is opportunistic mode ("Automatic").
+        setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+
+        final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+        final NetworkRequest cellRequest = new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_CELLULAR).build();
+        mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+        mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+        waitForIdle();
+        LinkProperties lp = new LinkProperties();
+        mCellNetworkAgent.sendLinkProperties(lp);
+        mCellNetworkAgent.connect(false);
+        waitForIdle();
+        cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+        cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+                mCellNetworkAgent);
+        CallbackInfo cbi = cellNetworkCallback.expectCallback(
+                CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+        Set<InetAddress> dnsServers = new HashSet<>();
+        checkDnsServers(cbi.arg, dnsServers);
+
+        // Send a validation event for a server that is not part of the current
+        // resolver config. The validation event should be ignored.
+        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+                mCellNetworkAgent.getNetwork().netId, "", "145.100.185.18", true);
+        cellNetworkCallback.assertNoCallback();
+
+        // Add a dns server to the LinkProperties.
+        LinkProperties lp2 = new LinkProperties(lp);
+        lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
+        mCellNetworkAgent.sendLinkProperties(lp2);
+        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+                mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+        dnsServers.add(InetAddress.getByName("145.100.185.16"));
+        checkDnsServers(cbi.arg, dnsServers);
+
+        // Send a validation event containing a hostname that is not part of
+        // the current resolver config. The validation event should be ignored.
+        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "hostname", true);
+        cellNetworkCallback.assertNoCallback();
+
+        // Send a validation event where validation failed.
+        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", false);
+        cellNetworkCallback.assertNoCallback();
+
+        // Send a validation event where validation succeeded for a server in
+        // the current resolver config. A LinkProperties callback with updated
+        // private dns fields should be sent.
+        mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+                mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
+        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+                mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+        checkDnsServers(cbi.arg, dnsServers);
+
+        // The private dns fields in LinkProperties should be preserved when
+        // the network agent sends unrelated changes.
+        LinkProperties lp3 = new LinkProperties(lp2);
+        lp3.setMtu(1300);
+        mCellNetworkAgent.sendLinkProperties(lp3);
+        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+                mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+        checkDnsServers(cbi.arg, dnsServers);
+        assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+
+        // Removing the only validated server should affect the private dns
+        // fields in LinkProperties.
+        LinkProperties lp4 = new LinkProperties(lp3);
+        lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
+        mCellNetworkAgent.sendLinkProperties(lp4);
+        cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+                mCellNetworkAgent);
+        cellNetworkCallback.assertNoCallback();
+        assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+        assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+        dnsServers.remove(InetAddress.getByName("145.100.185.16"));
+        checkDnsServers(cbi.arg, dnsServers);
+        assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
     }
 
     private void checkDirectlyConnectedRoutes(Object callbackObj,
@@ -3854,6 +3977,13 @@
         assertTrue(observedRoutes.containsAll(expectedRoutes));
     }
 
+    private static void checkDnsServers(Object callbackObj, Set<InetAddress> dnsServers) {
+        assertTrue(callbackObj instanceof LinkProperties);
+        LinkProperties lp = (LinkProperties) callbackObj;
+        assertEquals(dnsServers.size(), lp.getDnsServers().size());
+        assertTrue(lp.getDnsServers().containsAll(dnsServers));
+    }
+
     private static <T> void assertEmpty(T[] ts) {
         int length = ts.length;
         assertEquals("expected empty array, but length was " + length, 0, length);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
new file mode 100644
index 0000000..bcd8bf3
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -0,0 +1,201 @@
+/*
+ * 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.connectivity;
+
+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 org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.os.INetworkManagementService;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import java.net.InetAddress;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DnsManager}.
+ *
+ * Build, install and run with:
+ *  runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsManagerTest {
+    static final int TEST_NETID = 100;
+    static final int TEST_NETID_ALTERNATE = 101;
+    static final int TEST_NETID_UNTRACKED = 102;
+    final boolean IS_DEFAULT = true;
+    final boolean NOT_DEFAULT = false;
+
+    DnsManager mDnsManager;
+    MockContentResolver mContentResolver;
+
+    @Mock Context mCtx;
+    @Mock INetworkManagementService mNMService;
+    @Mock MockableSystemProperties mSystemProperties;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mContentResolver = new MockContentResolver();
+        mContentResolver.addProvider(Settings.AUTHORITY,
+                new FakeSettingsProvider());
+        when(mCtx.getContentResolver()).thenReturn(mContentResolver);
+        mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
+
+        // Clear the private DNS settings
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.PRIVATE_DNS_MODE, "");
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.PRIVATE_DNS_SPECIFIER, "");
+    }
+
+    @Test
+    public void testTrackedValidationUpdates() throws Exception {
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+                mDnsManager.getPrivateDnsConfig());
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID_ALTERNATE),
+                mDnsManager.getPrivateDnsConfig());
+        LinkProperties lp = new LinkProperties();
+        lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+        lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
+
+        // Send a validation event that is tracked on the alternate netId
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT);
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
+                InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+        LinkProperties fixedLp = new LinkProperties(lp);
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+        assertFalse(fixedLp.isPrivateDnsActive());
+        assertNull(fixedLp.getPrivateDnsServerName());
+        fixedLp = new LinkProperties(lp);
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID_ALTERNATE, fixedLp);
+        assertTrue(fixedLp.isPrivateDnsActive());
+        assertNull(fixedLp.getPrivateDnsServerName());
+
+        // Switch to strict mode
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.PRIVATE_DNS_MODE,
+                PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.PRIVATE_DNS_SPECIFIER, "strictmode.com");
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+                mDnsManager.getPrivateDnsConfig());
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        fixedLp = new LinkProperties(lp);
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+        assertTrue(fixedLp.isPrivateDnsActive());
+        assertEquals("strictmode.com", fixedLp.getPrivateDnsServerName());
+        fixedLp = new LinkProperties(lp);
+    }
+
+    @Test
+    public void testIgnoreUntrackedValidationUpdates() throws Exception {
+        // The PrivateDnsConfig map is empty, so no validation events will
+        // be tracked.
+        LinkProperties lp = new LinkProperties();
+        lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Validation event has untracked netId
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+                mDnsManager.getPrivateDnsConfig());
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
+                InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Validation event has untracked ipAddress
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Validation event has untracked hostname
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("3.3.3.3"), "hostname",
+                true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Validation event failed
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("3.3.3.3"), "", false));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Network removed
+        mDnsManager.removeNetwork(new Network(TEST_NETID));
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+
+        // Turn private DNS mode off
+        Settings.Global.putString(mContentResolver,
+                Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
+        mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+                mDnsManager.getPrivateDnsConfig());
+        mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+        mDnsManager.updatePrivateDnsValidation(
+                new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+                InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+        mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+        assertFalse(lp.isPrivateDnsActive());
+        assertNull(lp.getPrivateDnsServerName());
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
new file mode 100644
index 0000000..e58811b
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/MultipathPolicyTrackerTest.java
@@ -0,0 +1,360 @@
+/*
+ * 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.connectivity;
+
+import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
+
+import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH;
+import static com.android.server.net.NetworkPolicyManagerService.OPPORTUNISTIC_QUOTA_UNKNOWN;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.usage.NetworkStatsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.StringNetworkSpecifier;
+import android.os.Handler;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+import android.test.mock.MockContentResolver;
+import android.util.DataUnit;
+import android.util.RecurrenceRule;
+
+import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.LocalServices;
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.net.NetworkStatsManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.time.Clock;
+import java.time.Instant;
+import java.time.Period;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.temporal.ChronoUnit;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MultipathPolicyTrackerTest {
+    private static final Network TEST_NETWORK = new Network(123);
+    private static final int POLICY_SNOOZED = -100;
+
+    @Mock private Context mContext;
+    @Mock private Resources mResources;
+    @Mock private Handler mHandler;
+    @Mock private MultipathPolicyTracker.Dependencies mDeps;
+    @Mock private Clock mClock;
+    @Mock private ConnectivityManager mCM;
+    @Mock private NetworkPolicyManager mNPM;
+    @Mock private NetworkStatsManager mStatsManager;
+    @Mock private NetworkPolicyManagerInternal mNPMI;
+    @Mock private NetworkStatsManagerInternal mNetworkStatsManagerInternal;
+    @Mock private TelephonyManager mTelephonyManager;
+    private MockContentResolver mContentResolver;
+
+    private ArgumentCaptor<BroadcastReceiver> mConfigChangeReceiverCaptor;
+
+    private MultipathPolicyTracker mTracker;
+
+    private Clock mPreviousRecurrenceRuleClock;
+    private boolean mRecurrenceRuleClockMocked;
+
+    private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+        when(mContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
+        when(mContext.getSystemService(serviceName)).thenReturn(service);
+    }
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mPreviousRecurrenceRuleClock = RecurrenceRule.sClock;
+        RecurrenceRule.sClock = mClock;
+        mRecurrenceRuleClockMocked = true;
+
+        mConfigChangeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
+
+        when(mContext.getResources()).thenReturn(mResources);
+        when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+        when(mContext.registerReceiverAsUser(mConfigChangeReceiverCaptor.capture(),
+                any(), argThat(f -> f.hasAction(ACTION_CONFIGURATION_CHANGED)), any(), any()))
+                .thenReturn(null);
+
+        when(mDeps.getClock()).thenReturn(mClock);
+
+        when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+
+        mContentResolver = Mockito.spy(new MockContentResolver(mContext));
+        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        Settings.Global.clearProviderForTest();
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
+
+        mockService(Context.CONNECTIVITY_SERVICE, ConnectivityManager.class, mCM);
+        mockService(Context.NETWORK_POLICY_SERVICE, NetworkPolicyManager.class, mNPM);
+        mockService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class, mStatsManager);
+        mockService(Context.TELEPHONY_SERVICE, TelephonyManager.class, mTelephonyManager);
+
+        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
+        LocalServices.addService(NetworkPolicyManagerInternal.class, mNPMI);
+
+        LocalServices.removeServiceForTest(NetworkStatsManagerInternal.class);
+        LocalServices.addService(NetworkStatsManagerInternal.class, mNetworkStatsManagerInternal);
+
+        mTracker = new MultipathPolicyTracker(mContext, mHandler, mDeps);
+    }
+
+    @After
+    public void tearDown() {
+        // Avoid setting static clock to null (which should normally not be the case)
+        // if MockitoAnnotations.initMocks threw an exception
+        if (mRecurrenceRuleClockMocked) {
+            RecurrenceRule.sClock = mPreviousRecurrenceRuleClock;
+        }
+        mRecurrenceRuleClockMocked = false;
+    }
+
+    private void setDefaultQuotaGlobalSetting(long setting) {
+        Settings.Global.putInt(mContentResolver, NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES,
+                (int) setting);
+    }
+
+    private void testGetMultipathPreference(
+            long usedBytesToday, long subscriptionQuota, long policyWarning, long policyLimit,
+            long defaultGlobalSetting, long defaultResSetting, boolean roaming) {
+
+        // TODO: tests should not use ZoneId.systemDefault() once code handles TZ correctly.
+        final ZonedDateTime now = ZonedDateTime.ofInstant(
+                Instant.parse("2017-04-02T10:11:12Z"), ZoneId.systemDefault());
+        final ZonedDateTime startOfDay = now.truncatedTo(ChronoUnit.DAYS);
+        when(mClock.millis()).thenReturn(now.toInstant().toEpochMilli());
+        when(mClock.instant()).thenReturn(now.toInstant());
+        when(mClock.getZone()).thenReturn(ZoneId.systemDefault());
+
+        // Setup plan quota
+        when(mNPMI.getSubscriptionOpportunisticQuota(TEST_NETWORK, QUOTA_TYPE_MULTIPATH))
+                .thenReturn(subscriptionQuota);
+
+        // Setup user policy warning / limit
+        if (policyWarning != WARNING_DISABLED || policyLimit != LIMIT_DISABLED) {
+            final Instant recurrenceStart = Instant.parse("2017-04-01T00:00:00Z");
+            final RecurrenceRule recurrenceRule = new RecurrenceRule(
+                    ZonedDateTime.ofInstant(
+                            recurrenceStart,
+                            ZoneId.systemDefault()),
+                    null /* end */,
+                    Period.ofMonths(1));
+            final boolean snoozeWarning = policyWarning == POLICY_SNOOZED;
+            final boolean snoozeLimit = policyLimit == POLICY_SNOOZED;
+            when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[] {
+                    new NetworkPolicy(
+                            NetworkTemplate.buildTemplateMobileWildcard(),
+                            recurrenceRule,
+                            snoozeWarning ? 0 : policyWarning,
+                            snoozeLimit ? 0 : policyLimit,
+                            snoozeWarning ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+                            snoozeLimit ? recurrenceStart.toEpochMilli() + 1 : SNOOZE_NEVER,
+                            SNOOZE_NEVER,
+                            true /* metered */,
+                            false /* inferred */)
+            });
+        } else {
+            when(mNPM.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
+        }
+
+        // Setup default quota in settings and resources
+        if (defaultGlobalSetting > 0) {
+            setDefaultQuotaGlobalSetting(defaultGlobalSetting);
+        }
+        when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+                .thenReturn((int) defaultResSetting);
+
+        when(mNetworkStatsManagerInternal.getNetworkTotalBytes(
+                any(),
+                eq(startOfDay.toInstant().toEpochMilli()),
+                eq(now.toInstant().toEpochMilli()))).thenReturn(usedBytesToday);
+
+        ArgumentCaptor<ConnectivityManager.NetworkCallback> networkCallback =
+                ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
+        mTracker.start();
+        verify(mCM).registerNetworkCallback(any(), networkCallback.capture(), any());
+
+        // Simulate callback after capability changes
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .addTransportType(TRANSPORT_CELLULAR)
+                .setNetworkSpecifier(new StringNetworkSpecifier("234"));
+        if (!roaming) {
+            capabilities.addCapability(NET_CAPABILITY_NOT_ROAMING);
+        }
+        networkCallback.getValue().onCapabilitiesChanged(
+                TEST_NETWORK,
+                capabilities);
+    }
+
+    @Test
+    public void testGetMultipathPreference_SubscriptionQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                DataUnit.MEGABYTES.toBytes(14) /* subscriptionQuota */,
+                DataUnit.MEGABYTES.toBytes(100) /* policyWarning */,
+                LIMIT_DISABLED,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_UserWarningQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyWarning */,
+                LIMIT_DISABLED,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SnoozedWarningQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                POLICY_SNOOZED /* policyWarning */,
+                DataUnit.MEGABYTES.toBytes(15 * 29 * 20) /* policyLimit */,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Daily budget should be 15MB (5% of daily quota), 7MB used today: callback set for 8MB
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SnoozedBothQuota() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(7) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                // 29 days from Apr. 2nd to May 1st
+                POLICY_SNOOZED /* policyWarning */,
+                POLICY_SNOOZED /* policyLimit */,
+                DataUnit.MEGABYTES.toBytes(12) /* defaultGlobalSetting */,
+                2_500_000 /* defaultResSetting */,
+                false /* roaming */);
+
+        // Default global setting should be used: 12 - 7 = 5
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(5)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_SettingChanged() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                WARNING_DISABLED,
+                LIMIT_DISABLED,
+                -1 /* defaultGlobalSetting */,
+                DataUnit.MEGABYTES.toBytes(10) /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(8)), any(), any());
+
+        // Update setting
+        setDefaultQuotaGlobalSetting(DataUnit.MEGABYTES.toBytes(14));
+        mTracker.mSettingsObserver.onChange(
+                false, Settings.Global.getUriFor(NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES));
+
+        // Callback must have been re-registered with new setting
+        verify(mStatsManager, times(1)).unregisterUsageCallback(any());
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+    }
+
+    @Test
+    public void testGetMultipathPreference_ResourceChanged() {
+        testGetMultipathPreference(
+                DataUnit.MEGABYTES.toBytes(2) /* usedBytesToday */,
+                OPPORTUNISTIC_QUOTA_UNKNOWN,
+                WARNING_DISABLED,
+                LIMIT_DISABLED,
+                -1 /* defaultGlobalSetting */,
+                DataUnit.MEGABYTES.toBytes(14) /* defaultResSetting */,
+                false /* roaming */);
+
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(12)), any(), any());
+
+        when(mResources.getInteger(R.integer.config_networkDefaultDailyMultipathQuotaBytes))
+                .thenReturn((int) DataUnit.MEGABYTES.toBytes(16));
+
+        final BroadcastReceiver configChangeReceiver = mConfigChangeReceiverCaptor.getValue();
+        assertNotNull(configChangeReceiver);
+        configChangeReceiver.onReceive(mContext, new Intent());
+
+        // Uses the new setting (16 - 2 = 14MB)
+        verify(mStatsManager, times(1)).registerUsageCallback(
+                any(), anyInt(), eq(DataUnit.MEGABYTES.toBytes(14)), any(), any());
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 662e0ba..47e49d44 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -355,6 +355,7 @@
     @Test
     public void canRequireProvisioning() {
         setupForRequiredProvisioning();
+        sendConfigurationChanged();
         assertTrue(mTethering.isTetherProvisioningRequired());
     }
 
@@ -363,6 +364,7 @@
         setupForRequiredProvisioning();
         when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
                 .thenReturn(null);
+        sendConfigurationChanged();
         // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
         // We therefore still require provisioning.
         assertTrue(mTethering.isTetherProvisioningRequired());
@@ -372,6 +374,7 @@
     public void toleratesCarrierConfigMissing() {
         setupForRequiredProvisioning();
         when(mCarrierConfigManager.getConfig()).thenReturn(null);
+        sendConfigurationChanged();
         // We still have a provisioning app configured, so still require provisioning.
         assertTrue(mTethering.isTetherProvisioningRequired());
     }
@@ -411,6 +414,11 @@
         mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
+    private void sendConfigurationChanged() {
+        final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+        mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+    }
+
     private void verifyInterfaceServingModeStarted() throws Exception {
         verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(1))
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index 26248e5..70a47cf 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -50,6 +50,18 @@
     return "\033[%sm" % (";".join(codes))
 
 
+def ident(raw):
+    """Strips superficial signature changes, giving us a strong key that
+    can be used to identify members across API levels."""
+    raw = raw.replace(" deprecated ", " ")
+    raw = raw.replace(" synchronized ", " ")
+    raw = raw.replace(" final ", " ")
+    raw = re.sub("<.+?>", "", raw)
+    if " throws " in raw:
+        raw = raw[:raw.index(" throws ")]
+    return raw
+
+
 class Field():
     def __init__(self, clazz, line, raw, blame):
         self.clazz = clazz
@@ -69,8 +81,7 @@
             self.value = raw[3].strip(';"')
         else:
             self.value = None
-
-        self.ident = self.raw.replace(" deprecated ", " ")
+        self.ident = ident(self.raw)
 
     def __hash__(self):
         return hash(self.raw)
@@ -105,15 +116,7 @@
         for r in raw[2:]:
             if r == "throws": target = self.throws
             else: target.append(r)
-
-        # identity for compat purposes
-        ident = self.raw
-        ident = ident.replace(" deprecated ", " ")
-        ident = ident.replace(" synchronized ", " ")
-        ident = re.sub("<.+?>", "", ident)
-        if " throws " in ident:
-            ident = ident[:ident.index(" throws ")]
-        self.ident = ident
+        self.ident = ident(self.raw)
 
     def __hash__(self):
         return hash(self.raw)
@@ -1469,6 +1472,40 @@
     return failures
 
 
+def show_deprecations_at_birth(cur, prev):
+    """Show API deprecations at birth."""
+    global failures
+
+    # Remove all existing things so we're left with new
+    for prev_clazz in prev.values():
+        cur_clazz = cur[prev_clazz.fullname]
+
+        sigs = { i.ident: i for i in prev_clazz.ctors }
+        cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ]
+        sigs = { i.ident: i for i in prev_clazz.methods }
+        cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ]
+        sigs = { i.ident: i for i in prev_clazz.fields }
+        cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ]
+
+        # Forget about class entirely when nothing new
+        if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0:
+            del cur[prev_clazz.fullname]
+
+    for clazz in cur.values():
+        if " deprecated " in clazz.raw and not clazz.fullname in prev:
+            error(clazz, None, None, "Found API deprecation at birth")
+
+        for i in clazz.ctors + clazz.methods + clazz.fields:
+            if " deprecated " in i.raw:
+                error(clazz, i, None, "Found API deprecation at birth")
+
+    print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+                                            format(reset=True)))
+    for f in sorted(failures):
+        print failures[f]
+        print
+
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Enforces common Android public API design \
             patterns. It ignores lint messages from a previous API level, if provided.")
@@ -1481,6 +1518,8 @@
             help="Allow references to Google")
     parser.add_argument("--show-noticed", action='store_const', const=True,
             help="Show API changes noticed")
+    parser.add_argument("--show-deprecations-at-birth", action='store_const', const=True,
+            help="Show API deprecations at birth")
     args = vars(parser.parse_args())
 
     if args['no_color']:
@@ -1492,6 +1531,14 @@
     current_file = args['current.txt']
     previous_file = args['previous.txt']
 
+    if args['show_deprecations_at_birth']:
+        with current_file as f:
+            cur = _parse_stream(f)
+        with previous_file as f:
+            prev = _parse_stream(f)
+        show_deprecations_at_birth(cur, prev)
+        sys.exit()
+
     with current_file as f:
         cur_fail, cur_noticed = examine_stream(f)
     if not previous_file is None:
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 057772f..4146e02 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -101,6 +101,7 @@
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
 
+    fprintf(out, "#include <mutex>\n");
     fprintf(out, "#include <chrono>\n");
     fprintf(out, "#include <thread>\n");
     fprintf(out, "#include <log/log_event_list.h>\n");
@@ -150,9 +151,7 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out,
-            "static std::map<int, int> "
-            "getAtomUidField() {\n");
+    fprintf(out, "static std::map<int, int> getAtomUidField() {\n");
     fprintf(out, "  std::map<int, int> uidField;\n");
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
          atom != atoms.decls.end(); atom++) {
@@ -206,6 +205,11 @@
             "AtomsInfo::kStateAtomsFieldOptions = "
             "getStateAtomFieldOptions();\n");
 
+
+    fprintf(out, "int64_t lastRetryTimestampNs = -1;\n");
+    fprintf(out, "const int64_t kMinRetryIntervalNs = NS_PER_SEC * 60 * 20; // 20 minutes\n");
+    fprintf(out, "static std::mutex mLogdRetryMutex;\n");
+
     // Print write methods
     fprintf(out, "\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
@@ -317,7 +321,7 @@
        fprintf(out, "{\n");
        fprintf(out, "  int ret = 0;\n");
 
-       fprintf(out, "  for(int retry = 0; retry < 3; ++retry) {\n");
+       fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
        fprintf(out, "      ret =  try_stats_write(code");
 
        argIndex = 1;
@@ -340,8 +344,15 @@
        }
        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, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
+       fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
+                                "kMinRetryIntervalNs) break;\n");
+       fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
+       fprintf(out, "      }\n");
+       fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
        fprintf(out, "  }\n");
        fprintf(out, "  return ret;\n");
        fprintf(out, "}\n");
@@ -408,7 +419,7 @@
        fprintf(out, "{\n");
 
        fprintf(out, "  int ret = 0;\n");
-       fprintf(out, "  for(int retry = 0; retry < 3; ++retry) {\n");
+       fprintf(out, "  for(int retry = 0; retry < 2; ++retry) {\n");
        fprintf(out, "      ret =  try_stats_write_non_chained(code");
 
        argIndex = 1;
@@ -419,8 +430,15 @@
        }
        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, "          std::lock_guard<std::mutex> lock(mLogdRetryMutex);\n");
+       fprintf(out, "          if ((android::elapsedRealtimeNano() - lastRetryTimestampNs) <= "
+                                "kMinRetryIntervalNs) break;\n");
+       fprintf(out, "          lastRetryTimestampNs = android::elapsedRealtimeNano();\n");
+       fprintf(out, "      }\n");
+
+       fprintf(out, "      std::this_thread::sleep_for(std::chrono::milliseconds(10));\n");
        fprintf(out, "  }\n");
        fprintf(out, "  return ret;\n");
        fprintf(out, "}\n");
diff --git a/wifi/tests/AndroidTest.xml b/wifi/tests/AndroidTest.xml
index 764eb2b..45c7a17 100644
--- a/wifi/tests/AndroidTest.xml
+++ b/wifi/tests/AndroidTest.xml
@@ -23,5 +23,6 @@
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="android.net.wifi.test" />
         <option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
     </test>
 </configuration>