Merge "Control the QS label marquees." into pi-dev
diff --git a/Android.bp b/Android.bp
index 4d5d998..ce93568 100644
--- a/Android.bp
+++ b/Android.bp
@@ -145,11 +145,11 @@
         ":libcamera_client_framework_aidl",
         "core/java/android/hardware/IConsumerIrService.aidl",
         "core/java/android/hardware/ISerialManager.aidl",
+        "core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
         "core/java/android/hardware/display/IVirtualDisplayCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintClientActiveCallback.aidl",
-        "core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintService.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
diff --git a/Android.mk b/Android.mk
index e2f88e8..d7d9c90 100644
--- a/Android.mk
+++ b/Android.mk
@@ -252,6 +252,11 @@
 		-federate SupportLib https://developer.android.com \
 		-federationapi SupportLib prebuilts/sdk/current/support-api.txt
 
+# Federate AndroidX references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+		-federate AndroidX https://developer.android.com \
+		-federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
+
 # ====  Public API diff ===========================
 include $(CLEAR_VARS)
 
@@ -861,39 +866,58 @@
 
 # ==== hiddenapi lists =======================================
 
-# Copy blacklist and light greylist over into the build folder.
+# Copy light and dark greylist over into the build folder.
 # This is for ART buildbots which need to mock these lists and have alternative
 # rules for building them. Other rules in the build system should depend on the
 # files in the build folder.
 
-$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-blacklist.txt,\
-                            $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)))
-
+# Automatically add all methods which match the following signatures.
+# These need to be greylisted in order to allow applications to write their
+# own serializers.
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): REGEX_SERIALIZATION := \
+    "readObject\(Ljava/io/ObjectInputStream;\)V" \
+    "readObjectNoData\(\)V" \
+    "readResolve\(\)Ljava/lang/Object;" \
+    "serialVersionUID:J" \
+    "serialPersistentFields:\[Ljava/io/ObjectStreamField;" \
+    "writeObject\(Ljava/io/ObjectOutputStream;\)V" \
+    "writeReplace\(\)Ljava/lang/Object;"
+$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
 # Temporarily merge light greylist from two files. Vendor list will become dark
 # grey once we remove the UI toast.
 $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST): frameworks/base/config/hiddenapi-light-greylist.txt \
-                                               frameworks/base/config/hiddenapi-vendor-list.txt
-	sort $^ > $@
+                                               frameworks/base/config/hiddenapi-vendor-list.txt \
+                                               $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+	sort frameworks/base/config/hiddenapi-light-greylist.txt \
+	     frameworks/base/config/hiddenapi-vendor-list.txt \
+	     <(grep -E "\->("$(subst $(space),"|",$(REGEX_SERIALIZATION))")$$" $(PRIVATE_API)) \
+	> $@
+
+$(eval $(call copy-one-file,frameworks/base/config/hiddenapi-dark-greylist.txt,\
+                            $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)))
 
 # Generate dark greylist as private API minus (blacklist plus light greylist).
 
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): BLACKLIST := $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
-                                              $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST) \
-                                              $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
-	if [ ! -z "`comm -12 <(sort $(BLACKLIST)) <(sort $(LIGHT_GREYLIST))`" ]; then \
-		echo "There should be no overlap between $(BLACKLIST) and $(LIGHT_GREYLIST)" 1>&2; \
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): PRIVATE_API := $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): LIGHT_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): DARK_GREYLIST := $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST): $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE) \
+                                          $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
+                                          $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST)
+	if [ ! -z "`comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "There should be no overlap between $(LIGHT_GREYLIST) and $(DARK_GREYLIST)" 1>&2; \
+		comm -12 <(sort $(LIGHT_GREYLIST)) <(sort $(DARK_GREYLIST)) 1>&2; \
 		exit 1; \
-	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST))`" ]; then \
-		echo "$(BLACKLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
-		exit 2; \
 	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST))`" ]; then \
 		echo "$(LIGHT_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		comm -13 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST)) 1>&2; \
+		exit 2; \
+	elif [ ! -z "`comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST))`" ]; then \
+		echo "$(DARK_GREYLIST) must be a subset of $(PRIVATE_API)" 1>&2; \
+		comm -13 <(sort $(PRIVATE_API)) <(sort $(DARK_GREYLIST)) 1>&2; \
 		exit 3; \
 	fi
-	comm -23 <(sort $(PRIVATE_API)) <(sort $(BLACKLIST) $(LIGHT_GREYLIST)) > $@
+	comm -23 <(sort $(PRIVATE_API)) <(sort $(LIGHT_GREYLIST) $(DARK_GREYLIST)) > $@
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
new file mode 100644
index 0000000..6aa34a6
--- /dev/null
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs CorePerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CorePerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.core" />
+    </test>
+</configuration>
diff --git a/apct-tests/perftests/multiuser/Android.mk b/apct-tests/perftests/multiuser/Android.mk
index a803369..9bc7d05 100644
--- a/apct-tests/perftests/multiuser/Android.mk
+++ b/apct-tests/perftests/multiuser/Android.mk
@@ -26,6 +26,8 @@
 LOCAL_PACKAGE_NAME := MultiUserPerfTests
 LOCAL_PRIVATE_PLATFORM_APIS := true
 
+LOCAL_COMPATIBILITY_SUITE += device-tests
+
 LOCAL_CERTIFICATE := platform
 
 include $(BUILD_PACKAGE)
diff --git a/apct-tests/perftests/multiuser/AndroidTest.xml b/apct-tests/perftests/multiuser/AndroidTest.xml
new file mode 100644
index 0000000..6ede827
--- /dev/null
+++ b/apct-tests/perftests/multiuser/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs MultiUserPerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="MultiUserPerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.perftests.multiuser" />
+    </test>
+</configuration>
diff --git a/api/current.txt b/api/current.txt
index be5e3e7..8090aa0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -142,7 +142,8 @@
     field public static final java.lang.String TRANSMIT_IR = "android.permission.TRANSMIT_IR";
     field public static final java.lang.String UNINSTALL_SHORTCUT = "com.android.launcher.permission.UNINSTALL_SHORTCUT";
     field public static final java.lang.String UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
-    field public static final java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
+    field public static final java.lang.String USE_BIOMETRIC = "android.permission.USE_BIOMETRIC";
+    field public static final deprecated java.lang.String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT";
     field public static final java.lang.String USE_SIP = "android.permission.USE_SIP";
     field public static final java.lang.String VIBRATE = "android.permission.VIBRATE";
     field public static final java.lang.String WAKE_LOCK = "android.permission.WAKE_LOCK";
@@ -5877,6 +5878,8 @@
     method public java.lang.CharSequence getTitle();
     method public boolean isEnabled();
     method public void setEnabled(boolean);
+    method public void setShouldShowIcon(boolean);
+    method public boolean shouldShowIcon();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.RemoteAction> CREATOR;
   }
@@ -6112,8 +6115,12 @@
     method public android.view.WindowAnimationFrameStats getWindowAnimationFrameStats();
     method public android.view.WindowContentFrameStats getWindowContentFrameStats(int);
     method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
+    method public void grantRuntimePermission(java.lang.String, java.lang.String);
+    method public void grantRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
     method public boolean performGlobalAction(int);
+    method public void revokeRuntimePermission(java.lang.String, java.lang.String);
+    method public void revokeRuntimePermissionAsUser(java.lang.String, java.lang.String, android.os.UserHandle);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
     method public boolean setRotation(int);
     method public void setRunAsMonkey(boolean);
@@ -6261,6 +6268,7 @@
     method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler);
     method public void clear() throws java.io.IOException;
     method public void clear(int) throws java.io.IOException;
+    method public void clearWallpaper();
     method public void clearWallpaperOffsets(android.os.IBinder);
     method public void forgetLoadedWallpaper();
     method public android.graphics.drawable.Drawable getBuiltInDrawable();
@@ -6287,6 +6295,7 @@
     method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
     method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
     method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean, int) throws java.io.IOException;
+    method public void setDisplayPadding(android.graphics.Rect);
     method public void setResource(int) throws java.io.IOException;
     method public int setResource(int, int) throws java.io.IOException;
     method public void setStream(java.io.InputStream) throws java.io.IOException;
@@ -6454,12 +6463,11 @@
     method public android.content.ComponentName getMandatoryBackupTransport();
     method public int getMaximumFailedPasswordsForWipe(android.content.ComponentName);
     method public long getMaximumTimeToLock(android.content.ComponentName);
-    method public java.util.List<java.lang.String> getMeteredDataDisabled(android.content.ComponentName);
+    method public java.util.List<java.lang.String> getMeteredDataDisabledPackages(android.content.ComponentName);
     method public int getOrganizationColor(android.content.ComponentName);
     method public java.lang.CharSequence getOrganizationName(android.content.ComponentName);
     method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName);
     method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName);
-    method public java.lang.String getPasswordBlacklistName(android.content.ComponentName);
     method public long getPasswordExpiration(android.content.ComponentName);
     method public long getPasswordExpirationTimeout(android.content.ComponentName);
     method public int getPasswordHistoryLength(android.content.ComponentName);
@@ -6496,7 +6504,7 @@
     method public boolean installExistingPackage(android.content.ComponentName, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate, java.lang.String);
     method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean);
-    method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, boolean, boolean);
+    method public boolean installKeyPair(android.content.ComponentName, java.security.PrivateKey, java.security.cert.Certificate[], java.lang.String, int);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isAffiliatedUser();
@@ -6562,13 +6570,12 @@
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
-    method public java.util.List<java.lang.String> setMeteredDataDisabled(android.content.ComponentName, java.util.List<java.lang.String>);
+    method public java.util.List<java.lang.String> setMeteredDataDisabledPackages(android.content.ComponentName, java.util.List<java.lang.String>);
     method public void setNetworkLoggingEnabled(android.content.ComponentName, boolean);
     method public void setOrganizationColor(android.content.ComponentName, int);
     method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence);
     method public void setOverrideApnsEnabled(android.content.ComponentName, boolean);
     method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean);
-    method public boolean setPasswordBlacklist(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>);
     method public void setPasswordExpirationTimeout(android.content.ComponentName, long);
     method public void setPasswordHistoryLength(android.content.ComponentName, int);
     method public void setPasswordMinimumLength(android.content.ComponentName, int);
@@ -6680,9 +6687,14 @@
     field public static final int ID_TYPE_IMEI = 4; // 0x4
     field public static final int ID_TYPE_MEID = 8; // 0x8
     field public static final int ID_TYPE_SERIAL = 2; // 0x2
+    field public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1; // 0x1
+    field public static final int INSTALLKEY_SET_USER_SELECTABLE = 2; // 0x2
+    field public static final int KEYGUARD_DISABLE_BIOMETRICS = 416; // 0x1a0
+    field public static final int KEYGUARD_DISABLE_FACE = 128; // 0x80
     field public static final int KEYGUARD_DISABLE_FEATURES_ALL = 2147483647; // 0x7fffffff
     field public static final int KEYGUARD_DISABLE_FEATURES_NONE = 0; // 0x0
     field public static final int KEYGUARD_DISABLE_FINGERPRINT = 32; // 0x20
+    field public static final int KEYGUARD_DISABLE_IRIS = 256; // 0x100
     field public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 64; // 0x40
     field public static final int KEYGUARD_DISABLE_SECURE_CAMERA = 2; // 0x2
     field public static final int KEYGUARD_DISABLE_SECURE_NOTIFICATIONS = 4; // 0x4
@@ -7041,7 +7053,8 @@
     method public int getBackoffPolicy();
     method public android.content.ClipData getClipData();
     method public int getClipGrantFlags();
-    method public long getEstimatedNetworkBytes();
+    method public long getEstimatedNetworkDownloadBytes();
+    method public long getEstimatedNetworkUploadBytes();
     method public android.os.PersistableBundle getExtras();
     method public long getFlexMillis();
     method public int getId();
@@ -7058,8 +7071,10 @@
     method public long getTriggerContentMaxDelay();
     method public long getTriggerContentUpdateDelay();
     method public android.app.job.JobInfo.TriggerContentUri[] getTriggerContentUris();
+    method public boolean isImportantWhileForeground();
     method public boolean isPeriodic();
     method public boolean isPersisted();
+    method public boolean isPrefetch();
     method public boolean isRequireBatteryNotLow();
     method public boolean isRequireCharging();
     method public boolean isRequireDeviceIdle();
@@ -7085,15 +7100,15 @@
     method public android.app.job.JobInfo build();
     method public android.app.job.JobInfo.Builder setBackoffCriteria(long, int);
     method public android.app.job.JobInfo.Builder setClipData(android.content.ClipData, int);
-    method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+    method public android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long, long);
     method public android.app.job.JobInfo.Builder setExtras(android.os.PersistableBundle);
     method public android.app.job.JobInfo.Builder setImportantWhileForeground(boolean);
-    method public android.app.job.JobInfo.Builder setIsPrefetch(boolean);
     method public android.app.job.JobInfo.Builder setMinimumLatency(long);
     method public android.app.job.JobInfo.Builder setOverrideDeadline(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long);
     method public android.app.job.JobInfo.Builder setPeriodic(long, long);
     method public android.app.job.JobInfo.Builder setPersisted(boolean);
+    method public android.app.job.JobInfo.Builder setPrefetch(boolean);
     method public android.app.job.JobInfo.Builder setRequiredNetwork(android.net.NetworkRequest);
     method public android.app.job.JobInfo.Builder setRequiredNetworkType(int);
     method public android.app.job.JobInfo.Builder setRequiresBatteryNotLow(boolean);
@@ -7163,10 +7178,11 @@
 
   public final class JobWorkItem implements android.os.Parcelable {
     ctor public JobWorkItem(android.content.Intent);
-    ctor public JobWorkItem(android.content.Intent, long);
+    ctor public JobWorkItem(android.content.Intent, long, long);
     method public int describeContents();
     method public int getDeliveryCount();
-    method public long getEstimatedNetworkBytes();
+    method public long getEstimatedNetworkDownloadBytes();
+    method public long getEstimatedNetworkUploadBytes();
     method public android.content.Intent getIntent();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.job.JobWorkItem> CREATOR;
@@ -7194,21 +7210,25 @@
     field public static final java.lang.String HINT_ACTIONS = "actions";
     field public static final java.lang.String HINT_ERROR = "error";
     field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
-    field public static final java.lang.String HINT_KEY_WORDS = "key_words";
+    field public static final java.lang.String HINT_KEYWORDS = "keywords";
     field public static final java.lang.String HINT_LARGE = "large";
+    field public static final java.lang.String HINT_LAST_UPDATED = "last_updated";
     field public static final java.lang.String HINT_LIST = "list";
     field public static final java.lang.String HINT_LIST_ITEM = "list_item";
     field public static final java.lang.String HINT_NO_TINT = "no_tint";
     field public static final java.lang.String HINT_PARTIAL = "partial";
+    field public static final java.lang.String HINT_PERMISSION_REQUEST = "permission_request";
     field public static final java.lang.String HINT_SEE_MORE = "see_more";
     field public static final java.lang.String HINT_SELECTED = "selected";
     field public static final java.lang.String HINT_SHORTCUT = "shortcut";
     field public static final java.lang.String HINT_SUMMARY = "summary";
     field public static final java.lang.String HINT_TITLE = "title";
+    field public static final java.lang.String HINT_TTL = "ttl";
     field public static final java.lang.String SUBTYPE_COLOR = "color";
     field public static final java.lang.String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
     field public static final java.lang.String SUBTYPE_MAX = "max";
     field public static final java.lang.String SUBTYPE_MESSAGE = "message";
+    field public static final java.lang.String SUBTYPE_MILLIS = "millis";
     field public static final java.lang.String SUBTYPE_PRIORITY = "priority";
     field public static final java.lang.String SUBTYPE_RANGE = "range";
     field public static final deprecated java.lang.String SUBTYPE_SLIDER = "slider";
@@ -7279,11 +7299,12 @@
   public class SliceMetrics {
     ctor public SliceMetrics(android.content.Context, android.net.Uri);
     method public void logHidden();
-    method public void logTouch(android.net.Uri);
+    method public void logTouch(int, android.net.Uri);
     method public void logVisible();
   }
 
   public abstract class SliceProvider extends android.content.ContentProvider {
+    ctor public SliceProvider(java.lang.String...);
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
     method public final java.lang.String getType(android.net.Uri);
@@ -7328,6 +7349,20 @@
     field public static final android.os.Parcelable.Creator<android.app.usage.ConfigurationStats> CREATOR;
   }
 
+  public final class EventStats implements android.os.Parcelable {
+    ctor public EventStats(android.app.usage.EventStats);
+    method public void add(android.app.usage.EventStats);
+    method public int describeContents();
+    method public int getCount();
+    method public int getEventType();
+    method public long getFirstTimeStamp();
+    method public long getLastTimeStamp();
+    method public long getLastTimeUsed();
+    method public long getTotalTime();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.app.usage.EventStats> CREATOR;
+  }
+
   public final class ExternalStorageStats implements android.os.Parcelable {
     method public int describeContents();
     method public long getAppBytes();
@@ -7433,6 +7468,8 @@
     field public static final int MOVE_TO_BACKGROUND = 2; // 0x2
     field public static final int MOVE_TO_FOREGROUND = 1; // 0x1
     field public static final int NONE = 0; // 0x0
+    field public static final int SCREEN_INTERACTIVE = 15; // 0xf
+    field public static final int SCREEN_NON_INTERACTIVE = 16; // 0x10
     field public static final int SHORTCUT_INVOCATION = 8; // 0x8
     field public static final int STANDBY_BUCKET_CHANGED = 11; // 0xb
     field public static final int USER_INTERACTION = 7; // 0x7
@@ -7456,6 +7493,7 @@
     method public boolean isAppInactive(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
+    method public java.util.List<android.app.usage.EventStats> queryEventStats(int, long, long);
     method public android.app.usage.UsageEvents queryEvents(long, long);
     method public android.app.usage.UsageEvents queryEventsForSelf(long, long);
     method public java.util.List<android.app.usage.UsageStats> queryUsageStats(int, long, long);
@@ -9882,6 +9920,8 @@
     field public static final java.lang.String ACTION_MEDIA_UNMOUNTABLE = "android.intent.action.MEDIA_UNMOUNTABLE";
     field public static final java.lang.String ACTION_MEDIA_UNMOUNTED = "android.intent.action.MEDIA_UNMOUNTED";
     field public static final java.lang.String ACTION_MY_PACKAGE_REPLACED = "android.intent.action.MY_PACKAGE_REPLACED";
+    field public static final java.lang.String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
+    field public static final java.lang.String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
     field public static final java.lang.String ACTION_NEW_OUTGOING_CALL = "android.intent.action.NEW_OUTGOING_CALL";
     field public static final java.lang.String ACTION_OPEN_DOCUMENT = "android.intent.action.OPEN_DOCUMENT";
     field public static final java.lang.String ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
@@ -10042,6 +10082,7 @@
     field public static final java.lang.String EXTRA_SPLIT_NAME = "android.intent.extra.SPLIT_NAME";
     field public static final java.lang.String EXTRA_STREAM = "android.intent.extra.STREAM";
     field public static final java.lang.String EXTRA_SUBJECT = "android.intent.extra.SUBJECT";
+    field public static final java.lang.String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
     field public static final java.lang.String EXTRA_TEMPLATE = "android.intent.extra.TEMPLATE";
     field public static final java.lang.String EXTRA_TEXT = "android.intent.extra.TEXT";
     field public static final java.lang.String EXTRA_TITLE = "android.intent.extra.TITLE";
@@ -11155,7 +11196,7 @@
     method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraries(int);
-    method public android.os.PersistableBundle getSuspendedPackageAppExtras();
+    method public android.os.Bundle getSuspendedPackageAppExtras();
     method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures();
     method public abstract java.lang.String[] getSystemSharedLibraryNames();
     method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo);
@@ -13302,14 +13343,9 @@
     method public void rotate(float);
     method public final void rotate(float, float, float);
     method public int save();
-    method public deprecated int save(int);
-    method public deprecated int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
     method public int saveLayer(android.graphics.RectF, android.graphics.Paint);
-    method public deprecated int saveLayer(float, float, float, float, android.graphics.Paint, int);
     method public int saveLayer(float, float, float, float, android.graphics.Paint);
-    method public deprecated int saveLayerAlpha(android.graphics.RectF, int, int);
     method public int saveLayerAlpha(android.graphics.RectF, int);
-    method public deprecated int saveLayerAlpha(float, float, float, float, int, int);
     method public int saveLayerAlpha(float, float, float, float, int);
     method public void scale(float, float);
     method public final void scale(float, float, float, float);
@@ -13319,12 +13355,6 @@
     method public void setMatrix(android.graphics.Matrix);
     method public void skew(float, float);
     method public void translate(float, float);
-    field public static final int ALL_SAVE_FLAG = 31; // 0x1f
-    field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
-    field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
-    field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
-    field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
-    field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
   }
 
   public static final class Canvas.EdgeType extends java.lang.Enum {
@@ -13615,22 +13645,22 @@
     method public int getAllocator();
     method public boolean getConserveMemory();
     method public android.graphics.Rect getCrop();
-    method public boolean getDecodeAsAlphaMask();
-    method public boolean getMutable();
     method public android.graphics.ImageDecoder.OnPartialImageListener getOnPartialImageListener();
     method public android.graphics.PostProcessor getPostProcessor();
-    method public boolean getRequireUnpremultiplied();
-    method public android.util.Size getSampledSize(int);
-    method public android.graphics.ImageDecoder setAllocator(int);
-    method public android.graphics.ImageDecoder setConserveMemory(boolean);
-    method public android.graphics.ImageDecoder setCrop(android.graphics.Rect);
-    method public android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
-    method public android.graphics.ImageDecoder setMutable(boolean);
-    method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
-    method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor);
-    method public android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
-    method public android.graphics.ImageDecoder setResize(int, int);
-    method public android.graphics.ImageDecoder setResize(int);
+    method public boolean isDecodeAsAlphaMaskEnabled();
+    method public boolean isMutableRequired();
+    method public boolean isUnpremultipliedRequired();
+    method public void setAllocator(int);
+    method public void setConserveMemory(boolean);
+    method public void setCrop(android.graphics.Rect);
+    method public void setDecodeAsAlphaMaskEnabled(boolean);
+    method public void setMutableRequired(boolean);
+    method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener);
+    method public void setPostProcessor(android.graphics.PostProcessor);
+    method public void setTargetColorSpace(android.graphics.ColorSpace);
+    method public void setTargetSampleSize(int);
+    method public void setTargetSize(int, int);
+    method public void setUnpremultipliedRequired(boolean);
     field public static final int ALLOCATOR_DEFAULT = 0; // 0x0
     field public static final int ALLOCATOR_HARDWARE = 3; // 0x3
     field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2
@@ -13646,6 +13676,7 @@
   }
 
   public static class ImageDecoder.ImageInfo {
+    method public android.graphics.ColorSpace getColorSpace();
     method public java.lang.String getMimeType();
     method public android.util.Size getSize();
     method public boolean isAnimated();
@@ -15708,6 +15739,62 @@
 
 }
 
+package android.hardware.biometrics {
+
+  public class BiometricDialog {
+    method public void authenticate(android.hardware.biometrics.BiometricDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricDialog.AuthenticationCallback);
+    method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.biometrics.BiometricDialog.AuthenticationCallback);
+    field public static final int BIOMETRIC_ACQUIRED_GOOD = 0; // 0x0
+    field public static final int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+    field public static final int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2; // 0x2
+    field public static final int BIOMETRIC_ACQUIRED_PARTIAL = 1; // 0x1
+    field public static final int BIOMETRIC_ACQUIRED_TOO_FAST = 5; // 0x5
+    field public static final int BIOMETRIC_ACQUIRED_TOO_SLOW = 4; // 0x4
+    field public static final int BIOMETRIC_ERROR_CANCELED = 5; // 0x5
+    field public static final int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12; // 0xc
+    field public static final int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1; // 0x1
+    field public static final int BIOMETRIC_ERROR_LOCKOUT = 7; // 0x7
+    field public static final int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+    field public static final int BIOMETRIC_ERROR_NO_BIOMETRICS = 11; // 0xb
+    field public static final int BIOMETRIC_ERROR_NO_SPACE = 4; // 0x4
+    field public static final int BIOMETRIC_ERROR_TIMEOUT = 3; // 0x3
+    field public static final int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+    field public static final int BIOMETRIC_ERROR_USER_CANCELED = 10; // 0xa
+    field public static final int BIOMETRIC_ERROR_VENDOR = 8; // 0x8
+  }
+
+  public static abstract class BiometricDialog.AuthenticationCallback {
+    ctor public BiometricDialog.AuthenticationCallback();
+    method public void onAuthenticationError(int, java.lang.CharSequence);
+    method public void onAuthenticationFailed();
+    method public void onAuthenticationHelp(int, java.lang.CharSequence);
+    method public void onAuthenticationSucceeded(android.hardware.biometrics.BiometricDialog.AuthenticationResult);
+  }
+
+  public static class BiometricDialog.AuthenticationResult {
+    method public android.hardware.biometrics.BiometricDialog.CryptoObject getCryptoObject();
+  }
+
+  public static class BiometricDialog.Builder {
+    ctor public BiometricDialog.Builder(android.content.Context);
+    method public android.hardware.biometrics.BiometricDialog build();
+    method public android.hardware.biometrics.BiometricDialog.Builder setDescription(java.lang.CharSequence);
+    method public android.hardware.biometrics.BiometricDialog.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
+    method public android.hardware.biometrics.BiometricDialog.Builder setSubtitle(java.lang.CharSequence);
+    method public android.hardware.biometrics.BiometricDialog.Builder setTitle(java.lang.CharSequence);
+  }
+
+  public static final class BiometricDialog.CryptoObject {
+    ctor public BiometricDialog.CryptoObject(java.security.Signature);
+    ctor public BiometricDialog.CryptoObject(javax.crypto.Cipher);
+    ctor public BiometricDialog.CryptoObject(javax.crypto.Mac);
+    method public javax.crypto.Cipher getCipher();
+    method public javax.crypto.Mac getMac();
+    method public java.security.Signature getSignature();
+  }
+
+}
+
 package android.hardware.camera2 {
 
   public class CameraAccessException extends android.util.AndroidException {
@@ -15793,6 +15880,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> DEPTH_DEPTH_IS_EXCLUSIVE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> EDGE_AVAILABLE_EDGE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> FLASH_INFO_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
@@ -16029,6 +16117,9 @@
     field public static final int CONTROL_SCENE_MODE_THEATRE = 7; // 0x7
     field public static final int CONTROL_VIDEO_STABILIZATION_MODE_OFF = 0; // 0x0
     field public static final int CONTROL_VIDEO_STABILIZATION_MODE_ON = 1; // 0x1
+    field public static final int DISTORTION_CORRECTION_MODE_FAST = 1; // 0x1
+    field public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int DISTORTION_CORRECTION_MODE_OFF = 0; // 0x0
     field public static final int EDGE_MODE_FAST = 1; // 0x1
     field public static final int EDGE_MODE_HIGH_QUALITY = 2; // 0x2
     field public static final int EDGE_MODE_OFF = 0; // 0x0
@@ -16075,6 +16166,7 @@
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11; // 0xb
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12; // 0xc
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; // 0xa
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
@@ -16181,6 +16273,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_SCENE_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
     field public static final android.os.Parcelable.Creator<android.hardware.camera2.CaptureRequest> CREATOR;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> EDGE_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> FLASH_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> HOT_PIXEL_MODE;
@@ -16263,6 +16356,7 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_POST_RAW_SENSITIVITY_BOOST;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_SCENE_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> CONTROL_VIDEO_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> DISTORTION_CORRECTION_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> EDGE_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> FLASH_STATE;
@@ -16537,58 +16631,6 @@
 
 package android.hardware.fingerprint {
 
-  public class FingerprintDialog {
-    method public void authenticate(android.hardware.fingerprint.FingerprintDialog.CryptoObject, android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
-    method public void authenticate(android.os.CancellationSignal, java.util.concurrent.Executor, android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback);
-    field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
-    field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
-    field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
-    field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
-    field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
-    field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
-    field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
-    field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
-    field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
-    field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
-    field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
-    field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
-    field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
-    field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
-    field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
-    field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
-  }
-
-  public static abstract class FingerprintDialog.AuthenticationCallback {
-    ctor public FingerprintDialog.AuthenticationCallback();
-    method public void onAuthenticationError(int, java.lang.CharSequence);
-    method public void onAuthenticationFailed();
-    method public void onAuthenticationHelp(int, java.lang.CharSequence);
-    method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintDialog.AuthenticationResult);
-  }
-
-  public static class FingerprintDialog.AuthenticationResult {
-    method public android.hardware.fingerprint.FingerprintDialog.CryptoObject getCryptoObject();
-  }
-
-  public static class FingerprintDialog.Builder {
-    ctor public FingerprintDialog.Builder();
-    method public android.hardware.fingerprint.FingerprintDialog build(android.content.Context);
-    method public android.hardware.fingerprint.FingerprintDialog.Builder setDescription(java.lang.CharSequence);
-    method public android.hardware.fingerprint.FingerprintDialog.Builder setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener);
-    method public android.hardware.fingerprint.FingerprintDialog.Builder setSubtitle(java.lang.CharSequence);
-    method public android.hardware.fingerprint.FingerprintDialog.Builder setTitle(java.lang.CharSequence);
-  }
-
-  public static final class FingerprintDialog.CryptoObject {
-    ctor public FingerprintDialog.CryptoObject(java.security.Signature);
-    ctor public FingerprintDialog.CryptoObject(javax.crypto.Cipher);
-    ctor public FingerprintDialog.CryptoObject(javax.crypto.Mac);
-    method public javax.crypto.Cipher getCipher();
-    method public javax.crypto.Mac getMac();
-    method public java.security.Signature getSignature();
-  }
-
   public deprecated class FingerprintManager {
     method public deprecated void authenticate(android.hardware.fingerprint.FingerprintManager.CryptoObject, android.os.CancellationSignal, int, android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, android.os.Handler);
     method public deprecated boolean hasEnrolledFingerprints();
@@ -21922,6 +21964,7 @@
   }
 
   public final class AudioDeviceInfo {
+    method public java.lang.String getAddress();
     method public int[] getChannelCounts();
     method public int[] getChannelIndexMasks();
     method public int[] getChannelMasks();
@@ -23419,9 +23462,6 @@
     field public static final int HDCP_V2 = 3; // 0x3
     field public static final int HDCP_V2_1 = 4; // 0x4
     field public static final int HDCP_V2_2 = 5; // 0x5
-    field public static final int HW_SECURE_ALL = 5; // 0x5
-    field public static final int HW_SECURE_CRYPTO = 3; // 0x3
-    field public static final int HW_SECURE_DECODE = 4; // 0x4
     field public static final int KEY_TYPE_OFFLINE = 2; // 0x2
     field public static final int KEY_TYPE_RELEASE = 3; // 0x3
     field public static final int KEY_TYPE_STREAMING = 1; // 0x1
@@ -23430,9 +23470,12 @@
     field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId";
     field public static final java.lang.String PROPERTY_VENDOR = "vendor";
     field public static final java.lang.String PROPERTY_VERSION = "version";
+    field public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5; // 0x5
+    field public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3; // 0x3
+    field public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4; // 0x4
+    field public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1; // 0x1
+    field public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2; // 0x2
     field public static final int SECURITY_LEVEL_UNKNOWN = 0; // 0x0
-    field public static final int SW_SECURE_CRYPTO = 1; // 0x1
-    field public static final int SW_SECURE_DECODE = 2; // 0x2
   }
 
   public final class MediaDrm.CryptoSession {
@@ -23688,6 +23731,8 @@
     field public static final java.lang.String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
     field public static final java.lang.String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
     field public static final java.lang.String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+    field public static final java.lang.String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+    field public static final java.lang.String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
     field public static final java.lang.String MIMETYPE_TEXT_VTT = "text/vtt";
     field public static final java.lang.String MIMETYPE_VIDEO_AVC = "video/avc";
     field public static final java.lang.String MIMETYPE_VIDEO_DOLBY_VISION = "video/dolby-vision";
@@ -23869,6 +23914,8 @@
     method public void addTimedTextSource(java.io.FileDescriptor, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void addTimedTextSource(java.io.FileDescriptor, long, long, java.lang.String) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public void attachAuxEffect(int);
+    method public void clearOnMediaTimeDiscontinuityListener();
+    method public void clearOnSubtitleDataListener();
     method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri);
     method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri, android.view.SurfaceHolder);
     method public static android.media.MediaPlayer create(android.content.Context, android.net.Uri, android.view.SurfaceHolder, android.media.AudioAttributes, int);
@@ -23933,8 +23980,12 @@
     method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener, android.os.Handler);
     method public void setOnErrorListener(android.media.MediaPlayer.OnErrorListener);
     method public void setOnInfoListener(android.media.MediaPlayer.OnInfoListener);
+    method public void setOnMediaTimeDiscontinuityListener(android.media.MediaPlayer.OnMediaTimeDiscontinuityListener, android.os.Handler);
+    method public void setOnMediaTimeDiscontinuityListener(android.media.MediaPlayer.OnMediaTimeDiscontinuityListener);
     method public void setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener);
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
+    method public void setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener, android.os.Handler);
+    method public void setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener);
     method public void setOnTimedMetaDataAvailableListener(android.media.MediaPlayer.OnTimedMetaDataAvailableListener);
     method public void setOnTimedTextListener(android.media.MediaPlayer.OnTimedTextListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
@@ -23961,13 +24012,14 @@
     field public static final int MEDIA_INFO_BUFFERING_START = 701; // 0x2bd
     field public static final int MEDIA_INFO_METADATA_UPDATE = 802; // 0x322
     field public static final int MEDIA_INFO_NOT_SEEKABLE = 801; // 0x321
+    field public static final int MEDIA_INFO_STARTED_AS_NEXT = 2; // 0x2
     field public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902; // 0x386
     field public static final int MEDIA_INFO_UNKNOWN = 1; // 0x1
     field public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901; // 0x385
     field public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805; // 0x325
     field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
     field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
-    field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    field public static final deprecated java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
     field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
     field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
     field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
@@ -24032,6 +24084,10 @@
     method public abstract boolean onInfo(android.media.MediaPlayer, int, int);
   }
 
+  public static abstract interface MediaPlayer.OnMediaTimeDiscontinuityListener {
+    method public abstract void onMediaTimeDiscontinuity(android.media.MediaPlayer, android.media.MediaTimestamp);
+  }
+
   public static abstract interface MediaPlayer.OnPreparedListener {
     method public abstract void onPrepared(android.media.MediaPlayer);
   }
@@ -24040,6 +24096,10 @@
     method public abstract void onSeekComplete(android.media.MediaPlayer);
   }
 
+  public static abstract interface MediaPlayer.OnSubtitleDataListener {
+    method public abstract void onSubtitleData(android.media.MediaPlayer, android.media.SubtitleData);
+  }
+
   public static abstract interface MediaPlayer.OnTimedMetaDataAvailableListener {
     method public abstract void onTimedMetaDataAvailable(android.media.MediaPlayer, android.media.TimedMetaData);
   }
@@ -24392,9 +24452,11 @@
     method public long getAnchorMediaTimeUs();
     method public long getAnchorSytemNanoTime();
     method public float getMediaClockRate();
+    field public static final android.media.MediaTimestamp TIMESTAMP_UNKNOWN;
   }
 
   public final class MicrophoneInfo {
+    method public java.lang.String getAddress();
     method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping();
     method public java.lang.String getDescription();
     method public int getDirectionality();
@@ -24652,6 +24714,13 @@
     method public abstract void onLoadComplete(android.media.SoundPool, int, int);
   }
 
+  public final class SubtitleData {
+    method public byte[] getData();
+    method public long getDurationUs();
+    method public long getStartTimeUs();
+    method public int getTrackIndex();
+  }
+
   public final class SyncParams {
     ctor public SyncParams();
     method public android.media.SyncParams allowDefaults();
@@ -25699,6 +25768,7 @@
   public final class MediaSession {
     ctor public MediaSession(android.content.Context, java.lang.String);
     method public android.media.session.MediaController getController();
+    method public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public boolean isActive();
     method public void release();
@@ -25765,6 +25835,7 @@
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName);
     method public void addOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, android.content.ComponentName, android.os.Handler);
     method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+    method public boolean isTrustedForMediaControl(android.media.session.MediaSessionManager.RemoteUserInfo);
     method public void removeOnActiveSessionsChangedListener(android.media.session.MediaSessionManager.OnActiveSessionsChangedListener);
   }
 
@@ -25772,6 +25843,13 @@
     method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
   }
 
+  public static final class MediaSessionManager.RemoteUserInfo {
+    ctor public MediaSessionManager.RemoteUserInfo(java.lang.String, int, int);
+    method public java.lang.String getPackageName();
+    method public int getPid();
+    method public int getUid();
+  }
+
   public final class PlaybackState implements android.os.Parcelable {
     method public int describeContents();
     method public long getActions();
@@ -26935,8 +27013,8 @@
 
   public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
     method public void close() throws java.io.IOException;
+    method public java.io.FileDescriptor getFileDescriptor();
     method public int getPort();
-    method public java.io.FileDescriptor getSocket();
   }
 
   public final class IpSecTransform implements java.lang.AutoCloseable {
@@ -27065,6 +27143,7 @@
     method public void bindSocket(java.net.Socket) throws java.io.IOException;
     method public void bindSocket(java.io.FileDescriptor) throws java.io.IOException;
     method public int describeContents();
+    method public static android.net.Network fromNetworkHandle(long);
     method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
     method public long getNetworkHandle();
@@ -27164,6 +27243,8 @@
 
   public class NetworkRequest implements android.os.Parcelable {
     method public int describeContents();
+    method public boolean hasCapability(int);
+    method public boolean hasTransport(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR;
   }
@@ -28587,6 +28668,8 @@
     method public int getDistanceMm();
     method public int getDistanceStdDevMm();
     method public android.net.MacAddress getMacAddress();
+    method public int getNumAttemptedMeasurements();
+    method public int getNumSuccessfulMeasurements();
     method public android.net.wifi.aware.PeerHandle getPeerHandle();
     method public long getRangingTimestampMillis();
     method public int getRssi();
@@ -32037,7 +32120,6 @@
     field public static final java.lang.String BASE_OS;
     field public static final java.lang.String CODENAME;
     field public static final java.lang.String INCREMENTAL;
-    field public static final int MIN_SUPPORTED_TARGET_SDK_INT;
     field public static final int PREVIEW_SDK_INT;
     field public static final java.lang.String RELEASE;
     field public static final deprecated java.lang.String SDK;
@@ -38324,10 +38406,8 @@
     method public void shutdown();
   }
 
-  public static abstract class SEService.SecureElementListener extends android.os.Binder {
-    ctor public SEService.SecureElementListener();
-    method public android.os.IBinder asBinder();
-    method public void serviceConnected();
+  public static abstract interface SEService.SecureElementListener {
+    method public abstract void onServiceConnected();
   }
 
   public class Session {
@@ -38337,9 +38417,7 @@
     method public android.se.omapi.Reader getReader();
     method public boolean isClosed();
     method public android.se.omapi.Channel openBasicChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openBasicChannel(byte[]) throws java.io.IOException;
     method public android.se.omapi.Channel openLogicalChannel(byte[], byte) throws java.io.IOException;
-    method public android.se.omapi.Channel openLogicalChannel(byte[]) throws java.io.IOException;
   }
 
 }
@@ -38489,6 +38567,7 @@
     method public boolean isRandomizedEncryptionRequired();
     method public boolean isStrongBoxBacked();
     method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38516,6 +38595,7 @@
     method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...);
     method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -38607,6 +38687,8 @@
     method public boolean isDigestsSpecified();
     method public boolean isInvalidatedByBiometricEnrollment();
     method public boolean isRandomizedEncryptionRequired();
+    method public boolean isTrustedUserPresenceRequired();
+    method public boolean isUnlockedDeviceRequired();
     method public boolean isUserAuthenticationRequired();
     method public boolean isUserAuthenticationValidWhileOnBody();
     method public boolean isUserConfirmationRequired();
@@ -38625,6 +38707,8 @@
     method public android.security.keystore.KeyProtection.Builder setKeyValidityStart(java.util.Date);
     method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...);
+    method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean);
+    method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean);
     method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int);
@@ -39110,6 +39194,7 @@
     ctor public MediaBrowserService();
     method public void dump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
     method public final android.os.Bundle getBrowserRootHints();
+    method public final android.media.session.MediaSessionManager.RemoteUserInfo getCurrentBrowserInfo();
     method public android.media.session.MediaSession.Token getSessionToken();
     method public void notifyChildrenChanged(java.lang.String);
     method public void notifyChildrenChanged(java.lang.String, android.os.Bundle);
@@ -41562,7 +41647,6 @@
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
-    field public static final java.lang.String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -42363,12 +42447,11 @@
     method public java.lang.String iccTransmitApduBasicChannel(int, int, int, int, int, java.lang.String);
     method public java.lang.String iccTransmitApduLogicalChannel(int, int, int, int, int, int, java.lang.String);
     method public boolean isConcurrentVoiceAndDataSupported();
-    method public deprecated boolean isDataEnabled();
+    method public boolean isDataEnabled();
     method public boolean isHearingAidCompatibilitySupported();
     method public boolean isNetworkRoaming();
     method public boolean isSmsCapable();
     method public deprecated boolean isTtyModeSupported();
-    method public boolean isUserMobileDataEnabled();
     method public boolean isVoiceCapable();
     method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
     method public boolean isWorldPhone();
@@ -42379,13 +42462,12 @@
     method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
     method public void sendUssdRequest(java.lang.String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
     method public void sendVisualVoicemailSms(java.lang.String, int, java.lang.String, android.app.PendingIntent);
-    method public deprecated void setDataEnabled(boolean);
+    method public void setDataEnabled(boolean);
     method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
     method public void setNetworkSelectionModeAutomatic();
     method public boolean setNetworkSelectionModeManual(java.lang.String, boolean);
     method public boolean setOperatorBrandOverride(java.lang.String);
     method public boolean setPreferredNetworkTypeToGlobal();
-    method public void setUserMobileDataEnabled(boolean);
     method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
     method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
     method public deprecated void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
@@ -42660,6 +42742,7 @@
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_OK = 0; // 0x0
     field public static final int EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR = 1; // 0x1
     field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
+    field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
     field public static final java.lang.String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon";
   }
 
@@ -50265,38 +50348,13 @@
 
 package android.view.textclassifier {
 
-  public abstract class Logger {
-    ctor public Logger(android.view.textclassifier.Logger.Config);
-    method public java.text.BreakIterator getTokenIterator(java.util.Locale);
-    method public boolean isSmartSelection(java.lang.String);
-    method public final void logSelectionActionEvent(int, int, int);
-    method public final void logSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
-    method public final void logSelectionModifiedEvent(int, int);
-    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification);
-    method public final void logSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection);
-    method public final void logSelectionStartedEvent(int, int);
-    method public abstract void writeEvent(android.view.textclassifier.SelectionEvent);
-    field public static final int OUT_OF_BOUNDS = 2147483647; // 0x7fffffff
-    field public static final int OUT_OF_BOUNDS_NEGATIVE = -2147483648; // 0x80000000
-    field public static final java.lang.String WIDGET_CUSTOM_EDITTEXT = "customedit";
-    field public static final java.lang.String WIDGET_CUSTOM_TEXTVIEW = "customview";
-    field public static final java.lang.String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
-    field public static final java.lang.String WIDGET_EDITTEXT = "edittext";
-    field public static final java.lang.String WIDGET_EDIT_WEBVIEW = "edit-webview";
-    field public static final java.lang.String WIDGET_TEXTVIEW = "textview";
-    field public static final java.lang.String WIDGET_UNKNOWN = "unknown";
-    field public static final java.lang.String WIDGET_UNSELECTABLE_TEXTVIEW = "nosel-textview";
-    field public static final java.lang.String WIDGET_WEBVIEW = "webview";
-  }
-
-  public static final class Logger.Config {
-    ctor public Logger.Config(android.content.Context, java.lang.String, java.lang.String);
-    method public java.lang.String getPackageName();
-    method public java.lang.String getWidgetType();
-    method public java.lang.String getWidgetVersion();
-  }
-
   public final class SelectionEvent implements android.os.Parcelable {
+    method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int);
+    method public static android.view.textclassifier.SelectionEvent createSelectionActionEvent(int, int, int, android.view.textclassifier.TextClassification);
+    method public static android.view.textclassifier.SelectionEvent createSelectionModifiedEvent(int, int);
+    method public static android.view.textclassifier.SelectionEvent createSelectionModifiedEvent(int, int, android.view.textclassifier.TextClassification);
+    method public static android.view.textclassifier.SelectionEvent createSelectionModifiedEvent(int, int, android.view.textclassifier.TextSelection);
+    method public static android.view.textclassifier.SelectionEvent createSelectionStartedEvent(int, int);
     method public int describeContents();
     method public long getDurationSincePreviousEvent();
     method public long getDurationSinceSessionStart();
@@ -50307,13 +50365,14 @@
     method public int getEventType();
     method public int getInvocationMethod();
     method public java.lang.String getPackageName();
-    method public java.lang.String getSessionId();
+    method public android.view.textclassifier.TextClassificationSessionId getSessionId();
     method public java.lang.String getSignature();
     method public int getSmartEnd();
     method public int getSmartStart();
     method public int getStart();
     method public java.lang.String getWidgetType();
     method public java.lang.String getWidgetVersion();
+    method public static boolean isTerminal(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final int ACTION_ABANDON = 107; // 0x6b
     field public static final int ACTION_COPY = 101; // 0x65
@@ -50334,21 +50393,19 @@
     field public static final int EVENT_SMART_SELECTION_SINGLE = 3; // 0x3
     field public static final int INVOCATION_LINK = 2; // 0x2
     field public static final int INVOCATION_MANUAL = 1; // 0x1
+    field public static final int INVOCATION_UNKNOWN = 0; // 0x0
   }
 
   public final class TextClassification implements android.os.Parcelable {
     method public int describeContents();
+    method public java.util.List<android.app.RemoteAction> getActions();
     method public float getConfidenceScore(java.lang.String);
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
-    method public android.graphics.drawable.Drawable getIcon();
-    method public android.content.Intent getIntent();
-    method public java.lang.CharSequence getLabel();
-    method public android.view.View.OnClickListener getOnClickListener();
-    method public int getSecondaryActionsCount();
-    method public android.graphics.drawable.Drawable getSecondaryIcon(int);
-    method public android.content.Intent getSecondaryIntent(int);
-    method public java.lang.CharSequence getSecondaryLabel(int);
+    method public deprecated android.graphics.drawable.Drawable getIcon();
+    method public deprecated android.content.Intent getIntent();
+    method public deprecated java.lang.CharSequence getLabel();
+    method public deprecated android.view.View.OnClickListener getOnClickListener();
     method public java.lang.String getSignature();
     method public java.lang.String getText();
     method public void writeToParcel(android.os.Parcel, int);
@@ -50357,15 +50414,13 @@
 
   public static final class TextClassification.Builder {
     ctor public TextClassification.Builder();
-    method public android.view.textclassifier.TextClassification.Builder addSecondaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable);
+    method public android.view.textclassifier.TextClassification.Builder addAction(android.app.RemoteAction);
     method public android.view.textclassifier.TextClassification build();
-    method public android.view.textclassifier.TextClassification.Builder clearSecondaryActions();
     method public android.view.textclassifier.TextClassification.Builder setEntityType(java.lang.String, float);
-    method public android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
-    method public android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
-    method public android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String);
-    method public android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener);
-    method public android.view.textclassifier.TextClassification.Builder setPrimaryAction(android.content.Intent, java.lang.String, android.graphics.drawable.Drawable);
+    method public deprecated android.view.textclassifier.TextClassification.Builder setIcon(android.graphics.drawable.Drawable);
+    method public deprecated android.view.textclassifier.TextClassification.Builder setIntent(android.content.Intent);
+    method public deprecated android.view.textclassifier.TextClassification.Builder setLabel(java.lang.String);
+    method public deprecated android.view.textclassifier.TextClassification.Builder setOnClickListener(android.view.View.OnClickListener);
     method public android.view.textclassifier.TextClassification.Builder setSignature(java.lang.String);
     method public android.view.textclassifier.TextClassification.Builder setText(java.lang.String);
   }
@@ -50374,26 +50429,52 @@
     ctor public TextClassification.Options();
     method public int describeContents();
     method public android.os.LocaleList getDefaultLocales();
-    method public java.util.Calendar getReferenceTime();
+    method public java.time.ZonedDateTime getReferenceTime();
     method public android.view.textclassifier.TextClassification.Options setDefaultLocales(android.os.LocaleList);
-    method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.util.Calendar);
+    method public android.view.textclassifier.TextClassification.Options setReferenceTime(java.time.ZonedDateTime);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassification.Options> CREATOR;
   }
 
+  public final class TextClassificationContext {
+    method public java.lang.String getPackageName();
+    method public java.lang.String getWidgetType();
+    method public java.lang.String getWidgetVersion();
+  }
+
+  public static final class TextClassificationContext.Builder {
+    ctor public TextClassificationContext.Builder(java.lang.String, java.lang.String);
+    method public android.view.textclassifier.TextClassificationContext build();
+    method public android.view.textclassifier.TextClassificationContext.Builder setWidgetVersion(java.lang.String);
+  }
+
   public final class TextClassificationManager {
+    method public android.view.textclassifier.TextClassifier createTextClassificationSession(android.view.textclassifier.TextClassificationContext);
     method public android.view.textclassifier.TextClassifier getTextClassifier();
+    method public void setTextClassificationSessionFactory(android.view.textclassifier.TextClassificationSessionFactory);
     method public void setTextClassifier(android.view.textclassifier.TextClassifier);
   }
 
+  public abstract interface TextClassificationSessionFactory {
+    method public abstract android.view.textclassifier.TextClassifier createTextClassificationSession(android.view.textclassifier.TextClassificationContext);
+  }
+
+  public final class TextClassificationSessionId implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextClassificationSessionId> CREATOR;
+  }
+
   public abstract interface TextClassifier {
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.view.textclassifier.TextClassification.Options);
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int);
     method public default android.view.textclassifier.TextClassification classifyText(java.lang.CharSequence, int, int, android.os.LocaleList);
+    method public default void destroy();
     method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence, android.view.textclassifier.TextLinks.Options);
     method public default android.view.textclassifier.TextLinks generateLinks(java.lang.CharSequence);
-    method public default android.view.textclassifier.Logger getLogger(android.view.textclassifier.Logger.Config);
     method public default int getMaxGenerateLinksTextLength();
+    method public default boolean isDestroyed();
+    method public default void onSelectionEvent(android.view.textclassifier.SelectionEvent);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.view.textclassifier.TextSelection.Options);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int);
     method public default android.view.textclassifier.TextSelection suggestSelection(java.lang.CharSequence, int, int, android.os.LocaleList);
@@ -50409,6 +50490,15 @@
     field public static final java.lang.String TYPE_PHONE = "phone";
     field public static final java.lang.String TYPE_UNKNOWN = "";
     field public static final java.lang.String TYPE_URL = "url";
+    field public static final java.lang.String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
+    field public static final java.lang.String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
+    field public static final java.lang.String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
+    field public static final java.lang.String WIDGET_TYPE_EDITTEXT = "edittext";
+    field public static final java.lang.String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
+    field public static final java.lang.String WIDGET_TYPE_TEXTVIEW = "textview";
+    field public static final java.lang.String WIDGET_TYPE_UNKNOWN = "unknown";
+    field public static final java.lang.String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
+    field public static final java.lang.String WIDGET_TYPE_WEBVIEW = "webview";
   }
 
   public static final class TextClassifier.EntityConfig implements android.os.Parcelable {
diff --git a/api/removed.txt b/api/removed.txt
index 64a9c3f..2d76c5a 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -54,6 +54,24 @@
 
 }
 
+package android.app.job {
+
+  public class JobInfo implements android.os.Parcelable {
+    method public deprecated long getEstimatedNetworkBytes();
+  }
+
+  public static final class JobInfo.Builder {
+    method public deprecated android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
+    method public deprecated android.app.job.JobInfo.Builder setIsPrefetch(boolean);
+  }
+
+  public final class JobWorkItem implements android.os.Parcelable {
+    ctor public deprecated JobWorkItem(android.content.Intent, long);
+    method public deprecated long getEstimatedNetworkBytes();
+  }
+
+}
+
 package android.app.usage {
 
   public final class StorageStats implements android.os.Parcelable {
@@ -150,11 +168,30 @@
   public class Canvas {
     method public deprecated boolean clipRegion(android.graphics.Region, android.graphics.Region.Op);
     method public deprecated boolean clipRegion(android.graphics.Region);
+    method public deprecated int save(int);
+    method public deprecated int saveLayer(android.graphics.RectF, android.graphics.Paint, int);
+    method public deprecated int saveLayer(float, float, float, float, android.graphics.Paint, int);
+    method public deprecated int saveLayerAlpha(android.graphics.RectF, int, int);
+    method public deprecated int saveLayerAlpha(float, float, float, float, int, int);
+    field public static final int ALL_SAVE_FLAG = 31; // 0x1f
+    field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
+    field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
+    field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
+    field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
+    field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
   }
 
   public final class ImageDecoder implements java.lang.AutoCloseable {
     method public deprecated boolean getAsAlphaMask();
+    method public deprecated boolean getDecodeAsAlphaMask();
+    method public deprecated boolean getMutable();
+    method public deprecated boolean getRequireUnpremultiplied();
     method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
+    method public deprecated android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
+    method public deprecated android.graphics.ImageDecoder setMutable(boolean);
+    method public deprecated android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
+    method public deprecated android.graphics.ImageDecoder setResize(int, int);
+    method public deprecated android.graphics.ImageDecoder setResize(int);
     field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
     field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
     field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index e049f53..1284de88 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -57,6 +57,7 @@
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
     field public static final java.lang.String CONNECTIVITY_INTERNAL = "android.permission.CONNECTIVITY_INTERNAL";
     field public static final java.lang.String CONNECTIVITY_USE_RESTRICTED_NETWORKS = "android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS";
+    field public static final java.lang.String CONTROL_DISPLAY_SATURATION = "android.permission.CONTROL_DISPLAY_SATURATION";
     field public static final java.lang.String CONTROL_INCALL_EXPERIENCE = "android.permission.CONTROL_INCALL_EXPERIENCE";
     field public static final java.lang.String CONTROL_LOCATION_UPDATES = "android.permission.CONTROL_LOCATION_UPDATES";
     field public static final java.lang.String CONTROL_VPN = "android.permission.CONTROL_VPN";
@@ -400,10 +401,8 @@
   }
 
   public class WallpaperManager {
-    method public void clearWallpaper();
     method public void clearWallpaper(int, int);
     method public void setDisplayOffset(android.os.IBinder, int, int);
-    method public void setDisplayPadding(android.graphics.Rect);
     method public boolean setWallpaperComponent(android.content.ComponentName);
   }
 
@@ -1254,6 +1253,7 @@
     method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
     method public android.graphics.Point getStableDisplaySize();
     method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
+    method public void setSaturationLevel(float);
   }
 
 }
@@ -2103,6 +2103,7 @@
     method public java.lang.String getVersion();
     method public boolean isBackgroundScanningSupported();
     method public boolean isCaptureSupported();
+    method public boolean isInitializationRequired();
     method public boolean isProgramIdentifierSupported(int);
     method public boolean isProgramTypeSupported(int);
     method public void writeToParcel(android.os.Parcel, int);
@@ -3104,6 +3105,10 @@
     field public static final int ERROR_INVALID_NETWORK = 1; // 0x1
   }
 
+  public final class NetworkCapabilities implements android.os.Parcelable {
+    field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
+  }
+
   public class NetworkKey implements android.os.Parcelable {
     ctor public NetworkKey(android.net.WifiKey);
     method public int describeContents();
@@ -5503,7 +5508,6 @@
     field public static final int EUICC_OTA_NOT_NEEDED = 4; // 0x4
     field public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5; // 0x5
     field public static final int EUICC_OTA_SUCCEEDED = 3; // 0x3
-    field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
     field public static final java.lang.String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS = "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTIONS";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index d323725..b520dfb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1,8 +1,10 @@
 package android {
 
   public static final class Manifest.permission {
+    field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
     field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
+    field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
   }
 
 }
@@ -127,8 +129,8 @@
   public final class UiAutomation {
     method public void destroy();
     method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
-    method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
-    method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
   }
 
   public class UiModeManager {
diff --git a/cmds/incidentd/src/FdBuffer.cpp b/cmds/incidentd/src/FdBuffer.cpp
index 2b85ec0..c6e561f 100644
--- a/cmds/incidentd/src/FdBuffer.cpp
+++ b/cmds/incidentd/src/FdBuffer.cpp
@@ -34,11 +34,11 @@
 
 FdBuffer::~FdBuffer() {}
 
-status_t FdBuffer::read(unique_fd* fd, int64_t timeout) {
-    struct pollfd pfds = {.fd = fd->get(), .events = POLLIN};
+status_t FdBuffer::read(int fd, int64_t timeout) {
+    struct pollfd pfds = {.fd = fd, .events = POLLIN};
     mStartTime = uptimeMillis();
 
-    fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
 
     while (true) {
         if (mBuffer.size() >= MAX_BUFFER_COUNT * BUFFER_SIZE) {
@@ -67,16 +67,16 @@
                 VLOG("return event has error %s", strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             } else {
-                ssize_t amt = ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
+                ssize_t amt = ::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite());
                 if (amt < 0) {
                     if (errno == EAGAIN || errno == EWOULDBLOCK) {
                         continue;
                     } else {
-                        VLOG("Fail to read %d: %s", fd->get(), strerror(errno));
+                        VLOG("Fail to read %d: %s", fd, strerror(errno));
                         return -errno;
                     }
                 } else if (amt == 0) {
-                    VLOG("Reached EOF of fd=%d", fd->get());
+                    VLOG("Reached EOF of fd=%d", fd);
                     break;
                 }
                 mBuffer.wp()->move(amt);
@@ -87,7 +87,7 @@
     return NO_ERROR;
 }
 
-status_t FdBuffer::readFully(unique_fd* fd) {
+status_t FdBuffer::readFully(int fd) {
     mStartTime = uptimeMillis();
 
     while (true) {
@@ -99,10 +99,10 @@
         }
         if (mBuffer.writeBuffer() == NULL) return NO_MEMORY;
 
-        ssize_t amt = TEMP_FAILURE_RETRY(
-                ::read(fd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite()));
+        ssize_t amt =
+                TEMP_FAILURE_RETRY(::read(fd, mBuffer.writeBuffer(), mBuffer.currentToWrite()));
         if (amt < 0) {
-            VLOG("Fail to read %d: %s", fd->get(), strerror(errno));
+            VLOG("Fail to read %d: %s", fd, strerror(errno));
             return -errno;
         } else if (amt == 0) {
             VLOG("Done reading %zu bytes", mBuffer.size());
@@ -116,20 +116,20 @@
     return NO_ERROR;
 }
 
-status_t FdBuffer::readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd,
+status_t FdBuffer::readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd,
                                              int64_t timeoutMs, const bool isSysfs) {
     struct pollfd pfds[] = {
-            {.fd = fd->get(), .events = POLLIN},
-            {.fd = toFd->get(), .events = POLLOUT},
-            {.fd = fromFd->get(), .events = POLLIN},
+            {.fd = fd, .events = POLLIN},
+            {.fd = toFd.get(), .events = POLLOUT},
+            {.fd = fromFd.get(), .events = POLLIN},
     };
 
     mStartTime = uptimeMillis();
 
     // mark all fds non blocking
-    fcntl(fd->get(), F_SETFL, fcntl(fd->get(), F_GETFL, 0) | O_NONBLOCK);
-    fcntl(toFd->get(), F_SETFL, fcntl(toFd->get(), F_GETFL, 0) | O_NONBLOCK);
-    fcntl(fromFd->get(), F_SETFL, fcntl(fromFd->get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+    fcntl(toFd.get(), F_SETFL, fcntl(toFd.get(), F_GETFL, 0) | O_NONBLOCK);
+    fcntl(fromFd.get(), F_SETFL, fcntl(fromFd.get(), F_GETFL, 0) | O_NONBLOCK);
 
     // A circular buffer holds data read from fd and writes to parsing process
     uint8_t cirBuf[BUFFER_SIZE];
@@ -166,10 +166,10 @@
         for (int i = 0; i < 3; ++i) {
             if ((pfds[i].revents & POLLERR) != 0) {
                 if (i == 0 && isSysfs) {
-                    VLOG("fd %d is sysfs, ignore its POLLERR return value", fd->get());
+                    VLOG("fd %d is sysfs, ignore its POLLERR return value", fd);
                     continue;
                 }
-                VLOG("fd[%d]=%d returns error events: %s", i, fd->get(), strerror(errno));
+                VLOG("fd[%d]=%d returns error events: %s", i, fd, strerror(errno));
                 return errno != 0 ? -errno : UNKNOWN_ERROR;
             }
         }
@@ -178,17 +178,17 @@
         if (cirSize != BUFFER_SIZE && pfds[0].fd != -1) {
             ssize_t amt;
             if (rpos >= wpos) {
-                amt = ::read(fd->get(), cirBuf + rpos, BUFFER_SIZE - rpos);
+                amt = ::read(fd, cirBuf + rpos, BUFFER_SIZE - rpos);
             } else {
-                amt = ::read(fd->get(), cirBuf + rpos, wpos - rpos);
+                amt = ::read(fd, cirBuf + rpos, wpos - rpos);
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    VLOG("Fail to read fd %d: %s", fd->get(), strerror(errno));
+                    VLOG("Fail to read fd %d: %s", fd, strerror(errno));
                     return -errno;
                 }  // otherwise just continue
             } else if (amt == 0) {
-                VLOG("Reached EOF of input file %d", fd->get());
+                VLOG("Reached EOF of input file %d", fd);
                 pfds[0].fd = -1;  // reach EOF so don't have to poll pfds[0].
             } else {
                 rpos += amt;
@@ -200,13 +200,13 @@
         if (cirSize > 0 && pfds[1].fd != -1) {
             ssize_t amt;
             if (rpos > wpos) {
-                amt = ::write(toFd->get(), cirBuf + wpos, rpos - wpos);
+                amt = ::write(toFd.get(), cirBuf + wpos, rpos - wpos);
             } else {
-                amt = ::write(toFd->get(), cirBuf + wpos, BUFFER_SIZE - wpos);
+                amt = ::write(toFd.get(), cirBuf + wpos, BUFFER_SIZE - wpos);
             }
             if (amt < 0) {
                 if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                    VLOG("Fail to write toFd %d: %s", toFd->get(), strerror(errno));
+                    VLOG("Fail to write toFd.get() %d: %s", toFd.get(), strerror(errno));
                     return -errno;
                 }  // otherwise just continue
             } else {
@@ -217,8 +217,8 @@
 
         // if buffer is empty and fd is closed, close write fd.
         if (cirSize == 0 && pfds[0].fd == -1 && pfds[1].fd != -1) {
-            VLOG("Close write pipe %d", toFd->get());
-            toFd->reset();
+            VLOG("Close write pipe %d", toFd.get());
+            toFd.reset();
             pfds[1].fd = -1;
         }
 
@@ -231,14 +231,14 @@
         }
 
         // read from parsing process
-        ssize_t amt = ::read(fromFd->get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
+        ssize_t amt = ::read(fromFd.get(), mBuffer.writeBuffer(), mBuffer.currentToWrite());
         if (amt < 0) {
             if (!(errno == EAGAIN || errno == EWOULDBLOCK)) {
-                VLOG("Fail to read fromFd %d: %s", fromFd->get(), strerror(errno));
+                VLOG("Fail to read fromFd.get() %d: %s", fromFd.get(), strerror(errno));
                 return -errno;
             }  // otherwise just continue
         } else if (amt == 0) {
-            VLOG("Reached EOF of fromFd %d", fromFd->get());
+            VLOG("Reached EOF of fromFd.get() %d", fromFd.get());
             break;
         } else {
             mBuffer.wp()->move(amt);
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index db3a74b..f467da8 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -40,13 +40,13 @@
      * Returns NO_ERROR if there were no errors or if we timed out.
      * Will mark the file O_NONBLOCK.
      */
-    status_t read(unique_fd* fd, int64_t timeoutMs);
+    status_t read(int fd, int64_t timeoutMs);
 
     /**
      * Read the data until we hit eof.
      * Returns NO_ERROR if there were no errors.
      */
-    status_t readFully(unique_fd* fd);
+    status_t readFully(int fd);
 
     /**
      * Read processed results by streaming data to a parsing process, e.g. incident helper.
@@ -58,8 +58,8 @@
      *
      * Poll will return POLLERR if fd is from sysfs, handle this edge case.
      */
-    status_t readProcessedDataInStream(unique_fd* fd, unique_fd* toFd, unique_fd* fromFd,
-                                       int64_t timeoutMs, const bool isSysfs = false);
+    status_t readProcessedDataInStream(int fd, unique_fd toFd, unique_fd fromFd, int64_t timeoutMs,
+                                       const bool isSysfs = false);
 
     /**
      * Whether we timed out.
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index aeccefd..d02b4dd 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -352,8 +352,7 @@
             printPrivacy(p, out, String8(""));
         } else if (opt == "parse") {
             FdBuffer buf;
-            unique_fd infd(fileno(in));
-            status_t error = buf.read(&infd, 60000);
+            status_t error = buf.read(fileno(in), 60000);
             if (error != NO_ERROR) {
                 fprintf(err, "Error reading from stdin\n");
                 return error;
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index ece3083..3f693fa 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -20,7 +20,6 @@
 
 #include <dirent.h>
 #include <errno.h>
-#include <wait.h>
 
 #include <mutex>
 #include <set>
@@ -53,49 +52,15 @@
 const int FIELD_ID_INCIDENT_METADATA = 2;
 
 // incident section parameters
-const int WAIT_MAX = 5;
-const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
 const char INCIDENT_HELPER[] = "/system/bin/incident_helper";
-const char GZIP[] = "/system/bin/gzip";
+const char* GZIP[] = {"/system/bin/gzip", NULL};
 
 static pid_t fork_execute_incident_helper(const int id, Fpipe* p2cPipe, Fpipe* c2pPipe) {
     const char* ihArgs[]{INCIDENT_HELPER, "-s", String8::format("%d", id).string(), NULL};
-    return fork_execute_cmd(INCIDENT_HELPER, const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
+    return fork_execute_cmd(const_cast<char**>(ihArgs), p2cPipe, c2pPipe);
 }
 
 // ================================================================================
-static status_t statusCode(int status) {
-    if (WIFSIGNALED(status)) {
-        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
-        return -WTERMSIG(status);
-    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
-        VLOG("return by exit: %s", strerror(WEXITSTATUS(status)));
-        return -WEXITSTATUS(status);
-    }
-    return NO_ERROR;
-}
-
-static status_t kill_child(pid_t pid) {
-    int status;
-    VLOG("try to kill child process %d", pid);
-    kill(pid, SIGKILL);
-    if (waitpid(pid, &status, 0) == -1) return -1;
-    return statusCode(status);
-}
-
-static status_t wait_child(pid_t pid) {
-    int status;
-    bool died = false;
-    // wait for child to report status up to 1 seconds
-    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
-        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
-        // sleep for 0.2 second
-        nanosleep(&WAIT_INTERVAL_NS, NULL);
-    }
-    if (!died) return kill_child(pid);
-    return statusCode(status);
-}
-// ================================================================================
 static status_t write_section_header(int fd, int sectionId, size_t size) {
     uint8_t buf[20];
     uint8_t* p = write_length_delimited_tag_header(buf, sectionId, size);
@@ -299,8 +264,9 @@
     }
 
     // parent process
-    status_t readStatus = buffer.readProcessedDataInStream(
-            &fd, &p2cPipe.writeFd(), &c2pPipe.readFd(), this->timeoutMs, mIsSysfs);
+    status_t readStatus = buffer.readProcessedDataInStream(fd.get(), std::move(p2cPipe.writeFd()),
+                                                           std::move(c2pPipe.readFd()),
+                                                           this->timeoutMs, mIsSysfs);
 
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s",
@@ -328,12 +294,15 @@
 }
 // ================================================================================
 GZipSection::GZipSection(int id, const char* filename, ...) : Section(id) {
-    name = "gzip ";
-    name += filename;
     va_list args;
     va_start(args, filename);
     mFilenames = varargs(filename, args);
     va_end(args);
+    name = "gzip";
+    for (int i = 0; mFilenames[i] != NULL; i++) {
+        name += " ";
+        name += mFilenames[i];
+    }
 }
 
 GZipSection::~GZipSection() {}
@@ -351,8 +320,10 @@
         index++;  // look at the next file.
     }
     VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd.get());
-    if (fd.get() == -1) return -1;
-
+    if (fd.get() == -1) {
+      ALOGW("GZipSection %s can't open all the files", this->name.string());
+      return NO_ERROR; // e.g. LAST_KMSG will reach here in user build.
+    }
     FdBuffer buffer;
     Fpipe p2cPipe;
     Fpipe c2pPipe;
@@ -362,8 +333,7 @@
         return -errno;
     }
 
-    const char* gzipArgs[]{GZIP, NULL};
-    pid_t pid = fork_execute_cmd(GZIP, const_cast<char**>(gzipArgs), &p2cPipe, &c2pPipe);
+    pid_t pid = fork_execute_cmd((char* const*)GZIP, &p2cPipe, &c2pPipe);
     if (pid == -1) {
         ALOGW("GZipSection '%s' failed to fork", this->name.string());
         return -errno;
@@ -387,9 +357,9 @@
     VLOG("GZipSection '%s' editPos=%zd, dataBeginAt=%zd", this->name.string(), editPos,
          dataBeginAt);
 
-    status_t readStatus =
-            buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(), &c2pPipe.readFd(),
-                                             this->timeoutMs, isSysfs(mFilenames[index]));
+    status_t readStatus = buffer.readProcessedDataInStream(
+            fd.get(), std::move(p2cPipe.writeFd()), std::move(c2pPipe.readFd()), this->timeoutMs,
+            isSysfs(mFilenames[index]));
 
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("GZipSection '%s' failed to read data from gzip: %s, timedout: %s",
@@ -499,7 +469,7 @@
     pthread_attr_destroy(&attr);
 
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
-    err = buffer.read(&data->pipe.readFd(), this->timeoutMs);
+    err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
     if (err != NO_ERROR) {
         // TODO: Log this error into the incident report.
         ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
@@ -559,19 +529,27 @@
 // ================================================================================
 CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
     : Section(id, timeoutMs) {
-    name = command;
     va_list args;
     va_start(args, command);
     mCommand = varargs(command, args);
     va_end(args);
+    name = "cmd";
+    for (int i = 0; mCommand[i] != NULL; i++) {
+        name += " ";
+        name += mCommand[i];
+    }
 }
 
 CommandSection::CommandSection(int id, const char* command, ...) : Section(id) {
-    name = command;
     va_list args;
     va_start(args, command);
     mCommand = varargs(command, args);
     va_end(args);
+    name = "cmd";
+    for (int i = 0; mCommand[i] != NULL; i++) {
+        name += " ";
+        name += mCommand[i];
+    }
 }
 
 CommandSection::~CommandSection() { free(mCommand); }
@@ -586,26 +564,11 @@
         return -errno;
     }
 
-    pid_t cmdPid = fork();
+    pid_t cmdPid = fork_execute_cmd((char* const*)mCommand, NULL, &cmdPipe);
     if (cmdPid == -1) {
         ALOGW("CommandSection '%s' failed to fork", this->name.string());
         return -errno;
     }
-    // child process to execute the command as root
-    if (cmdPid == 0) {
-        // replace command's stdout with ihPipe's write Fd
-        if (dup2(cmdPipe.writeFd().get(), STDOUT_FILENO) != 1 || !ihPipe.close() ||
-            !cmdPipe.close()) {
-            ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(),
-                  strerror(errno));
-            _exit(EXIT_FAILURE);
-        }
-        execvp(this->mCommand[0], (char* const*)this->mCommand);
-        int err = errno;  // record command error code
-        ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(),
-              strerror(errno));
-        _exit(err);  // exit with command error code
-    }
     pid_t ihPid = fork_execute_incident_helper(this->id, &cmdPipe, &ihPipe);
     if (ihPid == -1) {
         ALOGW("CommandSection '%s' failed to fork", this->name.string());
@@ -613,7 +576,7 @@
     }
 
     cmdPipe.writeFd().reset();
-    status_t readStatus = buffer.read(&ihPipe.readFd(), this->timeoutMs);
+    status_t readStatus = buffer.read(ihPipe.readFd().get(), this->timeoutMs);
     if (readStatus != NO_ERROR || buffer.timedOut()) {
         ALOGW("CommandSection '%s' failed to read data from incident helper: %s, timedout: %s",
               this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false");
@@ -932,7 +895,7 @@
         // Parent process.
         // Read from the pipe concurrently to avoid blocking the child.
         FdBuffer buffer;
-        err = buffer.readFully(&dumpPipe.readFd());
+        err = buffer.readFully(dumpPipe.readFd().get());
         if (err != NO_ERROR) {
             ALOGW("TombstoneSection '%s' failed to read stack dump: %d", this->name.string(), err);
             dumpPipe.readFd().reset();
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index d799513..7db1fa7 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -19,6 +19,7 @@
 #include "incidentd_util.h"
 
 #include <sys/prctl.h>
+#include <wait.h>
 
 #include "section_list.h"
 
@@ -57,27 +58,28 @@
 
 unique_fd& Fpipe::writeFd() { return mWrite; }
 
-pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output) {
+pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output) {
     // fork used in multithreaded environment, avoid adding unnecessary code in child process
     pid_t pid = fork();
     if (pid == 0) {
-        if (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 || !input->close() ||
-            TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
-            !output->close()) {
-            ALOGW("Can't setup stdin and stdout for command %s", cmd);
+        VLOG("[In child]cmd %s", argv[0]);
+        if (input != NULL && (TEMP_FAILURE_RETRY(dup2(input->readFd().get(), STDIN_FILENO)) < 0 ||
+                              !input->close())) {
+            ALOGW("Failed to dup2 stdin.");
             _exit(EXIT_FAILURE);
         }
-
+        if (TEMP_FAILURE_RETRY(dup2(output->writeFd().get(), STDOUT_FILENO)) < 0 ||
+            !output->close()) {
+            ALOGW("Failed to dup2 stdout.");
+            _exit(EXIT_FAILURE);
+        }
         /* make sure the child dies when incidentd dies */
         prctl(PR_SET_PDEATHSIG, SIGKILL);
-
-        execv(cmd, argv);
-
-        ALOGW("%s failed in the child process: %s", cmd, strerror(errno));
-        _exit(EXIT_FAILURE);  // always exits with failure if any
+        execvp(argv[0], argv);
+        _exit(errno);  // always exits with failure if any
     }
     // close the fds used in child process.
-    input->readFd().reset();
+    if (input != NULL) input->readFd().reset();
     output->writeFd().reset();
     return pid;
 }
@@ -111,3 +113,39 @@
     clock_gettime(CLOCK_MONOTONIC, &ts);
     return static_cast<uint64_t>(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec);
 }
+
+// ================================================================================
+const int WAIT_MAX = 5;
+const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
+
+static status_t statusCode(int status) {
+    if (WIFSIGNALED(status)) {
+        VLOG("return by signal: %s", strerror(WTERMSIG(status)));
+        return -WTERMSIG(status);
+    } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) {
+        VLOG("return by exit: %s", strerror(WEXITSTATUS(status)));
+        return -WEXITSTATUS(status);
+    }
+    return NO_ERROR;
+}
+
+status_t kill_child(pid_t pid) {
+    int status;
+    VLOG("try to kill child process %d", pid);
+    kill(pid, SIGKILL);
+    if (waitpid(pid, &status, 0) == -1) return -1;
+    return statusCode(status);
+}
+
+status_t wait_child(pid_t pid) {
+    int status;
+    bool died = false;
+    // wait for child to report status up to 1 seconds
+    for (int loop = 0; !died && loop < WAIT_MAX; loop++) {
+        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
+        // sleep for 0.2 second
+        nanosleep(&WAIT_INTERVAL_NS, NULL);
+    }
+    if (!died) return kill_child(pid);
+    return statusCode(status);
+}
diff --git a/cmds/incidentd/src/incidentd_util.h b/cmds/incidentd/src/incidentd_util.h
index 228d776..b5f6e21 100644
--- a/cmds/incidentd/src/incidentd_util.h
+++ b/cmds/incidentd/src/incidentd_util.h
@@ -18,12 +18,15 @@
 #ifndef INCIDENTD_UTIL_H
 #define INCIDENTD_UTIL_H
 
-#include <android-base/unique_fd.h>
-
 #include <stdarg.h>
+#include <unistd.h>
+
+#include <android-base/unique_fd.h>
+#include <utils/Errors.h>
 
 #include "Privacy.h"
 
+using namespace android;
 using namespace android::base;
 
 /**
@@ -52,8 +55,9 @@
 /**
  * Forks and exec a command with two pipes, one connects stdin for input,
  * one connects stdout for output. It returns the pid of the child.
+ * Input pipe can be NULL to indicate child process doesn't read stdin.
  */
-pid_t fork_execute_cmd(const char* cmd, char* const argv[], Fpipe* input, Fpipe* output);
+pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output);
 
 /**
  * Grabs varargs from stack and stores them in heap with NULL-terminated array.
@@ -65,4 +69,10 @@
  */
 uint64_t Nanotime();
 
+/**
+ * Methods to wait or kill child process, return exit status code.
+ */
+status_t kill_child(pid_t pid);
+status_t wait_child(pid_t pid);
+
 #endif  // INCIDENTD_UTIL_H
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index bf77017..7a05d7e 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -37,7 +37,6 @@
 public:
     virtual void SetUp() override {
         ASSERT_NE(tf.fd, -1);
-        tffd.reset(tf.fd);
         ASSERT_NE(p2cPipe.init(), -1);
         ASSERT_NE(c2pPipe.init(), -1);
     }
@@ -57,13 +56,13 @@
         EXPECT_EQ(expected[i], '\0');
     }
 
-    bool DoDataStream(unique_fd* rFd, unique_fd* wFd) {
+    bool DoDataStream(const unique_fd& rFd, const unique_fd& wFd) {
         char buf[BUFFER_SIZE];
         ssize_t nRead;
-        while ((nRead = read(rFd->get(), buf, BUFFER_SIZE)) > 0) {
+        while ((nRead = read(rFd.get(), buf, BUFFER_SIZE)) > 0) {
             ssize_t nWritten = 0;
             while (nWritten < nRead) {
-                ssize_t amt = write(wFd->get(), buf + nWritten, nRead - nWritten);
+                ssize_t amt = write(wFd.get(), buf + nWritten, nRead - nWritten);
                 if (amt < 0) {
                     return false;
                 }
@@ -76,7 +75,6 @@
 protected:
     FdBuffer buffer;
     TemporaryFile tf;
-    unique_fd tffd;
     Fpipe p2cPipe;
     Fpipe c2pPipe;
 
@@ -87,7 +85,7 @@
 TEST_F(FdBufferTest, ReadAndWrite) {
     std::string testdata = "FdBuffer test string";
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
-    ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT));
+    ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
     AssertBufferReadSuccessful(testdata.size());
     AssertBufferContent(testdata.c_str());
 }
@@ -100,7 +98,7 @@
 TEST_F(FdBufferTest, ReadAndIterate) {
     std::string testdata = "FdBuffer test string";
     ASSERT_TRUE(WriteStringToFile(testdata, tf.path));
-    ASSERT_EQ(NO_ERROR, buffer.read(&tffd, READ_TIMEOUT));
+    ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, READ_TIMEOUT));
 
     int i = 0;
     EncodedBuffer::iterator it = buffer.data();
@@ -128,7 +126,7 @@
     } else {
         c2pPipe.writeFd().reset();
 
-        status_t status = buffer.read(&c2pPipe.readFd(), QUICK_TIMEOUT_MS);
+        status_t status = buffer.read(c2pPipe.readFd().get(), QUICK_TIMEOUT_MS);
         ASSERT_EQ(NO_ERROR, status);
         EXPECT_TRUE(buffer.timedOut());
 
@@ -148,7 +146,7 @@
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
         ASSERT_TRUE(WriteStringToFd(HEAD, c2pPipe.writeFd()));
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         // Must exit here otherwise the child process will continue executing the test binary.
@@ -157,8 +155,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -189,8 +188,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(HEAD.size() + testdata.size());
         AssertBufferContent(expected.c_str());
         wait(&pid);
@@ -206,7 +206,7 @@
     if (pid == 0) {
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         _exit(EXIT_SUCCESS);
@@ -214,8 +214,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         AssertBufferReadSuccessful(0);
         AssertBufferContent("");
         wait(&pid);
@@ -233,7 +234,7 @@
     if (pid == 0) {
         p2cPipe.writeFd().reset();
         c2pPipe.readFd().reset();
-        ASSERT_TRUE(DoDataStream(&p2cPipe.readFd(), &c2pPipe.writeFd()));
+        ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
         _exit(EXIT_SUCCESS);
@@ -241,8 +242,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&fd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), READ_TIMEOUT));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), READ_TIMEOUT));
         EXPECT_EQ(buffer.size(), fourMB);
         EXPECT_FALSE(buffer.timedOut());
         EXPECT_TRUE(buffer.truncated());
@@ -278,8 +280,9 @@
         p2cPipe.readFd().reset();
         c2pPipe.writeFd().reset();
 
-        ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(&tffd, &p2cPipe.writeFd(),
-                                                             &c2pPipe.readFd(), QUICK_TIMEOUT_MS));
+        ASSERT_EQ(NO_ERROR,
+                  buffer.readProcessedDataInStream(tf.fd, std::move(p2cPipe.writeFd()),
+                                                   std::move(c2pPipe.readFd()), QUICK_TIMEOUT_MS));
         EXPECT_TRUE(buffer.timedOut());
         kill(pid, SIGKILL);  // reap the child process
     }
diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
index 5edc0c7..10c2981 100644
--- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp
+++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp
@@ -38,7 +38,7 @@
 const uint8_t MESSAGE_TYPE = 11;
 const string STRING_FIELD_0 = "\x02\viamtestdata";
 const string VARINT_FIELD_1 = "\x08\x96\x01";  // 150
-const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string STRING_FIELD_2 = "\x12\vandroidwins";
 const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff";  // -1
 const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff";                  // -1
 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2;
@@ -58,8 +58,7 @@
 
     void writeToFdBuffer(string str) {
         ASSERT_TRUE(WriteStringToFile(str, tf.path));
-        unique_fd tffd(tf.fd);
-        ASSERT_EQ(NO_ERROR, buffer.read(&tffd, 10000));
+        ASSERT_EQ(NO_ERROR, buffer.read(tf.fd, 10000));
         ASSERT_EQ(str.size(), buffer.size());
     }
 
@@ -274,4 +273,4 @@
     autoMsg->children = list;
     string expected = "\x2a\xd" + STRING_FIELD_2;
     assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg);
-}
\ No newline at end of file
+}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 55192d0..2f6698b 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -19,6 +19,7 @@
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
 #include <android/os/IncidentReportArgs.h>
+#include <android/util/protobuf.h>
 #include <frameworks/base/libs/incident/proto/android/os/header.pb.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -31,12 +32,13 @@
 const int QUICK_TIMEOUT_MS = 100;
 
 const string VARINT_FIELD_1 = "\x08\x96\x01";  // 150
-const string STRING_FIELD_2 = "\x12\vwhatthefuck";
+const string STRING_FIELD_2 = "\x12\vandroidwins";
 const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff";  // -1
 
 using namespace android::base;
 using namespace android::binder;
 using namespace android::os;
+using namespace android::util;
 using namespace std;
 using ::testing::StrEq;
 using ::testing::Test;
@@ -154,17 +156,26 @@
     requests.setMainDest(android::os::DEST_LOCAL);
 
     ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
-    std::string expect, gzFile, actual;
+    std::string expected, gzFile, actual;
     ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile));
     ASSERT_TRUE(ReadFileToString(tf.path, &actual));
-    expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile;
-    EXPECT_THAT(actual, StrEq(expect));
+    // generates the expected protobuf result.
+    size_t fileLen = testFile.size();
+    size_t totalLen = 1 + get_varint_size(fileLen) + fileLen + 3 + gzFile.size();
+    uint8_t header[20];
+    header[0] = '\x2'; // header 0 << 3 + 2
+    uint8_t* ptr = write_raw_varint(header + 1, totalLen);
+    *ptr = '\n'; // header 1 << 3 + 2
+    ptr = write_raw_varint(++ptr, fileLen);
+    expected.assign((const char*)header, ptr - header);
+    expected += testFile + "\x12\x9F\x6" + gzFile;
+    EXPECT_THAT(actual, StrEq(expected));
 }
 
 TEST_F(SectionTest, GZipSectionNoFileFound) {
     GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL);
     requests.setMainFd(STDOUT_FILENO);
-    ASSERT_EQ(-1, gs.Execute(&requests));
+    ASSERT_EQ(NO_ERROR, gs.Execute(&requests));
 }
 
 TEST_F(SectionTest, CommandSectionConstructor) {
@@ -173,12 +184,12 @@
     CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
     CommandSection cs4(2, 43214, "single_command", NULL);
 
-    EXPECT_THAT(cs1.name.string(), StrEq("echo"));
-    EXPECT_THAT(cs2.name.string(), StrEq("single_command"));
+    EXPECT_THAT(cs1.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs2.name.string(), StrEq("cmd single_command"));
     EXPECT_EQ(3123, cs3.timeoutMs);
     EXPECT_EQ(43214, cs4.timeoutMs);
-    EXPECT_THAT(cs3.name.string(), StrEq("echo"));
-    EXPECT_THAT(cs4.name.string(), StrEq("single_command"));
+    EXPECT_THAT(cs3.name.string(), StrEq("cmd echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs4.name.string(), StrEq("cmd single_command"));
 }
 
 TEST_F(SectionTest, CommandSectionEcho) {
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 9df229c..6676196 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -264,13 +264,13 @@
                     } else if ("q".equals(line) || "quit".equals(line)) {
                         break;
                     } else if ("play".equals(line)) {
-                        mController.play();
+                        mController.play("");
                     } else if ("pause".equals(line)) {
-                        mController.pause();
+                        mController.pause("");
                     } else if ("next".equals(line)) {
-                        mController.next();
+                        mController.next("");
                     } else if ("previous".equals(line)) {
-                        mController.previous();
+                        mController.previous("");
                     } else {
                         System.out.println("Invalid command: " + line);
                     }
diff --git a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
index b76d669..37b7acf 100644
--- a/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
+++ b/cmds/requestsync/src/com/android/commands/requestsync/RequestSync.java
@@ -29,23 +29,21 @@
     private String[] mArgs;
     private int mNextArg;
     private String mCurArgData;
-    private boolean mIsForegroundRequest;
+
+    private int mExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
 
     enum Operation {
         REQUEST_SYNC {
             @Override
             void invoke(RequestSync caller) {
-                if (caller.mIsForegroundRequest) {
-                    caller.mExtras.putBoolean(
-                            ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC, true);
-                } else {
-                    caller.mExtras.putBoolean(
-                            ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC, true);
+                final int flag = caller.mExemptionFlag;
+                caller.mExtras.putInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, flag);
+                if (flag == ContentResolver.SYNC_EXEMPTION_NONE) {
                     System.out.println(
                             "Making a sync request as a background app.\n"
                             + "Note: request may be throttled by App Standby.\n"
                             + "To override this behavior and run a sync immediately,"
-                            + " pass a -f option.\n");
+                            + " pass a -f or -F option (use -h for help).\n");
                 }
                 final SyncRequest request =
                         new SyncRequest.Builder()
@@ -213,7 +211,10 @@
                 mExtras.putBoolean(key, Boolean.valueOf(value));
 
             } else if (opt.equals("-f") || opt.equals("--foreground")) {
-                mIsForegroundRequest = true;
+                mExemptionFlag = ContentResolver.SYNC_EXEMPTION_ACTIVE;
+
+            } else if (opt.equals("-F") || opt.equals("--top")) {
+                mExemptionFlag = ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
 
             } else {
                 System.err.println("Error: Unknown option: " + opt);
@@ -293,7 +294,9 @@
                 "       -a|--authority <AUTHORITY>\n" +
                 "    App-standby related options\n" +
                 "\n" +
-                "       -f|--foreground (Exempt a sync from app standby)\n" +
+                "       -f|--foreground (cause WORKING_SET, FREQUENT sync adapters" +
+                        " to run immediately)\n" +
+                "       -F|--top (cause even RARE sync adapters to run immediately)\n" +
                 "    ContentResolver extra options:\n" +
                 "      --is|--ignore-settings: Add SYNC_EXTRAS_IGNORE_SETTINGS\n" +
                 "      --ib|--ignore-backoff: Add SYNC_EXTRAS_IGNORE_BACKOFF\n" +
diff --git a/cmds/statsd/benchmark/metric_util.cpp b/cmds/statsd/benchmark/metric_util.cpp
index b67764b..50b05cd 100644
--- a/cmds/statsd/benchmark/metric_util.cpp
+++ b/cmds/statsd/benchmark/metric_util.cpp
@@ -127,25 +127,25 @@
 }
 
 AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
-    const string& name, ActivityForegroundStateChanged::Activity activity) {
+    const string& name, ActivityForegroundStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(4);  // Activity field.
-    field_value_matcher->set_eq_int(activity);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateMoveToBackgroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+        "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
 }
 
 AtomMatcher CreateMoveToForegroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+        "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND);
 }
 
 Predicate CreateScheduledJobPredicate() {
@@ -315,25 +315,25 @@
 }
 
 std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(
         android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
     event->write(uid);
     event->write("pkg_name");
     event->write("class_name");
-    event->write(activity);
+    event->write(state);
     event->init();
     return event;
 }
 
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index a35570b..c1ff275 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -79,8 +79,6 @@
       mSendBroadcast(sendBroadcast),
       mTimeBaseSec(timeBaseSec),
       mLastLogTimestamp(0) {
-    StatsPullerManager statsPullerManager;
-    statsPullerManager.SetTimeBaseSec(mTimeBaseSec);
 }
 
 StatsLogProcessor::~StatsLogProcessor() {
@@ -177,7 +175,7 @@
 
     uint64_t curTimeSec = getElapsedRealtimeSec();
     if (curTimeSec - mLastPullerCacheClearTimeSec > StatsdStats::kPullerCacheClearIntervalSec) {
-        mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec);
+        mStatsPullerManager.ClearPullerCacheIfNecessary(curTimeSec * NS_PER_SEC);
         mLastPullerCacheClearTimeSec = curTimeSec;
     }
 
@@ -199,11 +197,6 @@
     sp<MetricsManager> newMetricsManager =
         new MetricsManager(key, config, mTimeBaseSec, mUidMap,
                            mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
-    auto it = mMetricsManagers.find(key);
-    if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
-        ALOGE("Can't accept more configs!");
-        return;
-    }
 
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
@@ -213,7 +206,6 @@
             mUidMap->addListener(newMetricsManager.get());
         }
         mMetricsManagers[key] = newMetricsManager;
-        // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
         VLOG("StatsdConfig valid");
     } else {
         // If there is any error in the config, don't use it.
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index b03b4b4..9f70c75 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -595,7 +595,7 @@
 status_t StatsService::cmd_print_pulled_metrics(FILE* out, const Vector<String8>& args) {
     int s = atoi(args[1].c_str());
     vector<shared_ptr<LogEvent> > stats;
-    if (mStatsPullerManager.Pull(s, &stats)) {
+    if (mStatsPullerManager.Pull(s, getElapsedRealtimeNs(), &stats)) {
         for (const auto& it : stats) {
             fprintf(out, "Pull from %d: %s\n", s, it->ToString().c_str());
         }
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index e796d19..3f69a2c 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -55,8 +55,9 @@
                 }
                 break;
             case Subscription::SubscriberInformationCase::kPerfettoDetails:
-                if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details())) {
-                    ALOGW("Failed to generate prefetto traces.");
+                if (!CollectPerfettoTraceAndUploadToDropbox(subscription.perfetto_details(),
+                                                            rule_id, configKey)) {
+                    ALOGW("Failed to generate perfetto traces.");
                 }
                 break;
             case Subscription::SubscriberInformationCase::kBroadcastSubscriberDetails:
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 99611f4..7fe8e62 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -16,7 +16,6 @@
 
 syntax = "proto2";
 
-// TODO: Not the right package and class name
 package android.os.statsd;
 option java_package = "com.android.os";
 option java_outer_classname = "AtomsProto";
@@ -49,7 +48,7 @@
     oneof pushed {
         // For StatsLog reasons, 1 is illegal and will not work. Must start at 2.
         BleScanStateChanged ble_scan_state_changed = 2;
-        BleUnoptimizedScanStateChanged ble_unoptimized_scan_state_changed = 3;
+        // 3 is available for use
         BleScanResultReceived ble_scan_result_received = 4;
         SensorStateChanged sensor_state_changed = 5;
         GpsScanStateChanged gps_scan_state_changed = 6;
@@ -60,12 +59,12 @@
         LongPartialWakelockStateChanged long_partial_wakelock_state_changed = 11;
         MobileRadioPowerStateChanged mobile_radio_power_state_changed = 12;
         WifiRadioPowerStateChanged wifi_radio_power_state_changed = 13;
-        // TODO: 14-19 are blank, but need not be
+        // 14 - 19 are available
         BatterySaverModeStateChanged battery_saver_mode_state_changed = 20;
         DeviceIdleModeStateChanged device_idle_mode_state_changed = 21;
         DeviceIdlingModeStateChanged device_idling_mode_state_changed = 22;
         AudioStateChanged audio_state_changed = 23;
-        MediaCodecActivityChanged media_codec_activity_changed = 24;
+        MediaCodecStateChanged media_codec_state_changed = 24;
         CameraStateChanged camera_state_changed = 25;
         FlashlightStateChanged flashlight_state_changed = 26;
         UidProcessStateChanged uid_process_state_changed = 27;
@@ -74,8 +73,7 @@
         BatteryLevelChanged battery_level_changed = 30;
         ChargingStateChanged charging_state_changed = 31;
         PluggedStateChanged plugged_state_changed = 32;
-        // TODO: 33 is blank, but is available for use.
-        DeviceOnStatusChanged device_on_status_changed = 34;
+        // 33 - 34 are available
         WakeupAlarmOccurred wakeup_alarm_occurred = 35;
         KernelWakeupReported kernel_wakeup_reported = 36;
         WifiLockStateChanged wifi_lock_state_changed = 37;
@@ -86,12 +84,12 @@
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
         IsolatedUidChanged isolated_uid_changed = 43;
         PacketWakeupOccurred packet_wakeup_occurred = 44;
-        DropboxErrorChanged dropbox_error_changed = 45;
+        // 45 is available
         AnomalyDetected anomaly_detected = 46;
         AppBreadcrumbReported app_breadcrumb_reported = 47;
-        AppStartChanged app_start_changed = 48;
-        AppStartCancelChanged app_start_cancel_changed = 49;
-        AppStartFullyDrawnChanged app_start_fully_drawn_changed = 50;
+        AppStartOccurred app_start_occurred = 48;
+        AppStartCanceled app_start_canceled = 49;
+        AppStartFullyDrawn app_start_fully_drawn = 50;
         LmkKillOccurred lmk_kill_occurred = 51;
         PictureInPictureStateChanged picture_in_picture_state_changed = 52;
         WifiMulticastLockStateChanged wifi_multicast_lock_state_changed = 53;
@@ -106,18 +104,25 @@
         KeyguardStateChanged keyguard_state_changed = 62;
         KeyguardBouncerStateChanged keyguard_bouncer_state_changed = 63;
         KeyguardBouncerPasswordEntered keyguard_bouncer_password_entered = 64;
-        AppDied app_died=65;
+        AppDied app_died = 65;
         ResourceConfigurationChanged resource_configuration_changed = 66;
         BluetoothEnabledStateChanged bluetooth_enabled_state_changed = 67;
         BluetoothConnectionStateChanged bluetooth_connection_state_changed = 68;
         BluetoothA2dpAudioStateChanged bluetooth_a2dp_audio_state_changed = 69;
-        UsbConnectorStateChanged usb_connector_changed = 70;
+        UsbConnectorStateChanged usb_connector_state_changed = 70;
         SpeakerImpedanceReported speaker_impedance_reported = 71;
         HardwareFailed hardware_failed = 72;
         PhysicalDropDetected physical_drop_detected = 73;
         ChargeCyclesReported charge_cycles_reported = 74;
         MobileConnectionStateChanged mobile_connection_state_changed = 75;
         MobileRadioTechnologyChanged mobile_radio_technology_changed = 76;
+        UsbDeviceAttached usb_device_attached = 77;
+        AppCrashOccurred app_crash_occurred = 78;
+        ANROccurred anr_occurred = 79;
+        WTFOccurred wtf_occurred = 80;
+        LowMemReported low_mem_reported = 81;
+
+
     }
 
     // Pulled events will start at field 10000.
@@ -133,7 +138,7 @@
         CpuTimePerFreq cpu_time_per_freq = 10008;
         CpuTimePerUid cpu_time_per_uid = 10009;
         CpuTimePerUidFreq cpu_time_per_uid_freq = 10010;
-        WifiActivityEnergyInfo wifi_activity_energy_info = 10011;
+        WifiActivityInfo wifi_activity_info = 10011;
         ModemActivityInfo modem_activity_info = 10012;
         BluetoothActivityInfo bluetooth_activity_info = 10007;
         ProcessMemoryState process_memory_state = 10013;
@@ -223,28 +228,26 @@
  *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message ProcessLifeCycleStateChanged {
-    // TODO: should be a string tagged w/ uid annotation
     optional int32 uid = 1 [(is_uid) = true];
 
     // The process name (usually same as the app name).
-    optional string name = 2;
+    optional string process_name = 2;
 
     // What lifecycle state the process changed to.
     // This enum is specific to atoms.proto.
-    enum Event {
-        PROCESS_FINISHED = 0;
-        PROCESS_STARTED = 1;
-        PROCESS_CRASHED = 2;
-        PROCESS_ANRED = 3;
+    enum State {
+        FINISHED = 0;
+        STARTED = 1;
+        CRASHED = 2;
     }
-    optional Event event = 3;
+    optional State state = 3;
 }
 
 /**
  * Logs when the ble scan state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   packages/apps/Bluetooth/src/com/android/bluetooth/gatt/AppScanStats.java
  */
 message BleScanStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -252,25 +255,17 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all ble stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
-}
 
-/**
- * Logs when an unoptimized ble scan state changes.
- *
- * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
- */
-// TODO: Consider changing to tracking per-scanner-id (log from AppScanStats).
-message BleUnoptimizedScanStateChanged {
-    repeated AttributionNode attribution_node = 1;
-
-    enum State {
-        OFF = 0;
-        ON = 1;
-    }
-    optional State state = 2;
+    // Does the scan have a filter.
+    optional bool is_filtered = 3;
+    // Whether the scan is a CALLBACK_TYPE_FIRST_MATCH scan. Called 'background' scan internally.
+    optional bool is_first_match = 4;
+    // Whether the scan set to piggy-back off the results of other scans (SCAN_MODE_OPPORTUNISTIC).
+    optional bool is_opportunistic = 5;
 }
 
 /**
@@ -284,19 +279,18 @@
     repeated AttributionNode attribution_node = 1;
 
     // Number of ble scan results returned.
-    optional int32 num_of_results = 2;
+    optional int32 num_results = 2;
 }
 
 /**
  * Logs when a sensor state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message SensorStateChanged {
     repeated AttributionNode attribution_node = 1;
 
-    // TODO: Is there a way to get the actual name of the sensor?
     // The id (int) of the sensor.
     optional int32 sensor_id = 2;
 
@@ -329,13 +323,13 @@
  * Logs when a sync manager sync state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message SyncStateChanged {
     repeated AttributionNode attribution_node = 1;
 
     // Name of the sync (as named in the app). Can be chosen at run-time.
-    optional string name = 2;
+    optional string sync_name = 2;
 
     enum State {
         OFF = 0;
@@ -348,13 +342,13 @@
  * Logs when a job scheduler job state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message ScheduledJobStateChanged {
     repeated AttributionNode attribution_node = 1;
 
     // Name of the job (as named in the app)
-    optional string name = 2;
+    optional string job_name = 2;
 
     enum State {
         FINISHED = 0;
@@ -365,7 +359,7 @@
 
     // The reason a job has stopped.
     // This is only applicable when the state is FINISHED.
-    // The default value is CANCELED.
+    // The default value is STOP_REASON_UNKNOWN.
     optional android.app.job.StopReasonEnum stop_reason = 4;
 }
 
@@ -373,7 +367,7 @@
  * Logs when the audio state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message AudioStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -381,6 +375,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all audio stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -389,14 +385,16 @@
  * Logs when the video codec state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
-message MediaCodecActivityChanged {
+message MediaCodecStateChanged {
     repeated AttributionNode attribution_node = 1;
 
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all mediaCodec stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -405,7 +403,7 @@
  * Logs when the flashlight state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message FlashlightStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -413,6 +411,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all flashlight stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -421,7 +421,7 @@
  * Logs when the camera state changes.
  *
  * Logged from:
- *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ *   frameworks/base/services/core/java/com/android/server/am/BatteryStatsService.java
  */
 message CameraStateChanged {
     repeated AttributionNode attribution_node = 1;
@@ -429,6 +429,8 @@
     enum State {
         OFF = 0;
         ON = 1;
+        // RESET indicates all camera stopped. Used when it (re)starts (e.g. after it crashes).
+        RESET = 2;
     }
     optional State state = 2;
 }
@@ -559,22 +561,6 @@
     optional android.os.BatteryPluggedStateEnum state = 1;
 }
 
-// TODO: Define this more precisely.
-// TODO: Log the ON state somewhere. It isn't currently logged anywhere.
-/**
- * Logs when the device turns off or on.
- *
- * Logged from:
- *   frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
- */
-message DeviceOnStatusChanged {
-    enum State {
-        OFF = 0;
-        ON = 1;
-    }
-    optional State state = 1;
-}
-
 /**
  * Logs when an app's wakeup alarm fires.
  *
@@ -596,8 +582,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message MobileRadioPowerStateChanged {
-    // TODO: Add attribution instead of uid?
-    optional int32 uid = 1 [(is_uid) = true];
+    repeated AttributionNode attribution_node = 1;
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -611,8 +596,7 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message WifiRadioPowerStateChanged {
-    // TODO: Add attribution instead of uid?
-    optional int32 uid = 1 [(is_uid) = true];
+    repeated AttributionNode attribution_node = 1;
 
     // Power state, from frameworks/base/core/proto/android/telephony/enums.proto.
     optional android.telephony.DataConnectionPowerStateEnum state = 2;
@@ -966,6 +950,21 @@
     optional int32 sim_slot_index = 2;
 }
 
+/**
+ * Logs the VID and PID of any connected USB devices.
+ *
+ * Notes if any Audio, HID (input buttons/mouse/keyboard), or Storage interfaces are present.
+ *
+ * Logged by Vendor.
+ */
+message UsbDeviceAttached {
+    optional int32 vid = 1;
+    optional int32 pid = 2;
+    optional bool has_audio = 3;
+    optional bool has_hid = 4;
+    optional bool has_storage = 5;
+}
+
 
 /**
  * Logs when Bluetooth is enabled and disabled.
@@ -1103,6 +1102,8 @@
     optional int32 confidence_pctg = 1;
     // Peak acceleration of the drop, in 1/1000s of a g.
     optional int32 accel_peak_thousandths_g = 2;
+    // Duration of freefall in ms
+    optional int32 freefall_time_millis = 3;
 }
 
 /**
@@ -1135,7 +1136,6 @@
 message DaveyOccurred {
     // The UID that logged this atom.
     optional int32 uid = 1 [(is_uid) = true];
-    ;
 
     // Amount of time it took to render the frame. Should be >=700ms.
     optional int64 jank_duration_millis = 2;
@@ -1202,42 +1202,70 @@
     optional string pkg_name = 2;
     optional string class_name = 3;
 
-    enum Activity {
-        MOVE_TO_BACKGROUND = 0;
-        MOVE_TO_FOREGROUND = 1;
+    enum State {
+        BACKGROUND = 0;
+        FOREGROUND = 1;
     }
-    optional Activity activity = 4;
+    optional State state = 4;
 }
 
 /**
- * Logs when an error is written to dropbox.
+ * Logs when an app crashes.
  * Logged from:
  *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
  */
-message DropboxErrorChanged {
-    // The uid if available. -1 means not available.
+message AppCrashOccurred {
     optional int32 uid = 1 [(is_uid) = true];
 
-    // Tag used when recording this error to dropbox. Contains data_ or system_ prefix.
-    optional string tag = 2;
+    optional string event_type = 2;
 
     // The name of the process.
+    // system_server if it is not by an app
     optional string process_name = 3;
 
     // The pid if available. -1 means not available.
     optional sint32 pid = 4;
+}
 
-    // 1 indicates is instant app. -1 indicates Not applicable.
-    optional sint32 is_instant_app = 5;
+/**
+ * Logs when a WTF (What a Terrible Failure) happened.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message WTFOccurred {
+    optional int32 uid = 1 [(is_uid) = true];
 
-    // The activity name if available.
-    optional string activity_name = 6;
+    optional string tag = 2;
 
-    // The package name if available.
-    optional string package_name = 7;
+    // The name of the process.
+    // system_server if it is not by an app
+    optional string process_name = 3;
 
-    // 1 indicates in foreground. -1 indicates not available.
-    optional sint32 is_foreground = 8;
+    // The pid if available. -1 means not available.
+    optional sint32 pid = 4;
+}
+
+/**
+ * Logs when system server reports low memory.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+ */
+message LowMemReported {
+}
+
+/**
+ * Logs when an app ANR (App Not Responding) occurs.
+ * Logged from:
+ *      frameworks/base/services/core/java/com/android/server/am/AppErrors.java
+ */
+message ANROccurred {
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional string process_name = 2;
+
+    optional string short_component_name = 3;
+
+    optional string reason = 4;
 }
 
 /*
@@ -1280,7 +1308,7 @@
     optional int64 alert_id = 3;
 }
 
-message AppStartChanged {
+message AppStartOccurred {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1288,7 +1316,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WARM = 1;
         HOT = 2;
         COLD = 3;
@@ -1321,13 +1349,13 @@
     optional string launch_token = 13;
 
     // The compiler filter used when when the package was optimized.
-    optional string package_optimization_compilation_filter = 14;
+    optional int32 package_optimization_compilation_filter = 14;
 
     // The reason why the package was optimized.
-    optional string package_optimization_compilation_reason = 15;
+    optional int32 package_optimization_compilation_reason = 15;
 }
 
-message AppStartCancelChanged {
+message AppStartCanceled {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1335,7 +1363,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WARM = 1;
         HOT = 2;
         COLD = 3;
@@ -1347,7 +1375,7 @@
     optional string activity_name = 4;
 }
 
-message AppStartFullyDrawnChanged {
+message AppStartFullyDrawn {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
@@ -1355,7 +1383,7 @@
     optional string pkg_name = 2;
 
     enum TransitionType {
-        APP_START_TRANSITION_TYPE_UNKNOWN = 0;
+        UNKNOWN = 0;
         WITH_BUNDLE = 1;
         WITHOUT_BUNDLE = 2;
     }
@@ -1440,8 +1468,8 @@
  *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
  */
 message IsolatedUidChanged {
-    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+    // NOTE: DO NOT annotate uid field in this atom. This atom is specially handled in statsd.
     optional int32 parent_uid = 1;
 
     optional int32 isolated_uid = 2;
@@ -1602,9 +1630,8 @@
 message WifiBytesTransferByFgBg {
     optional int32 uid = 1 [(is_uid) = true];
 
-    // 1 denotes foreground and 0 denotes background. This is called Set in
-    // NetworkStats.
-    optional int32 is_foreground = 2;
+    // 1 denotes foreground and 0 denotes background. This is called Set in NetworkStats.
+    optional bool is_foreground = 2;
 
     optional int64 rx_bytes = 3;
 
@@ -1644,7 +1671,7 @@
 
     // 1 denotes foreground and 0 denotes background. This is called Set in
     // NetworkStats.
-    optional int32 is_foreground = 2;
+    optional bool is_foreground = 2;
 
     optional int64 rx_bytes = 3;
 
@@ -1741,7 +1768,7 @@
 /**
  * Pulls Wifi Controller Activity Energy Info
  */
-message WifiActivityEnergyInfo {
+message WifiActivityInfo {
     // timestamp(wall clock) of record creation
     optional uint64 timestamp_millis = 1;
     // stack reported state
@@ -1917,7 +1944,8 @@
 }
 
 /**
- * Pulls the temperature of various parts of the device, in Celsius.
+ * Pulls the temperature of various parts of the device.
+ * The units are tenths of a degree Celsius. Eg: 30.3C is reported as 303.
  *
  * Pulled from:
  *   frameworks/base/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
@@ -1929,6 +1957,6 @@
     // The name of the temperature source. Eg. CPU0
     optional string sensor_name = 2;
 
-    // Temperature in degrees C.
-    optional float temperature_C = 3;
+    // Temperature in tenths of a degree C.
+    optional int32 temperature_dC = 3;
 }
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index 3661d2b..60a4b23 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -149,9 +149,9 @@
         }
     }
 
+    ConditionState newCondition =
+            evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
     if (!mSliced) {
-        ConditionState newCondition =
-                evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
 
         bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
         mNonSlicedConditionState = newCondition;
@@ -172,7 +172,7 @@
                 break;
             }
         }
-        nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
+        nonSlicedConditionCache[mIndex] = newCondition;
         VLOG("CombinationPredicate %lld sliced may changed? %d", (long long)mConditionId,
             conditionChangedCache[mIndex] == true);
     }
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 73efb39..87104a3 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -289,9 +289,15 @@
         // The event doesn't match this condition. So we just report existing condition values.
         conditionChangedCache[mIndex] = false;
         if (mSliced) {
-            // if the condition result is sliced. metrics won't directly get value from the
-            // cache, so just set any value other than kNotEvaluated.
+            // if the condition result is sliced. The overall condition is true if any of the sliced
+            // condition is true
             conditionCache[mIndex] = mInitialValue;
+            for (const auto& slicedCondition : mSlicedConditionState) {
+                if (slicedCondition.second > 0) {
+                    conditionCache[mIndex] = ConditionState::kTrue;
+                    break;
+                }
+            }
         } else {
             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
             if (itr == mSlicedConditionState.end()) {
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
index fe1740b..1965ce6 100644
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ b/cmds/statsd/src/condition/StateTracker.cpp
@@ -141,7 +141,7 @@
     // one keys matched.
     HashableDimensionKey primaryKey;
     HashableDimensionKey state;
-    if (!filterValues(mPrimaryKeys, event.getValues(), &primaryKey) ||
+    if ((mPrimaryKeys.size() > 0 && !filterValues(mPrimaryKeys, event.getValues(), &primaryKey)) ||
         !filterValues(mOutputDimensions, event.getValues(), &state)) {
         ALOGE("Failed to filter fields in the event?? panic now!");
         conditionCache[mIndex] =
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index f5310a4..ff25091 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
 #include "config/ConfigManager.h"
 #include "storage/StorageManager.h"
 
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <android-base/file.h>
@@ -68,24 +72,37 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-        auto it = mConfigs.find(key);
-
         const int numBytes = config.ByteSize();
         vector<uint8_t> buffer(numBytes);
         config.SerializeToArray(&buffer[0], numBytes);
 
-        const bool isDuplicate =
-            it != mConfigs.end() &&
-            StorageManager::hasIdenticalConfig(key, buffer);
+        auto uidIt = mConfigs.find(key.GetUid());
+        // GuardRail: Limit the number of configs per uid.
+        if (uidIt != mConfigs.end()) {
+            auto it = uidIt->second.find(key);
+            if (it == uidIt->second.end() &&
+                uidIt->second.size() >= StatsdStats::kMaxConfigCountPerUid) {
+                ALOGE("ConfigManager: uid %d has exceeded the config count limit", key.GetUid());
+                return;
+            }
+        }
 
-        // Update saved file on disk. We still update timestamp of file when
-        // there exists a duplicate configuration to avoid garbage collection.
+        // Check if it's a duplicate config.
+        if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end() &&
+            StorageManager::hasIdenticalConfig(key, buffer)) {
+            // This is a duplicate config.
+            ALOGI("ConfigManager This is a duplicate config %s", key.ToString().c_str());
+            // Update saved file on disk. We still update timestamp of file when
+            // there exists a duplicate configuration to avoid garbage collection.
+            update_saved_configs_locked(key, buffer, numBytes);
+            return;
+        }
+
+        // Update saved file on disk.
         update_saved_configs_locked(key, buffer, numBytes);
 
-        if (isDuplicate) return;
-
-        // Add to set
-        mConfigs.insert(key);
+        // Add to set.
+        mConfigs[key.GetUid()].insert(key);
 
         for (sp<ConfigListener> listener : mListeners) {
             broadcastList.push_back(listener);
@@ -113,11 +130,10 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-        auto it = mConfigs.find(key);
-        if (it != mConfigs.end()) {
+        auto uidIt = mConfigs.find(key.GetUid());
+        if (uidIt != mConfigs.end() && uidIt->second.find(key) != uidIt->second.end()) {
             // Remove from map
-            mConfigs.erase(it);
-
+            uidIt->second.erase(key);
             for (sp<ConfigListener> listener : mListeners) {
                 broadcastList.push_back(listener);
             }
@@ -150,18 +166,20 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-        for (auto it = mConfigs.begin(); it != mConfigs.end();) {
+        auto uidIt = mConfigs.find(uid);
+        if (uidIt == mConfigs.end()) {
+            return;
+        }
+
+        for (auto it = uidIt->second.begin(); it != uidIt->second.end(); ++it) {
             // Remove from map
-            if (it->GetUid() == uid) {
                 remove_saved_configs(*it);
                 removed.push_back(*it);
                 mConfigReceivers.erase(*it);
-                it = mConfigs.erase(it);
-            } else {
-                it++;
-            }
         }
 
+        mConfigs.erase(uidIt);
+
         for (sp<ConfigListener> listener : mListeners) {
             broadcastList.push_back(listener);
         }
@@ -182,17 +200,16 @@
     {
         lock_guard <mutex> lock(mMutex);
 
-
-        for (auto it = mConfigs.begin(); it != mConfigs.end();) {
-            // Remove from map
-            removed.push_back(*it);
-            auto receiverIt = mConfigReceivers.find(*it);
-            if (receiverIt != mConfigReceivers.end()) {
-                mConfigReceivers.erase(*it);
+        for (auto uidIt = mConfigs.begin(); uidIt != mConfigs.end();) {
+            for (auto it = uidIt->second.begin(); it != uidIt->second.end();) {
+                // Remove from map
+                removed.push_back(*it);
+                it = uidIt->second.erase(it);
             }
-            it = mConfigs.erase(it);
+            uidIt = mConfigs.erase(uidIt);
         }
 
+        mConfigReceivers.clear();
         for (sp<ConfigListener> listener : mListeners) {
             broadcastList.push_back(listener);
         }
@@ -211,8 +228,10 @@
     lock_guard<mutex> lock(mMutex);
 
     vector<ConfigKey> ret;
-    for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
-        ret.push_back(*it);
+    for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
+        for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
+            ret.push_back(*it);
+        }
     }
     return ret;
 }
@@ -231,13 +250,15 @@
 void ConfigManager::Dump(FILE* out) {
     lock_guard<mutex> lock(mMutex);
 
-    fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
+    fprintf(out, "CONFIGURATIONS\n");
     fprintf(out, "     uid name\n");
-    for (const auto& key : mConfigs) {
-        fprintf(out, "  %6d %lld\n", key.GetUid(), (long long)key.GetId());
-        auto receiverIt = mConfigReceivers.find(key);
-        if (receiverIt != mConfigReceivers.end()) {
-            fprintf(out, "    -> received by PendingIntent as binder\n");
+    for (auto uidIt = mConfigs.cbegin(); uidIt != mConfigs.cend(); ++uidIt) {
+        for (auto it = uidIt->second.cbegin(); it != uidIt->second.cend(); ++it) {
+            fprintf(out, "  %6d %lld\n", it->GetUid(), (long long)it->GetId());
+            auto receiverIt = mConfigReceivers.find(*it);
+            if (receiverIt != mConfigReceivers.end()) {
+                fprintf(out, "    -> received by PendingIntent as binder\n");
+            }
         }
     }
 }
@@ -255,331 +276,6 @@
     StorageManager::writeFile(file_name.c_str(), &buffer[0], numBytes);
 }
 
-StatsdConfig build_fake_config() {
-    // HACK: Hard code a test metric for counting screen on events...
-    StatsdConfig config;
-    config.set_id(12345);
-
-    int WAKE_LOCK_TAG_ID = 1111;  // put a fake id here to make testing easier.
-    int WAKE_LOCK_UID_KEY_ID = 1;
-    int WAKE_LOCK_NAME_KEY = 3;
-    int WAKE_LOCK_STATE_KEY = 4;
-    int WAKE_LOCK_ACQUIRE_VALUE = 1;
-    int WAKE_LOCK_RELEASE_VALUE = 0;
-
-    int APP_USAGE_TAG_ID = 12345;
-    int APP_USAGE_UID_KEY_ID = 1;
-    int APP_USAGE_STATE_KEY = 2;
-    int APP_USAGE_FOREGROUND = 1;
-    int APP_USAGE_BACKGROUND = 0;
-
-    int SCREEN_EVENT_TAG_ID = 29;
-    int SCREEN_EVENT_STATE_KEY = 1;
-    int SCREEN_EVENT_ON_VALUE = 2;
-    int SCREEN_EVENT_OFF_VALUE = 1;
-
-    int UID_PROCESS_STATE_TAG_ID = 27;
-    int UID_PROCESS_STATE_UID_KEY = 1;
-
-    int KERNEL_WAKELOCK_TAG_ID = 1004;
-    int KERNEL_WAKELOCK_COUNT_KEY = 2;
-    int KERNEL_WAKELOCK_NAME_KEY = 1;
-
-    int DEVICE_TEMPERATURE_TAG_ID = 33;
-    int DEVICE_TEMPERATURE_KEY = 1;
-
-    // Count Screen ON events.
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(1);  // METRIC_1
-    metric->set_what(102);  //  "SCREEN_TURNED_ON"
-    metric->set_bucket(ONE_MINUTE);
-
-    // Anomaly threshold for screen-on count.
-    // TODO(b/70627390): Uncomment once the bug is fixed.
-    /*Alert* alert = config.add_alert();
-    alert->set_id("ALERT_1");
-    alert->set_metric_name("METRIC_1");
-    alert->set_number_of_buckets(6);
-    alert->set_trigger_if_sum_gt(10);
-    alert->set_refractory_period_secs(30);
-    Alert::IncidentdDetails* details = alert->mutable_incidentd_details();
-    details->add_section(12);
-    details->add_section(13);*/
-
-    config.add_allowed_log_source("AID_ROOT");
-    config.add_allowed_log_source("AID_SYSTEM");
-    config.add_allowed_log_source("AID_BLUETOOTH");
-    config.add_allowed_log_source("com.android.statsd.dogfood");
-    config.add_allowed_log_source("com.android.systemui");
-
-    // Count process state changes, slice by uid.
-    metric = config.add_count_metric();
-    metric->set_id(2);  // "METRIC_2"
-    metric->set_what(104);
-    metric->set_bucket(ONE_MINUTE);
-    FieldMatcher* dimensions = metric->mutable_dimensions_in_what();
-    dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
-    dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
-
-    // Anomaly threshold for background count.
-    // TODO(b/70627390): Uncomment once the bug is fixed.
-    /*
-    alert = config.add_alert();
-    alert->set_id("ALERT_2");
-    alert->set_metric_name("METRIC_2");
-    alert->set_number_of_buckets(4);
-    alert->set_trigger_if_sum_gt(30);
-    alert->set_refractory_period_secs(20);
-    details = alert->mutable_incidentd_details();
-    details->add_section(14);
-    details->add_section(15);*/
-
-    // Count process state changes, slice by uid, while SCREEN_IS_OFF
-    metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(104);
-    metric->set_bucket(ONE_MINUTE);
-
-    dimensions = metric->mutable_dimensions_in_what();
-    dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
-    dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
-    metric->set_condition(202);
-
-    // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
-    metric = config.add_count_metric();
-    metric->set_id(4);
-    metric->set_what(107);
-    metric->set_bucket(ONE_MINUTE);
-    dimensions = metric->mutable_dimensions_in_what();
-    dimensions->set_field(WAKE_LOCK_TAG_ID);
-    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-
-
-    metric->set_condition(204);
-    MetricConditionLink* link = metric->add_links();
-    link->set_condition(203);
-    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
-
-    // Duration of an app holding any wl, while screen on and app in background, slice by uid
-    DurationMetric* durationMetric = config.add_duration_metric();
-    durationMetric->set_id(5);
-    durationMetric->set_bucket(ONE_MINUTE);
-    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-    dimensions = durationMetric->mutable_dimensions_in_what();
-    dimensions->set_field(WAKE_LOCK_TAG_ID);
-    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what(205);
-    durationMetric->set_condition(204);
-    link = durationMetric->add_links();
-    link->set_condition(203);
-    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
-
-    // max Duration of an app holding any wl, while screen on and app in background, slice by uid
-    durationMetric = config.add_duration_metric();
-    durationMetric->set_id(6);
-    durationMetric->set_bucket(ONE_MINUTE);
-    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-    dimensions = durationMetric->mutable_dimensions_in_what();
-    dimensions->set_field(WAKE_LOCK_TAG_ID);
-    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what(205);
-    durationMetric->set_condition(204);
-    link = durationMetric->add_links();
-    link->set_condition(203);
-    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
-
-    // Duration of an app holding any wl, while screen on and app in background
-    durationMetric = config.add_duration_metric();
-    durationMetric->set_id(7);
-    durationMetric->set_bucket(ONE_MINUTE);
-    durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-    durationMetric->set_what(205);
-    durationMetric->set_condition(204);
-    link = durationMetric->add_links();
-    link->set_condition(203);
-    link->mutable_fields_in_what()->set_field(WAKE_LOCK_TAG_ID);
-    link->mutable_fields_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    link->mutable_fields_in_condition()->set_field(APP_USAGE_TAG_ID);
-    link->mutable_fields_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
-
-
-    // Duration of screen on time.
-    durationMetric = config.add_duration_metric();
-    durationMetric->set_id(8);
-    durationMetric->set_bucket(ONE_MINUTE);
-    durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-    durationMetric->set_what(201);
-
-    // Anomaly threshold for background count.
-    // TODO(b/70627390): Uncomment once the bug is fixed.
-    /*
-    alert = config.add_alert();
-    alert->set_id(308);
-    alert->set_metric_id(8);
-    alert->set_number_of_buckets(4);
-    alert->set_trigger_if_sum_gt(2000000000); // 2 seconds
-    alert->set_refractory_period_secs(120);
-    details = alert->mutable_incidentd_details();
-    details->add_section(-1);*/
-
-    // Value metric to count KERNEL_WAKELOCK when screen turned on
-    ValueMetric* valueMetric = config.add_value_metric();
-    valueMetric->set_id(11);
-    valueMetric->set_what(109);
-    valueMetric->mutable_value_field()->set_field(KERNEL_WAKELOCK_TAG_ID);
-    valueMetric->mutable_value_field()->add_child()->set_field(KERNEL_WAKELOCK_COUNT_KEY);
-    valueMetric->set_condition(201);
-    dimensions = valueMetric->mutable_dimensions_in_what();
-    dimensions->set_field(KERNEL_WAKELOCK_TAG_ID);
-    dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY);
-    // This is for testing easier. We should never set bucket size this small.
-    durationMetric->set_bucket(ONE_MINUTE);
-
-    // Add an EventMetric to log process state change events.
-    EventMetric* eventMetric = config.add_event_metric();
-    eventMetric->set_id(9);
-    eventMetric->set_what(102); // "SCREEN_TURNED_ON"
-
-    // Add an GaugeMetric.
-    GaugeMetric* gaugeMetric = config.add_gauge_metric();
-    gaugeMetric->set_id(10);
-    gaugeMetric->set_what(101);
-    auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
-    gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID);
-    gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY);
-    durationMetric->set_bucket(ONE_MINUTE);
-
-    // Event matchers.
-    AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher();
-    temperatureAtomMatcher->set_id(101);  // "DEVICE_TEMPERATURE"
-    temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id(
-        DEVICE_TEMPERATURE_TAG_ID);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(102);  // "SCREEN_TURNED_ON"
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
-    FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
-    fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(103);  // "SCREEN_TURNED_OFF"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
-    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
-    fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(104);  // "PROCESS_STATE_CHANGE"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(105);  // "APP_GOES_BACKGROUND"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
-    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
-    fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(106);  // "APP_GOES_FOREGROUND"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
-    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
-    fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(107);  // "APP_GET_WL"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
-    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
-    fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(108);  //"APP_RELEASE_WL"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
-    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
-    fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
-    fieldValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
-
-    // pulled events
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(109);  // "KERNEL_WAKELOCK"
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID);
-
-    // Predicates.............
-    Predicate* predicate = config.add_predicate();
-    predicate->set_id(201);  // "SCREEN_IS_ON"
-    SimplePredicate* simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start(102);  // "SCREEN_TURNED_ON"
-    simplePredicate->set_stop(103);
-    simplePredicate->set_count_nesting(false);
-
-    predicate = config.add_predicate();
-    predicate->set_id(202);  // "SCREEN_IS_OFF"
-    simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start(103);
-    simplePredicate->set_stop(102);  // "SCREEN_TURNED_ON"
-    simplePredicate->set_count_nesting(false);
-
-    predicate = config.add_predicate();
-    predicate->set_id(203);  // "APP_IS_BACKGROUND"
-    simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start(105);
-    simplePredicate->set_stop(106);
-    FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions();
-    predicate_dimension1->set_field(APP_USAGE_TAG_ID);
-    predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID);
-    simplePredicate->set_count_nesting(false);
-
-    predicate = config.add_predicate();
-    predicate->set_id(204);  // "APP_IS_BACKGROUND_AND_SCREEN_ON"
-    Predicate_Combination* combination_predicate = predicate->mutable_combination();
-    combination_predicate->set_operation(LogicalOperation::AND);
-    combination_predicate->add_predicate(203);
-    combination_predicate->add_predicate(201);
-
-    predicate = config.add_predicate();
-    predicate->set_id(205);  // "WL_HELD_PER_APP_PER_NAME"
-    simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start(107);
-    simplePredicate->set_stop(108);
-    FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions();
-    predicate_dimension1->set_field(WAKE_LOCK_TAG_ID);
-    predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    predicate_dimension->add_child()->set_field(WAKE_LOCK_NAME_KEY);
-    simplePredicate->set_count_nesting(true);
-
-    predicate = config.add_predicate();
-    predicate->set_id(206);  // "WL_HELD_PER_APP"
-    simplePredicate = predicate->mutable_simple_predicate();
-    simplePredicate->set_start(107);
-    simplePredicate->set_stop(108);
-    simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-    predicate_dimension = simplePredicate->mutable_dimensions();
-    predicate_dimension->set_field(WAKE_LOCK_TAG_ID);
-    predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
-    simplePredicate->set_count_nesting(true);
-
-    return config;
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 9a38188a..611c342 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -36,9 +36,6 @@
 
 /**
  * Keeps track of which configurations have been set from various sources.
- *
- * TODO: Store the configs persistently too.
- * TODO: Dump method for debugging.
  */
 class ConfigManager : public virtual android::RefBase {
 public:
@@ -125,9 +122,9 @@
     void remove_saved_configs(const ConfigKey& key);
 
     /**
-     * Config keys that have been set.
+     * Maps from uid to the config keys that have been set.
      */
-    std::set<ConfigKey> mConfigs;
+    std::map<int, std::set<ConfigKey>> mConfigs;
 
     /**
      * Each config key can be subscribed by up to one receiver, specified as IBinder from
diff --git a/cmds/statsd/src/external/Perfetto.cpp b/cmds/statsd/src/external/Perfetto.cpp
index b09d373..0554483 100644
--- a/cmds/statsd/src/external/Perfetto.cpp
+++ b/cmds/statsd/src/external/Perfetto.cpp
@@ -15,6 +15,7 @@
  */
 
 #define DEBUG false  // STOPSHIP if true
+#include "config/ConfigKey.h"
 #include "Log.h"
 
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alert
@@ -22,6 +23,7 @@
 #include <android-base/unique_fd.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
@@ -36,7 +38,9 @@
 namespace os {
 namespace statsd {
 
-bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config) {
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t alert_id,
+                                            const ConfigKey& configKey) {
     VLOG("Starting trace collection through perfetto");
 
     if (!config.has_trace_config()) {
@@ -44,6 +48,13 @@
         return false;
     }
 
+    char alertId[20];
+    char configId[20];
+    char configUid[20];
+    snprintf(alertId, sizeof(alertId), "%" PRId64, alert_id);
+    snprintf(configId, sizeof(configId), "%" PRId64, configKey.GetId());
+    snprintf(configUid, sizeof(configUid), "%d", configKey.GetUid());
+
     android::base::unique_fd readPipe;
     android::base::unique_fd writePipe;
     if (!android::base::Pipe(&readPipe, &writePipe)) {
@@ -82,7 +93,8 @@
         }
 
         execl("/system/bin/perfetto", "perfetto", "--background", "--config", "-", "--dropbox",
-              kDropboxTag, nullptr);
+              kDropboxTag, "--alert-id", alertId, "--config-id", configId, "--config-uid",
+              configUid, nullptr);
 
         // execl() doesn't return in case of success, if we get here something
         // failed.
diff --git a/cmds/statsd/src/external/Perfetto.h b/cmds/statsd/src/external/Perfetto.h
index 2a5679c..1e7f728 100644
--- a/cmds/statsd/src/external/Perfetto.h
+++ b/cmds/statsd/src/external/Perfetto.h
@@ -24,13 +24,16 @@
 namespace os {
 namespace statsd {
 
+class ConfigKey;
 class PerfettoDetails;  // Declared in statsd_config.pb.h
 
 // Starts the collection of a Perfetto trace with the given |config|.
 // The trace is uploaded to Dropbox by the perfetto cmdline util once done.
 // This method returns immediately after passing the config and does NOT wait
 // for the full duration of the trace.
-bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config);
+bool CollectPerfettoTraceAndUploadToDropbox(const PerfettoDetails& config,
+                                            int64_t alert_id,
+                                            const ConfigKey& configKey);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
index b3acdfc..33a17de 100644
--- a/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
+++ b/cmds/statsd/src/external/ResourceThermalManagerPuller.cpp
@@ -120,7 +120,9 @@
                         wallClockTimestampNs, elapsedTimestampNs);
                 ptr->write((static_cast<int>(temps[i].type)));
                 ptr->write(temps[i].name);
-                ptr->write(temps[i].currentValue);
+                // Convert the temperature to an int.
+                int32_t temp = static_cast<int>(temps[i].currentValue * 10);
+                ptr->write(temp);
                 ptr->init();
                 data->push_back(ptr);
             }
diff --git a/cmds/statsd/src/external/StatsPuller.cpp b/cmds/statsd/src/external/StatsPuller.cpp
index 3b0cd34..ec5a5d6 100644
--- a/cmds/statsd/src/external/StatsPuller.cpp
+++ b/cmds/statsd/src/external/StatsPuller.cpp
@@ -35,26 +35,31 @@
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
 StatsPuller::StatsPuller(const int tagId)
     : mTagId(tagId) {
-    mCoolDownSec = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownSec;
-    VLOG("Puller for tag %d created. Cooldown set to %ld", mTagId, mCoolDownSec);
+    mCoolDownNs = StatsPullerManagerImpl::kAllPullAtomInfo.find(tagId)->second.coolDownNs;
+    VLOG("Puller for tag %d created. Cooldown set to %lld", mTagId, (long long)mCoolDownNs);
 }
 
-bool StatsPuller::Pull(std::vector<std::shared_ptr<LogEvent>>* data) {
+bool StatsPuller::Pull(const int64_t elapsedTimeNs, std::vector<std::shared_ptr<LogEvent>>* data) {
     lock_guard<std::mutex> lock(mLock);
+    int64_t wallClockTimeNs = getWallClockNs();
     StatsdStats::getInstance().notePull(mTagId);
-    long curTime = getElapsedRealtimeSec();
-    if (curTime - mLastPullTimeSec < mCoolDownSec) {
+    if (elapsedTimeNs - mLastPullTimeNs < mCoolDownNs) {
         (*data) = mCachedData;
         StatsdStats::getInstance().notePullFromCache(mTagId);
         return true;
     }
-    if (mMinPullIntervalSec > curTime - mLastPullTimeSec) {
-        mMinPullIntervalSec = curTime - mLastPullTimeSec;
-        StatsdStats::getInstance().updateMinPullIntervalSec(mTagId, mMinPullIntervalSec);
+    if (mMinPullIntervalNs > elapsedTimeNs - mLastPullTimeNs) {
+        mMinPullIntervalNs = elapsedTimeNs - mLastPullTimeNs;
+        StatsdStats::getInstance().updateMinPullIntervalSec(mTagId,
+                                                            mMinPullIntervalNs / NS_PER_SEC);
     }
     mCachedData.clear();
-    mLastPullTimeSec = curTime;
+    mLastPullTimeNs = elapsedTimeNs;
     bool ret = PullInternal(&mCachedData);
+    for (const shared_ptr<LogEvent>& data : mCachedData) {
+        data->setElapsedTimestampNs(elapsedTimeNs);
+        data->setLogdWallClockTimestampNs(wallClockTimeNs);
+    }
     if (ret) {
       mergeIsolatedUidsToHostUid(mCachedData, mUidMap, mTagId);
       (*data) = mCachedData;
@@ -70,12 +75,12 @@
     lock_guard<std::mutex> lock(mLock);
     int ret = mCachedData.size();
     mCachedData.clear();
-    mLastPullTimeSec = 0;
+    mLastPullTimeNs = 0;
     return ret;
 }
 
-int StatsPuller::ClearCacheIfNecessary(long timestampSec) {
-    if (timestampSec - mLastPullTimeSec > mCoolDownSec) {
+int StatsPuller::ClearCacheIfNecessary(int64_t timestampNs) {
+    if (timestampNs - mLastPullTimeNs > mCoolDownNs) {
         return clearCache();
     } else {
         return 0;
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 936c47e..caac677 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,13 +37,13 @@
 
     virtual ~StatsPuller() {}
 
-    bool Pull(std::vector<std::shared_ptr<LogEvent>>* data);
+    bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear cache immediately
     int ForceClearCache();
 
     // Clear cache if elapsed time is more than cooldown time
-    int ClearCacheIfNecessary(long timestampSec);
+    int ClearCacheIfNecessary(int64_t timestampNs);
 
     static void SetUidMap(const sp<UidMap>& uidMap);
 
@@ -59,9 +59,9 @@
     // If a pull request comes before cooldown, a cached version from purevious pull
     // will be returned.
     // The actual value should be determined by individual pullers.
-    long mCoolDownSec;
+    int64_t mCoolDownNs;
     // For puller stats
-    long mMinPullIntervalSec = LONG_MAX;
+    int64_t mMinPullIntervalNs = LONG_MAX;
 
     virtual bool PullInternal(std::vector<std::shared_ptr<LogEvent>>* data) = 0;
 
@@ -69,7 +69,7 @@
     // cached data will be returned.
     std::vector<std::shared_ptr<LogEvent>> mCachedData;
 
-    long mLastPullTimeSec;
+    int64_t mLastPullTimeNs;
 
     int clearCache();
 
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 2717d5c..83d59c0 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -26,10 +26,9 @@
  public:
     virtual ~StatsPullerManager() {}
 
-    virtual void RegisterReceiver(int tagId,
-                                  wp <PullDataReceiver> receiver,
-                                  long intervalMs) {
-        mPullerManager.RegisterReceiver(tagId, receiver, intervalMs);
+    virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+                                  int64_t intervalNs) {
+        mPullerManager.RegisterReceiver(tagId, receiver, nextPullTimeNs, intervalNs);
     };
 
     virtual void UnRegisterReceiver(int tagId, wp <PullDataReceiver> receiver) {
@@ -45,13 +44,9 @@
         mPullerManager.OnAlarmFired();
     }
 
-    virtual bool
-    Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-        return mPullerManager.Pull(tagId, data);
-    }
-
-    void SetTimeBaseSec(const long timeBaseSec) {
-        mPullerManager.SetTimeBaseSec(timeBaseSec);
+    virtual bool Pull(const int tagId, const int64_t timesNs,
+                      vector<std::shared_ptr<LogEvent>>* data) {
+        return mPullerManager.Pull(tagId, timesNs, data);
     }
 
     int ForceClearPullerCache() {
@@ -62,8 +57,8 @@
         mPullerManager.SetStatsCompanionService(statsCompanionService);
     }
 
-    int ClearPullerCacheIfNecessary(long timestampSec) {
-        return mPullerManager.ClearPullerCacheIfNecessary(timestampSec);
+    int ClearPullerCacheIfNecessary(int64_t timestampNs) {
+        return mPullerManager.ClearPullerCacheIfNecessary(timestampNs);
     }
 
  private:
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
index dd6406b..2f0e885 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.cpp
@@ -19,15 +19,17 @@
 
 #include <android/os/IStatsCompanionService.h>
 #include <cutils/log.h>
+#include <math.h>
 #include <algorithm>
 #include <climits>
+#include "../StatsService.h"
 #include "../logd/LogEvent.h"
 #include "../stats_log_util.h"
 #include "../statscompanion_util.h"
 #include "ResourceHealthManagerPuller.h"
 #include "ResourceThermalManagerPuller.h"
 #include "StatsCompanionServicePuller.h"
-#include "StatsService.h"
+#include "StatsPullerManagerImpl.h"
 #include "SubsystemSleepStatePuller.h"
 #include "statslog.h"
 
@@ -47,89 +49,136 @@
 const std::map<int, PullAtomInfo> StatsPullerManagerImpl::kAllPullAtomInfo = {
         // wifi_bytes_transfer
         {android::util::WIFI_BYTES_TRANSFER,
-         {{2, 3, 4, 5}, {}, 1,
+         {{2, 3, 4, 5},
+          {},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER)}},
         // wifi_bytes_transfer_by_fg_bg
         {android::util::WIFI_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6}, {2}, 1,
+         {{3, 4, 5, 6},
+          {2},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::WIFI_BYTES_TRANSFER_BY_FG_BG)}},
         // mobile_bytes_transfer
         {android::util::MOBILE_BYTES_TRANSFER,
-         {{2, 3, 4, 5}, {}, 1,
+         {{2, 3, 4, 5},
+          {},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER)}},
         // mobile_bytes_transfer_by_fg_bg
         {android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG,
-         {{3, 4, 5, 6}, {2}, 1,
+         {{3, 4, 5, 6},
+          {2},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::MOBILE_BYTES_TRANSFER_BY_FG_BG)}},
         // bluetooth_bytes_transfer
         {android::util::BLUETOOTH_BYTES_TRANSFER,
-         {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
+         {{2, 3},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::BLUETOOTH_BYTES_TRANSFER)}},
         // kernel_wakelock
         {android::util::KERNEL_WAKELOCK,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::KERNEL_WAKELOCK)}},
         // subsystem_sleep_state
         {android::util::SUBSYSTEM_SLEEP_STATE,
-         {{}, {}, 1, new SubsystemSleepStatePuller()}},
+         {{}, {}, 1 * NS_PER_SEC, new SubsystemSleepStatePuller()}},
         // cpu_time_per_freq
         {android::util::CPU_TIME_PER_FREQ,
-         {{3}, {2}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
+         {{3},
+          {2},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_FREQ)}},
         // cpu_time_per_uid
         {android::util::CPU_TIME_PER_UID,
-         {{2, 3}, {}, 1, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
+         {{2, 3},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID)}},
         // cpu_time_per_uid_freq
-        // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_TIME_PER_UID_FREQ,
-         {{4}, {2,3}, 0, new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
+         {{4},
+          {2, 3},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_TIME_PER_UID_FREQ)}},
         // cpu_active_time
-        // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_ACTIVE_TIME,
-         {{2}, {}, 0, new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
+         {{2},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_ACTIVE_TIME)}},
         // cpu_cluster_time
-        // the throttling is 3sec, handled in frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
+        // the throttling is 3sec, handled in
+        // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
         {android::util::CPU_CLUSTER_TIME,
-         {{3}, {2}, 0, new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
+         {{3},
+          {2},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::CPU_CLUSTER_TIME)}},
         // wifi_activity_energy_info
-        {android::util::WIFI_ACTIVITY_ENERGY_INFO,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_ENERGY_INFO)}},
+        {android::util::WIFI_ACTIVITY_INFO,
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::WIFI_ACTIVITY_INFO)}},
         // modem_activity_info
         {android::util::MODEM_ACTIVITY_INFO,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::MODEM_ACTIVITY_INFO)}},
         // bluetooth_activity_info
         {android::util::BLUETOOTH_ACTIVITY_INFO,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::BLUETOOTH_ACTIVITY_INFO)}},
         // system_elapsed_realtime
         {android::util::SYSTEM_ELAPSED_REALTIME,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::SYSTEM_ELAPSED_REALTIME)}},
         // system_uptime
         {android::util::SYSTEM_UPTIME,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::SYSTEM_UPTIME)}},
         // disk_space
         {android::util::DISK_SPACE,
-         {{}, {}, 1, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::DISK_SPACE)}},
         // remaining_battery_capacity
         {android::util::REMAINING_BATTERY_CAPACITY,
-         {{}, {}, 1, new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new ResourceHealthManagerPuller(android::util::REMAINING_BATTERY_CAPACITY)}},
         // full_battery_capacity
         {android::util::FULL_BATTERY_CAPACITY,
-         {{}, {}, 1, new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
+         {{},
+          {},
+          1 * NS_PER_SEC,
+          new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}},
         // process_memory_state
         {android::util::PROCESS_MEMORY_STATE,
-         {{4,5,6,7,8},
-          {2,3},
-          0,
+         {{4, 5, 6, 7, 8},
+          {2, 3},
+          1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}},
         // temperature
         {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}};
 
-StatsPullerManagerImpl::StatsPullerManagerImpl()
-    : mCurrentPullingInterval(LONG_MAX) {
+StatsPullerManagerImpl::StatsPullerManagerImpl() : mNextPullTimeNs(LONG_MAX) {
 }
 
-bool StatsPullerManagerImpl::Pull(int tagId, vector<shared_ptr<LogEvent>>* data) {
+bool StatsPullerManagerImpl::Pull(const int tagId, const int64_t timeNs,
+                                  vector<shared_ptr<LogEvent>>* data) {
     VLOG("Initiating pulling %d", tagId);
 
     if (kAllPullAtomInfo.find(tagId) != kAllPullAtomInfo.end()) {
-        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(data);
+        bool ret = kAllPullAtomInfo.find(tagId)->second.puller->Pull(timeNs, data);
         VLOG("pulled %d items", (int)data->size());
         return ret;
     } else {
@@ -148,12 +197,14 @@
 }
 
 void StatsPullerManagerImpl::updateAlarmLocked() {
-    long currentTimeMs = getElapsedRealtimeMillis();
-    long nextAlarmTimeMs = currentTimeMs + mCurrentPullingInterval -
-        (currentTimeMs - mTimeBaseSec * 1000) % mCurrentPullingInterval;
+    if (mNextPullTimeNs == LONG_MAX) {
+        VLOG("No need to set alarms. Skipping");
+        return;
+    }
+
     sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
     if (statsCompanionServiceCopy != nullptr) {
-        statsCompanionServiceCopy->setPullingAlarms(nextAlarmTimeMs, mCurrentPullingInterval);
+        statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
     } else {
         VLOG("StatsCompanionService not available. Alarm not set.");
     }
@@ -174,7 +225,7 @@
 }
 
 void StatsPullerManagerImpl::RegisterReceiver(int tagId, wp<PullDataReceiver> receiver,
-                                              long intervalMs) {
+                                              int64_t nextPullTimeNs, int64_t intervalNs) {
     AutoMutex _l(mLock);
     auto& receivers = mReceivers[tagId];
     for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -185,21 +236,24 @@
     }
     ReceiverInfo receiverInfo;
     receiverInfo.receiver = receiver;
-    receiverInfo.timeInfo.first = intervalMs;
-    receivers.push_back(receiverInfo);
 
     // Round it to the nearest minutes. This is the limit of alarm manager.
-    // In practice, we should limit it higher.
-    long roundedIntervalMs = intervalMs/1000/60 * 1000 * 60;
+    // In practice, we should always have larger buckets.
+    int64_t roundedIntervalNs = intervalNs / NS_PER_SEC / 60 * NS_PER_SEC * 60;
     // Scheduled pulling should be at least 1 min apart.
     // This can be lower in cts tests, in which case we round it to 1 min.
-    if (roundedIntervalMs < 60 * 1000) {
-        roundedIntervalMs = 60 * 1000;
+    if (roundedIntervalNs < 60 * (int64_t)NS_PER_SEC) {
+        roundedIntervalNs = 60 * (int64_t)NS_PER_SEC;
     }
+
+    receiverInfo.intervalNs = roundedIntervalNs;
+    receiverInfo.nextPullTimeNs = nextPullTimeNs;
+    receivers.push_back(receiverInfo);
+
     // There is only one alarm for all pulled events. So only set it to the smallest denom.
-    if (roundedIntervalMs < mCurrentPullingInterval) {
-        VLOG("Updating pulling interval %ld", intervalMs);
-        mCurrentPullingInterval = roundedIntervalMs;
+    if (nextPullTimeNs < mNextPullTimeNs) {
+        VLOG("Updating next pull time %lld", (long long)mNextPullTimeNs);
+        mNextPullTimeNs = nextPullTimeNs;
         updateAlarmLocked();
     }
     VLOG("Puller for tagId %d registered of %d", tagId, (int)receivers.size());
@@ -224,16 +278,22 @@
 void StatsPullerManagerImpl::OnAlarmFired() {
     AutoMutex _l(mLock);
 
-    uint64_t currentTimeMs = getElapsedRealtimeMillis();
+    int64_t currentTimeNs = getElapsedRealtimeNs();
+
+    int64_t minNextPullTimeNs = LONG_MAX;
 
     vector<pair<int, vector<ReceiverInfo*>>> needToPull =
             vector<pair<int, vector<ReceiverInfo*>>>();
     for (auto& pair : mReceivers) {
         vector<ReceiverInfo*> receivers = vector<ReceiverInfo*>();
         if (pair.second.size() != 0) {
-            for (auto& receiverInfo : pair.second) {
-                if (receiverInfo.timeInfo.first + receiverInfo.timeInfo.second > currentTimeMs) {
+            for (ReceiverInfo& receiverInfo : pair.second) {
+                if (receiverInfo.nextPullTimeNs < currentTimeNs) {
                     receivers.push_back(&receiverInfo);
+                } else {
+                    if (receiverInfo.nextPullTimeNs < minNextPullTimeNs) {
+                        minNextPullTimeNs = receiverInfo.nextPullTimeNs;
+                    }
                 }
             }
             if (receivers.size() > 0) {
@@ -244,18 +304,29 @@
 
     for (const auto& pullInfo : needToPull) {
         vector<shared_ptr<LogEvent>> data;
-        if (Pull(pullInfo.first, &data)) {
+        if (Pull(pullInfo.first, currentTimeNs, &data)) {
             for (const auto& receiverInfo : pullInfo.second) {
                 sp<PullDataReceiver> receiverPtr = receiverInfo->receiver.promote();
                 if (receiverPtr != nullptr) {
                     receiverPtr->onDataPulled(data);
-                    receiverInfo->timeInfo.second = currentTimeMs;
+                    // 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;
+                    if (receiverInfo->nextPullTimeNs < minNextPullTimeNs) {
+                        minNextPullTimeNs = receiverInfo->nextPullTimeNs;
+                    }
                 } else {
                     VLOG("receiver already gone.");
                 }
             }
         }
     }
+
+    mNextPullTimeNs = minNextPullTimeNs;
+    updateAlarmLocked();
 }
 
 int StatsPullerManagerImpl::ForceClearPullerCache() {
@@ -266,10 +337,10 @@
     return totalCleared;
 }
 
-int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(long timestampSec) {
+int StatsPullerManagerImpl::ClearPullerCacheIfNecessary(int64_t timestampNs) {
     int totalCleared = 0;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
-        totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampSec);
+        totalCleared += pulledAtom.second.puller->ClearCacheIfNecessary(timestampNs);
     }
     return totalCleared;
 }
diff --git a/cmds/statsd/src/external/StatsPullerManagerImpl.h b/cmds/statsd/src/external/StatsPullerManagerImpl.h
index 682ad33..8c771f3 100644
--- a/cmds/statsd/src/external/StatsPullerManagerImpl.h
+++ b/cmds/statsd/src/external/StatsPullerManagerImpl.h
@@ -41,7 +41,7 @@
   std::vector<int> nonAdditiveFields;
   // How long should the puller wait before doing an actual pull again. Default
   // 1 sec. Set this to 0 if this is handled elsewhere.
-  long coolDownSec = 1;
+  int64_t coolDownNs = 1 * NS_PER_SEC;
   // The actual puller
   sp<StatsPuller> puller;
 } PullAtomInfo;
@@ -50,7 +50,8 @@
 public:
     static StatsPullerManagerImpl& GetInstance();
 
-    void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, long intervalMs);
+    void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+                          int64_t intervalNs);
 
     void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
 
@@ -59,13 +60,11 @@
 
     void OnAlarmFired();
 
-    bool Pull(const int tagId, vector<std::shared_ptr<LogEvent>>* data);
-
-    void SetTimeBaseSec(long timeBaseSec) {mTimeBaseSec = timeBaseSec;};
+    bool Pull(const int tagId, const int64_t timeNs, vector<std::shared_ptr<LogEvent>>* data);
 
     int ForceClearPullerCache();
 
-    int ClearPullerCacheIfNecessary(long timestampSec);
+    int ClearPullerCacheIfNecessary(int64_t timestampNs);
 
     void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
 
@@ -77,8 +76,8 @@
     sp<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     typedef struct {
-        // pull_interval_sec : last_pull_time_sec
-        std::pair<uint64_t, uint64_t> timeInfo;
+        int64_t nextPullTimeNs;
+        int64_t intervalNs;
         wp<PullDataReceiver> receiver;
     } ReceiverInfo;
 
@@ -90,12 +89,7 @@
 
     void updateAlarmLocked();
 
-    long mCurrentPullingInterval;
-
-    // for pulled metrics, it is important for the buckets to be aligned to multiple of smallest
-    // bucket size. All pulled metrics start pulling based on this time, so that they can be
-    // correctly attributed to the correct buckets.
-    long mTimeBaseSec;
+    int64_t mNextPullTimeNs;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
index 7675888..7f8755b 100644
--- a/cmds/statsd/src/guardrail/StatsdStats.h
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -78,17 +78,17 @@
     ~StatsdStats(){};
 
     // TODO: set different limit if the device is low ram.
-    const static int kDimensionKeySizeSoftLimit = 300;
-    const static int kDimensionKeySizeHardLimit = 500;
+    const static int kDimensionKeySizeSoftLimit = 500;
+    const static int kDimensionKeySizeHardLimit = 800;
 
     // Per atom dimension key size limit
     static const std::map<int, std::pair<size_t, size_t>> kAtomDimensionKeySizeLimitMap;
 
-    const static int kMaxConfigCount = 10;
+    const static int kMaxConfigCountPerUid = 10;
     const static int kMaxAlertCountPerConfig = 100;
-    const static int kMaxConditionCountPerConfig = 200;
-    const static int kMaxMetricCountPerConfig = 300;
-    const static int kMaxMatcherCountPerConfig = 500;
+    const static int kMaxConditionCountPerConfig = 300;
+    const static int kMaxMetricCountPerConfig = 1000;
+    const static int kMaxMatcherCountPerConfig = 800;
 
     // The max number of old config stats we keep.
     const static int kMaxIceBoxSize = 20;
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 8e8a529..afa5140 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -117,7 +117,8 @@
     }
 }
 
-void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
+void CountMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                           const uint64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index ef738ac..fd9f0e0 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -61,7 +61,7 @@
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, const uint64_t eventTime) override;
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c6b9405..c28bb88 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -169,7 +169,8 @@
 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
 // 2. No condition in dimension
 // 3. The links covers all dimension fields in the sliced child condition predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime) {
+void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt1(bool condition,
+                                                                   const uint64_t eventTime) {
     if (mMetric2ConditionLinks.size() != 1 ||
         !mHasLinksToAllConditionDimensionsInTracker ||
         !mDimensionsInCondition.empty()) {
@@ -241,7 +242,8 @@
 // SlicedConditionChange optimization case 2:
 // 1. If combination condition, logical operation is AND, only one sliced child predicate.
 // 2. Has dimensions_in_condition and it equals to the output dimensions of the sliced predicate.
-void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime) {
+void DurationMetricProducer::onSlicedConditionMayChangeLocked_opt2(bool condition,
+                                                                   const uint64_t eventTime) {
     if (mMetric2ConditionLinks.size() > 1 || !mSameConditionDimensionsInTracker) {
         return;
     }
@@ -322,7 +324,8 @@
     }
 }
 
-void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
+void DurationMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                              const uint64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
     flushIfNeededLocked(eventTime);
 
@@ -333,20 +336,20 @@
     bool changeDimTrackable = mWizard->IsChangedDimensionTrackable(mConditionTrackerIndex);
     if (changeDimTrackable && mHasLinksToAllConditionDimensionsInTracker &&
         mDimensionsInCondition.empty()) {
-        onSlicedConditionMayChangeLocked_opt1(eventTime);
+        onSlicedConditionMayChangeLocked_opt1(overallCondition, eventTime);
         return;
     }
 
     if (changeDimTrackable && mSameConditionDimensionsInTracker &&
         mMetric2ConditionLinks.size() <= 1) {
-        onSlicedConditionMayChangeLocked_opt2(eventTime);
+        onSlicedConditionMayChangeLocked_opt2(overallCondition, eventTime);
         return;
     }
 
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         for (auto& pair : whatIt.second) {
-            pair.second->onSlicedConditionMayChange(eventTime);
+            pair.second->onSlicedConditionMayChange(overallCondition, eventTime);
         }
     }
 
@@ -372,7 +375,7 @@
                     if (newTracker != nullptr) {
                         newTracker->setEventKey(MetricDimensionKey(
                                 whatIt.first, conditionDimension));
-                        newTracker->onSlicedConditionMayChange(eventTime);
+                        newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
                 }
@@ -398,7 +401,7 @@
                     if (newTracker != nullptr) {
                         newTracker->setEventKey(
                             MetricDimensionKey(whatIt.first, conditionDimension));
-                        newTracker->onSlicedConditionMayChange(eventTime);
+                        newTracker->onSlicedConditionMayChange(overallCondition, eventTime);
                         whatIt.second[conditionDimension] = std::move(newTracker);
                     }
                 }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 985749d..75f2391 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -68,10 +68,10 @@
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, const uint64_t eventTime) override;
 
-    void onSlicedConditionMayChangeLocked_opt1(const uint64_t eventTime);
-    void onSlicedConditionMayChangeLocked_opt2(const uint64_t eventTime);
+    void onSlicedConditionMayChangeLocked_opt1(bool overallCondition, const uint64_t eventTime);
+    void onSlicedConditionMayChangeLocked_opt2(bool overallCondition, const uint64_t eventTime);
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index bd8b293..42a5a3a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -79,7 +79,8 @@
     mProto->clear();
 }
 
-void EventMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
+void EventMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                           const uint64_t eventTime) {
 }
 
 std::unique_ptr<std::vector<uint8_t>> serializeProtoLocked(ProtoOutputStream& protoOutput) {
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index d6f81fd..93c6c9a 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -53,7 +53,7 @@
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, const uint64_t eventTime) override;
 
     void dropDataLocked(const uint64_t dropTimeNs) override;
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index f08d54a..b13c3e7 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -112,11 +112,13 @@
 
     // Kicks off the puller immediately.
     if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
-        mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+        mStatsPullerManager->RegisterReceiver(
+                mPullTagId, this, mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
     }
 
-    VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
-         (long long)mBucketSizeNs, (long long)mStartTimeNs);
+    VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
+         (long long)metric.id(), (long long)mBucketSizeNs, (long long)mStartTimeNs,
+         mConditionSliced);
 }
 
 // for testing
@@ -155,7 +157,7 @@
 
 void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    VLOG("gauge metric %lld report now...", (long long)mMetricId);
+    VLOG("Gauge metric %lld report now...", (long long)mMetricId);
 
     flushIfNeededLocked(dumpTimeNs);
     if (mPastBuckets.empty()) {
@@ -168,7 +170,7 @@
     for (const auto& pair : mPastBuckets) {
         const MetricDimensionKey& dimensionKey = pair.first;
 
-        VLOG("  dimension key %s", dimensionKey.toString().c_str());
+        VLOG("Gauge dimension key %s", dimensionKey.toString().c_str());
         uint64_t wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
@@ -221,8 +223,9 @@
                 }
             }
             protoOutput->end(bucketInfoToken);
-            VLOG("\t bucket [%lld - %lld] includes %d atoms.", (long long)bucket.mBucketStartNs,
-                 (long long)bucket.mBucketEndNs, (int)bucket.mGaugeAtoms.size());
+            VLOG("Gauge \t bucket [%lld - %lld] includes %d atoms.",
+                 (long long)bucket.mBucketStartNs, (long long)bucket.mBucketEndNs,
+                 (int)bucket.mGaugeAtoms.size());
         }
         protoOutput->end(wrapperToken);
     }
@@ -233,27 +236,6 @@
 }
 
 void GaugeMetricProducer::pullLocked() {
-    vector<std::shared_ptr<LogEvent>> allData;
-    if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
-        ALOGE("Stats puller failed for tag: %d", mPullTagId);
-        return;
-    }
-    for (const auto& data : allData) {
-        onMatchedLogEventLocked(0, *data);
-    }
-}
-
-void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
-                                                   const uint64_t eventTime) {
-    VLOG("Metric %lld onConditionChanged", (long long)mMetricId);
-    flushIfNeededLocked(eventTime);
-    mCondition = conditionMet;
-
-    // Push mode. No need to proactively pull the gauge data.
-    if (mPullTagId == -1) {
-        return;
-    }
-
     bool triggerPuller = false;
     switch(mSamplingType) {
         // When the metric wants to do random sampling and there is already one gauge atom for the
@@ -274,18 +256,38 @@
     }
 
     vector<std::shared_ptr<LogEvent>> allData;
-    if (!mStatsPullerManager->Pull(mPullTagId, &allData)) {
-        ALOGE("Stats puller failed for tag: %d", mPullTagId);
+    if (!mStatsPullerManager->Pull(mPullTagId, getElapsedRealtimeNs(), &allData)) {
+        ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
         return;
     }
+
     for (const auto& data : allData) {
         onMatchedLogEventLocked(0, *data);
     }
-    flushIfNeededLocked(eventTime);
 }
 
-void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
+void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
+                                                   const uint64_t eventTime) {
+    VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
+    flushIfNeededLocked(eventTime);
+    mCondition = conditionMet;
+
+    if (mPullTagId != -1) {
+        pullLocked();
+    }  // else: Push mode. No need to proactively pull the gauge data.
+}
+
+void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                           const uint64_t eventTime) {
+    VLOG("GaugeMetric %lld onSlicedConditionMayChange overall condition %d", (long long)mMetricId,
+         overallCondition);
+    flushIfNeededLocked(eventTime);
+    // 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();
+    }  // else: Push mode. No need to proactively pull the gauge data.
 }
 
 std::shared_ptr<vector<FieldValue>> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
@@ -337,7 +339,7 @@
     uint64_t eventTimeNs = event.GetElapsedTimestampNs();
     mTagId = event.GetTagId();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
-        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
+        VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         return;
     }
@@ -403,8 +405,8 @@
     uint64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
 
     if (eventTimeNs < currentBucketEndTimeNs) {
-        VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs,
-             (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
+        VLOG("Gauge eventTime is %lld, less than next bucket start time %lld",
+             (long long)eventTimeNs, (long long)(mCurrentBucketStartTimeNs + mBucketSizeNs));
         return;
     }
 
@@ -414,7 +416,7 @@
     int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
-    VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId,
+    VLOG("Gauge metric %lld: new bucket start time: %lld", (long long)mMetricId,
          (long long)mCurrentBucketStartTimeNs);
 }
 
@@ -433,7 +435,7 @@
         info.mGaugeAtoms = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        VLOG("gauge metric %lld, dump key value: %s", (long long)mMetricId,
+        VLOG("Gauge gauge metric %lld, dump key value: %s", (long long)mMetricId,
              slice.first.toString().c_str());
     }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 4b543f8..9605b13 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -100,7 +100,7 @@
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, const uint64_t eventTime) override;
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
@@ -155,6 +155,7 @@
     const size_t mDimensionHardLimit;
 
     FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
+    FRIEND_TEST(GaugeMetricProducerTest, TestWithSlicedCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index f4495a1..bf529c8 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -33,7 +33,6 @@
 
     bool condition;
     ConditionKey conditionKey;
-
     std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mMetric2ConditionLinks) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 4983f96..0b3d677 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -98,9 +98,9 @@
         onConditionChangedLocked(condition, eventTime);
     }
 
-    void onSlicedConditionMayChange(const uint64_t eventTime) {
+    void onSlicedConditionMayChange(bool overallCondition, const uint64_t eventTime) {
         std::lock_guard<std::mutex> lock(mMutex);
-        onSlicedConditionMayChangeLocked(eventTime);
+        onSlicedConditionMayChangeLocked(overallCondition, eventTime);
     }
 
     bool isConditionSliced() const {
@@ -163,7 +163,8 @@
 
 protected:
     virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0;
-    virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0;
+    virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                  const uint64_t eventTime) = 0;
     virtual void onDumpReportLocked(const uint64_t dumpTimeNs,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual size_t byteSizeLocked() const = 0;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index c773d4f..1be082a 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -326,7 +326,8 @@
                     // notification, and the metric can query the sliced conditions that are
                     // interesting to it.
                 } else {
-                    mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
+                    mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
+                                                                                 eventTime);
                 }
             }
         }
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index ed4d7e4..d0f510d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -110,10 +110,12 @@
     }
     mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
 
-    if (!metric.has_condition() && mPullTagId != -1) {
-        VLOG("Setting up periodic pulling for %d", mPullTagId);
-        mStatsPullerManager->RegisterReceiver(mPullTagId, this, bucketSizeMills);
+    // Kicks off the puller immediately.
+    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);
 }
@@ -134,7 +136,8 @@
     }
 }
 
-void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
+void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition,
+                                                           const uint64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
 }
 
@@ -193,26 +196,21 @@
     // TODO: Clear mDimensionKeyMap once the report is dumped.
 }
 
-void ValueMetricProducer::onConditionChangedLocked(const bool condition, const uint64_t eventTime) {
+void ValueMetricProducer::onConditionChangedLocked(const bool condition,
+                                                   const uint64_t eventTimeNs) {
     mCondition = condition;
 
-    if (eventTime < mCurrentBucketStartTimeNs) {
-        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTime,
+    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+        VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
              (long long)mCurrentBucketStartTimeNs);
         return;
     }
 
-    flushIfNeededLocked(eventTime);
+    flushIfNeededLocked(eventTimeNs);
 
     if (mPullTagId != -1) {
-        if (mCondition == true) {
-            mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000);
-        } else if (mCondition == false) {
-            mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
-        }
-
         vector<shared_ptr<LogEvent>> allData;
-        if (mStatsPullerManager->Pull(mPullTagId, &allData)) {
+        if (mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
             if (allData.size() == 0) {
                 return;
             }
@@ -314,8 +312,13 @@
 
     if (mPullTagId != -1) { // for pulled events
         if (mCondition == true) {
-            interval.start = value;
-            interval.startUpdated = true;
+            if (!interval.startUpdated) {
+                interval.start = value;
+                interval.startUpdated = true;
+            } else {
+                // skip it if there is already value recorded for the start
+                VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str());
+            }
         } else {
             // Generally we expect value to be monotonically increasing.
             // If not, there was a reset event. We take the absolute value as
@@ -384,6 +387,7 @@
     int tainted = 0;
     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];
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index c4477b3..45d9531 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -53,7 +53,7 @@
 
         if (mPullTagId != -1) {
             vector<shared_ptr<LogEvent>> allData;
-            mStatsPullerManager->Pull(mPullTagId, &allData);
+            mStatsPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
             if (allData.size() == 0) {
                 // This shouldn't happen since this valuemetric is not useful now.
             }
@@ -93,7 +93,7 @@
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(const uint64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, const uint64_t eventTime) override;
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
@@ -159,6 +159,10 @@
     FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2);
+    FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index ddfb8cc..4132703 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -92,7 +92,7 @@
                           const bool stopAll) = 0;
     virtual void noteStopAll(const uint64_t eventTime) = 0;
 
-    virtual void onSlicedConditionMayChange(const uint64_t timestamp) = 0;
+    virtual void onSlicedConditionMayChange(bool overallCondition, const uint64_t timestamp) = 0;
     virtual void onConditionChanged(bool condition, const uint64_t timestamp) = 0;
 
     // Flush stale buckets if needed, and return true if the tracker has no on-going duration
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index df9e6ae..15d9619 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -245,7 +245,8 @@
     return flushCurrentBucket(eventTimeNs, output);
 }
 
-void MaxDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+void MaxDurationTracker::onSlicedConditionMayChange(bool overallCondition,
+                                                    const uint64_t timestamp) {
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& pair : mInfos) {
         if (pair.second.state == kStopped) {
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 32d42fa..884e8ac 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -53,7 +53,7 @@
             const uint64_t& eventTimeNs,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override;
 
-    void onSlicedConditionMayChange(const uint64_t timestamp) override;
+    void onSlicedConditionMayChange(bool overallCondition, const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index da79217..50db9a0 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -214,7 +214,8 @@
     return flushCurrentBucket(eventTimeNs, output);
 }
 
-void OringDurationTracker::onSlicedConditionMayChange(const uint64_t timestamp) {
+void OringDurationTracker::onSlicedConditionMayChange(bool overallCondition,
+                                                      const uint64_t timestamp) {
     vector<pair<HashableDimensionKey, int>> startedToPaused;
     vector<pair<HashableDimensionKey, int>> pausedToStarted;
     if (!mStarted.empty()) {
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index ca8abfe..987e28e 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -45,7 +45,7 @@
                   const bool stopAll) override;
     void noteStopAll(const uint64_t eventTime) override;
 
-    void onSlicedConditionMayChange(const uint64_t timestamp) override;
+    void onSlicedConditionMayChange(bool overallCondition, const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
 
     bool flushCurrentBucket(
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index cab61e9..efd810f 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -221,7 +221,8 @@
 
 int64_t TimeUnitToBucketSizeInMillisGuardrailed(int uid, TimeUnit unit) {
     int64_t bucketSizeMillis = TimeUnitToBucketSizeInMillis(unit);
-    if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL) {
+    if (bucketSizeMillis > 1000 && bucketSizeMillis < 5 * 60 * 1000LL && uid != AID_SHELL &&
+        uid != AID_ROOT) {
         bucketSizeMillis = 5 * 60 * 1000LL;
     }
     return bucketSizeMillis;
diff --git a/cmds/statsd/statsd.rc b/cmds/statsd/statsd.rc
index 920273b..3a0c224 100644
--- a/cmds/statsd/statsd.rc
+++ b/cmds/statsd/statsd.rc
@@ -20,5 +20,5 @@
 
 on post-fs-data
     # Create directory for statsd
-    mkdir /data/misc/stats-data/ 0770 statsd statsd
-    mkdir /data/misc/stats-service/ 0770 statsd statsd
+    mkdir /data/misc/stats-data/ 0770 statsd system
+    mkdir /data/misc/stats-service/ 0770 statsd system
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 90c3a2f..838745e 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -64,16 +64,6 @@
 
 const int64_t testConfigId = 12345;
 
-TEST(ConfigManagerTest, TestFakeConfig) {
-    auto metricsManager = std::make_unique<MetricsManager>(
-        ConfigKey(0, testConfigId), build_fake_config(), 1000, new UidMap(),
-        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){}),
-        new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){}));
-    EXPECT_TRUE(metricsManager->isConfigValid());
-}
-
 /**
  * Test the addOrUpdate and remove methods
  */
diff --git a/cmds/statsd/tests/FieldValue_test.cpp b/cmds/statsd/tests/FieldValue_test.cpp
index 73e7c44..5a6aba6 100644
--- a/cmds/statsd/tests/FieldValue_test.cpp
+++ b/cmds/statsd/tests/FieldValue_test.cpp
@@ -356,7 +356,7 @@
     EXPECT_EQ("location1", atom.attribution_node(0).tag());
     EXPECT_EQ(2222, atom.attribution_node(1).uid());
     EXPECT_EQ("location2", atom.attribution_node(1).tag());
-    EXPECT_EQ(999, atom.num_of_results());
+    EXPECT_EQ(999, atom.num_results());
 }
 
 
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 2e6a0f0..2b91324 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -34,7 +34,7 @@
     *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
     *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
 
-    auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_CHANGED);
+    auto atomMatcher = CreateSimpleAtomMatcher("", android::util::APP_START_OCCURRED);
     *config.add_atom_matcher() = atomMatcher;
 
     auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
@@ -49,18 +49,18 @@
     gaugeMetric->mutable_gauge_fields_filter()->set_include_all(false);
     gaugeMetric->set_sampling_type(sampling_type);
     auto fieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
-    fieldMatcher->set_field(android::util::APP_START_CHANGED);
+    fieldMatcher->set_field(android::util::APP_START_OCCURRED);
     fieldMatcher->add_child()->set_field(3);  // type (enum)
     fieldMatcher->add_child()->set_field(4);  // activity_name(str)
     fieldMatcher->add_child()->set_field(7);  // activity_start_msec(int64)
     *gaugeMetric->mutable_dimensions_in_what() =
-        CreateDimensions(android::util::APP_START_CHANGED, {1 /* uid field */ });
+        CreateDimensions(android::util::APP_START_OCCURRED, {1 /* uid field */ });
     gaugeMetric->set_bucket(FIVE_MINUTES);
 
     auto links = gaugeMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
     auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(android::util::APP_START_CHANGED);
+    dimensionWhat->set_field(android::util::APP_START_OCCURRED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
     auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
@@ -68,12 +68,12 @@
     return config;
 }
 
-std::unique_ptr<LogEvent> CreateAppStartChangedEvent(
-    const int uid, const string& pkg_name, AppStartChanged::TransitionType type,
+std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
+    const int uid, const string& pkg_name, AppStartOccurred::TransitionType type,
     const string& activity_name, const string& calling_pkg_name, const bool is_instant_app,
     int64_t activity_start_msec, uint64_t timestampNs) {
     auto logEvent = std::make_unique<LogEvent>(
-        android::util::APP_START_CHANGED, timestampNs);
+        android::util::APP_START_OCCURRED, timestampNs);
     logEvent->write(uid);
     logEvent->write(pkg_name);
     logEvent->write(type);
@@ -112,32 +112,32 @@
             appUid1, bucketStartTimeNs + 2 * bucketSizeNs + 100));
 
 
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::WARM, "activity_name1", "calling_pkg_name1",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::WARM, "activity_name1", "calling_pkg_name1",
             true /*is_instant_app*/, 101 /*activity_start_msec*/, bucketStartTimeNs + 10));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::HOT, "activity_name2", "calling_pkg_name2",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::HOT, "activity_name2", "calling_pkg_name2",
             true /*is_instant_app*/, 102 /*activity_start_msec*/, bucketStartTimeNs + 20));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::COLD, "activity_name3", "calling_pkg_name3",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::COLD, "activity_name3", "calling_pkg_name3",
             true /*is_instant_app*/, 103 /*activity_start_msec*/, bucketStartTimeNs + 30));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::WARM, "activity_name4", "calling_pkg_name4",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::WARM, "activity_name4", "calling_pkg_name4",
             true /*is_instant_app*/, 104 /*activity_start_msec*/,
             bucketStartTimeNs + bucketSizeNs + 30));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::COLD, "activity_name5", "calling_pkg_name5",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::COLD, "activity_name5", "calling_pkg_name5",
             true /*is_instant_app*/, 105 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid1, "app1", AppStartChanged::HOT, "activity_name6", "calling_pkg_name6",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid1, "app1", AppStartOccurred::HOT, "activity_name6", "calling_pkg_name6",
             false /*is_instant_app*/, 106 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs + 10));
 
         events.push_back(CreateMoveToBackgroundEvent(
                 appUid2, bucketStartTimeNs + bucketSizeNs + 10));
-        events.push_back(CreateAppStartChangedEvent(
-            appUid2, "app2", AppStartChanged::COLD, "activity_name7", "calling_pkg_name7",
+        events.push_back(CreateAppStartOccurredEvent(
+            appUid2, "app2", AppStartOccurred::COLD, "activity_name7", "calling_pkg_name7",
             true /*is_instant_app*/, 201 /*activity_start_msec*/,
             bucketStartTimeNs + 2 * bucketSizeNs + 10));
 
@@ -159,7 +159,7 @@
         EXPECT_EQ(2, gaugeMetrics.data_size());
 
         auto data = gaugeMetrics.data(0);
-        EXPECT_EQ(android::util::APP_START_CHANGED, data.dimensions_in_what().field());
+        EXPECT_EQ(android::util::APP_START_OCCURRED, data.dimensions_in_what().field());
         EXPECT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
         EXPECT_EQ(1 /* uid field */,
                   data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -171,29 +171,29 @@
             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(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type());
+            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_changed().activity_name());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
-                      data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(0).atom(1).app_start_changed().type());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(0).atom(1).app_start_occurred().type());
             EXPECT_EQ("activity_name3",
-                      data.bucket_info(0).atom(1).app_start_changed().activity_name());
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_name());
             EXPECT_EQ(103L,
-                      data.bucket_info(0).atom(1).app_start_changed().activity_start_millis());
+                      data.bucket_info(0).atom(1).app_start_occurred().activity_start_millis());
 
             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(AppStartChanged::WARM,
-                      data.bucket_info(1).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
-                      data.bucket_info(1).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(104L,
-                      data.bucket_info(1).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(2, data.bucket_info(2).atom_size());
             EXPECT_EQ(2, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -202,41 +202,41 @@
                       data.bucket_info(2).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
                       data.bucket_info(2).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(2).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
-                      data.bucket_info(2).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(105L,
-                      data.bucket_info(2).atom(0).app_start_changed().activity_start_millis());
-            EXPECT_EQ(AppStartChanged::HOT,
-                      data.bucket_info(2).atom(1).app_start_changed().type());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
+            EXPECT_EQ(AppStartOccurred::HOT,
+                      data.bucket_info(2).atom(1).app_start_occurred().type());
             EXPECT_EQ("activity_name6",
-                      data.bucket_info(2).atom(1).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_name());
             EXPECT_EQ(106L,
-                      data.bucket_info(2).atom(1).app_start_changed().activity_start_millis());
+                      data.bucket_info(2).atom(1).app_start_occurred().activity_start_millis());
         } else {
             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(AppStartChanged::HOT, data.bucket_info(0).atom(0).app_start_changed().type());
+            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_changed().activity_name());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(102L,
-                      data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
 
             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(AppStartChanged::WARM,
-                      data.bucket_info(1).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::WARM,
+                      data.bucket_info(1).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name4",
-                      data.bucket_info(1).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(104L,
-                      data.bucket_info(1).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(1).atom(0).app_start_occurred().activity_start_millis());
 
             EXPECT_EQ(1, data.bucket_info(2).atom_size());
             EXPECT_EQ(1, data.bucket_info(2).elapsed_timestamp_nanos_size());
@@ -245,17 +245,17 @@
                       data.bucket_info(2).start_bucket_nanos());
             EXPECT_EQ(bucketStartTimeNs + 3 * bucketSizeNs,
                       data.bucket_info(2).end_bucket_nanos());
-            EXPECT_EQ(AppStartChanged::COLD,
-                      data.bucket_info(2).atom(0).app_start_changed().type());
+            EXPECT_EQ(AppStartOccurred::COLD,
+                      data.bucket_info(2).atom(0).app_start_occurred().type());
             EXPECT_EQ("activity_name5",
-                      data.bucket_info(2).atom(0).app_start_changed().activity_name());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_name());
             EXPECT_EQ(105L,
-                      data.bucket_info(2).atom(0).app_start_changed().activity_start_millis());
+                      data.bucket_info(2).atom(0).app_start_occurred().activity_start_millis());
         }
 
         data = gaugeMetrics.data(1);
 
-        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_CHANGED);
+        EXPECT_EQ(data.dimensions_in_what().field(), android::util::APP_START_OCCURRED);
         EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
         EXPECT_EQ(1 /* uid field */,
                   data.dimensions_in_what().value_tuple().dimensions_value(0).field());
@@ -266,10 +266,10 @@
         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(AppStartChanged::COLD, data.bucket_info(0).atom(0).app_start_changed().type());
+        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_changed().activity_name());
-        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_changed().activity_start_millis());
+                  data.bucket_info(0).atom(0).app_start_occurred().activity_name());
+        EXPECT_EQ(201L, data.bucket_info(0).atom(0).app_start_occurred().activity_start_millis());
     }
 }
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
index 5c4eda8..04ce73a7 100644
--- a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -210,7 +210,7 @@
 
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 1);
     stats.noteAtomLogged(android::util::SENSOR_STATE_CHANGED, now + 2);
-    stats.noteAtomLogged(android::util::DROPBOX_ERROR_CHANGED, now + 3);
+    stats.noteAtomLogged(android::util::APP_CRASH_OCCURRED, now + 3);
     // pulled event, should ignore
     stats.noteAtomLogged(android::util::WIFI_BYTES_TRANSFER, now + 4);
 
@@ -228,7 +228,7 @@
         if (atomStats.tag() == android::util::SENSOR_STATE_CHANGED && atomStats.count() == 3) {
             sensorAtomGood = true;
         }
-        if (atomStats.tag() == android::util::DROPBOX_ERROR_CHANGED && atomStats.count() == 1) {
+        if (atomStats.tag() == android::util::APP_CRASH_OCCURRED && atomStats.count() == 1) {
             dropboxAtomGood = true;
         }
     }
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index a75d6c8..7ca66fd 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -63,7 +63,7 @@
     // For now we still need this so that it doesn't do real pulling.
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -213,10 +213,11 @@
 
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventUpgradeTimeNs);
                 event->write("some value");
@@ -281,10 +282,11 @@
 
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write("some value");
@@ -338,13 +340,90 @@
                             ->mValue.int_value);
 }
 
+TEST(GaugeMetricProducerTest, TestWithSlicedCondition) {
+    const int conditionTag = 65;
+    GaugeMetric metric;
+    metric.set_id(1111111);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    metric.set_condition(StringToId("APP_DIED"));
+    auto dim = metric.mutable_dimensions_in_what();
+    dim->set_field(tagId);
+    dim->add_child()->set_field(1);
+
+    dim = metric.mutable_dimensions_in_condition();
+    dim->set_field(conditionTag);
+    dim->add_child()->set_field(1);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    EXPECT_CALL(*wizard, query(_, _, _, _, _, _))
+            .WillRepeatedly(
+                    Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
+                              const vector<Matcher>& dimensionFields, const bool isSubsetDim,
+                              const bool isPartialLink,
+                              std::unordered_set<HashableDimensionKey>* dimensionKeySet) {
+                        dimensionKeySet->clear();
+                        int pos[] = {1, 0, 0};
+                        Field f(conditionTag, pos, 0);
+                        HashableDimensionKey key;
+                        key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
+                        dimensionKeySet->insert(key);
+
+                        return ConditionState::kTrue;
+                    }));
+
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(1000);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    GaugeMetricProducer gaugeProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
+    gaugeProducer.setBucketSize(60 * NS_PER_SEC);
+
+    gaugeProducer.onSlicedConditionMayChange(true, bucketStartTimeNs + 8);
+
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
+    EXPECT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+
+    EXPECT_EQ(1UL, key.getDimensionKeyInCondition().getValues().size());
+    EXPECT_EQ(1000000, key.getDimensionKeyInCondition().getValues()[0].mValue.int_value);
+
+    EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(1000);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    gaugeProducer.onDataPulled(allData);
+
+    EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
+    EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+}
+
 TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
     sp<AlarmMonitor> alarmMonitor;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     GaugeMetric metric;
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 13cdb0b..817dcae 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -209,7 +209,7 @@
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
 
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+    tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
 
     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
 
@@ -249,9 +249,9 @@
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
+    tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 5);
     // condition to true.
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 1000);
+    tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 1000);
     // 2nd duration: 1000ns
     tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
 
@@ -291,7 +291,7 @@
 
     tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
 
-    tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
+    tracker.onSlicedConditionMayChange(true, eventStartTimeNs + 15);
 
     tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
 
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index a8eb2703..c650a06 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -45,6 +45,8 @@
 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
+const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
+const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
 const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
 
 /*
@@ -62,7 +64,7 @@
     // For now we still need this so that it doesn't do real pulling.
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
@@ -141,11 +143,12 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(tagId);
@@ -154,7 +157,8 @@
                 data->push_back(event);
                 return true;
             }))
-            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
                 event->write(tagId);
@@ -260,10 +264,11 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     shared_ptr<MockStatsPullerManager> pullerManager =
             make_shared<StrictMock<MockStatsPullerManager>>();
-    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, _))
-            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
                 event->write(tagId);
@@ -428,6 +433,376 @@
             std::ceil(1.0 * event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
 }
 
+// Test value metric no condition, the pull on bucket boundary come in time and too late
+TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      tagId, bucketStartTimeNs, pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+
+    vector<shared_ptr<LogEvent>> allData;
+    // pull 1
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
+    event->write(tagId);
+    event->write(11);
+    event->init();
+    allData.push_back(event);
+
+    valueProducer.onDataPulled(allData);
+    // 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);
+
+    // pull 2 at correct time
+    allData.clear();
+    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
+    event->write(tagId);
+    event->write(23);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // tartUpdated:false tainted:0 sum:12
+    EXPECT_EQ(true, curInterval.startUpdated);
+    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(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
+
+    // pull 3 come late.
+    // The previous bucket gets closed with error. (Has start value 23, no ending)
+    // Another bucket gets closed with error. (No start, but ending with 36)
+    // The new bucket is back to normal.
+    allData.clear();
+    event = make_shared<LogEvent>(tagId, bucket6StartTimeNs + 1);
+    event->write(tagId);
+    event->write(36);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // startUpdated:false tainted:0 sum:12
+    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(0, curInterval.tainted);
+    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);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
+ * was delivered late.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            // condition becomes true
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(tagId);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // condition becomes false
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                event->write(tagId);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // startUpdated:false tainted:0 sum:0 start:100
+    EXPECT_EQ(100, curInterval.start);
+    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(0, curInterval.tainted);
+    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    // pull on bucket boundary come late, condition change happens before it
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    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);
+
+    // Now the alarm is delivered.
+    // since the condition turned to off before this pull finish, it has no effect
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
+    event->write(1);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    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);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
+ * change to false, and then true again. This is due to alarm delivered late.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            // condition becomes true
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(tagId);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // condition becomes false
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20);
+                event->write(tagId);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // condition becomes true again
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30);
+                event->write(tagId);
+                event->write(130);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // startUpdated:false tainted:0 sum:0 start:100
+    EXPECT_EQ(100, curInterval.start);
+    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(0, curInterval.tainted);
+    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    // pull on bucket boundary come late, condition change happens before it
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    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);
+
+    // condition changed to true again, before the pull alarm is delivered
+    valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(true, curInterval.startUpdated);
+    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);
+
+    // Now the alarm is delivered, but it is considered late, it has no effect
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50);
+    event->write(1);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    EXPECT_EQ(true, curInterval.startUpdated);
+    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);
+}
+
+/*
+ * Test pulled event with non sliced condition. The pull on boundary come late because the puller is
+ * very slow.
+ */
+TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) {
+    ValueMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.mutable_value_field()->set_field(tagId);
+    metric.mutable_value_field()->add_child()->set_field(2);
+    metric.set_condition(StringToId("SCREEN_ON"));
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
+    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
+    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
+
+    EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+            // condition becomes true
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(tagId);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            // condition becomes false
+            .WillOnce(Invoke([](int tagId, int64_t timeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 20);
+                event->write(tagId);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
+
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
+    valueProducer.setBucketSize(60 * NS_PER_SEC);
+    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
+
+    // has one slice
+    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
+    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    // startUpdated:false tainted:0 sum:0 start:100
+    EXPECT_EQ(100, curInterval.start);
+    EXPECT_EQ(true, curInterval.startUpdated);
+    EXPECT_EQ(0, curInterval.tainted);
+    EXPECT_EQ(0, curInterval.sum);
+    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+
+    // pull on bucket boundary come late, condition change happens before it.
+    // But puller is very slow in this one, so the data come after bucket finish
+    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    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);
+
+    // Alarm is delivered in time, but the pull is very slow, and pullers are called in order,
+    // so this one comes even later
+    vector<shared_ptr<LogEvent>> allData;
+    allData.clear();
+    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 30);
+    event->write(1);
+    event->write(110);
+    event->init();
+    allData.push_back(event);
+    valueProducer.onDataPulled(allData);
+
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
+    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);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index f040bf9..5afaba6 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -35,9 +35,11 @@
 
 class MockStatsPullerManager : public StatsPullerManager {
 public:
-    MOCK_METHOD3(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver, long intervalMs));
+    MOCK_METHOD4(RegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver,
+                                        int64_t nextPulltimeNs, int64_t intervalNs));
     MOCK_METHOD2(UnRegisterReceiver, void(int tagId, wp<PullDataReceiver> receiver));
-    MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
+    MOCK_METHOD3(Pull, bool(const int pullCode, const int64_t timeNs,
+                            vector<std::shared_ptr<LogEvent>>* data));
 };
 
 class MockUidMap : public UidMap {
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index ce44a35..6621500 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -152,42 +152,42 @@
 }
 
 AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
-    const string& name, ActivityForegroundStateChanged::Activity activity) {
+    const string& name, ActivityForegroundStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(4);  // Activity field.
-    field_value_matcher->set_eq_int(activity);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateMoveToBackgroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+        "Background", ActivityForegroundStateChanged::BACKGROUND);
 }
 
 AtomMatcher CreateMoveToForegroundAtomMatcher() {
     return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+        "Foreground", ActivityForegroundStateChanged::FOREGROUND);
 }
 
 AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
-    const string& name, ProcessLifeCycleStateChanged::Event event) {
+    const string& name, ProcessLifeCycleStateChanged::State state) {
     AtomMatcher atom_matcher;
     atom_matcher.set_id(StringToId(name));
     auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
     simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
     field_value_matcher->set_field(3);  // Process state field.
-    field_value_matcher->set_eq_int(event);
+    field_value_matcher->set_eq_int(state);
     return atom_matcher;
 }
 
 AtomMatcher CreateProcessCrashAtomMatcher() {
     return CreateProcessLifeCycleStateChangedAtomMatcher(
-        "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
+        "Crashed", ProcessLifeCycleStateChanged::CRASHED);
 }
 
 Predicate CreateScheduledJobPredicate() {
@@ -241,8 +241,8 @@
 Predicate CreateIsInBackgroundPredicate() {
     Predicate predicate;
     predicate.set_id(StringToId("IsInBackground"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("Background"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("Foreground"));
     return predicate;
 }
 
@@ -373,25 +373,25 @@
 }
 
 std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-    const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+    const int uid, const ActivityForegroundStateChanged::State state, uint64_t timestampNs) {
     auto event = std::make_unique<LogEvent>(
         android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
     event->write(uid);
     event->write("pkg_name");
     event->write("class_name");
-    event->write(activity);
+    event->write(state);
     event->init();
     return event;
 }
 
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::BACKGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
     return CreateActivityForegroundStateChangedEvent(
-        uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+        uid, ActivityForegroundStateChanged::FOREGROUND, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
@@ -418,19 +418,19 @@
 }
 
 std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
-    const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) {
+    const int uid, const ProcessLifeCycleStateChanged::State state, uint64_t timestampNs) {
     auto logEvent = std::make_unique<LogEvent>(
         android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
     logEvent->write(uid);
     logEvent->write("");
-    logEvent->write(event);
+    logEvent->write(state);
     logEvent->init();
     return logEvent;
 }
 
 std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
     return CreateProcessLifeCycleStateChangedEvent(
-        uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs);
+        uid, ProcessLifeCycleStateChanged::CRASHED, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateIsolatedUidChangedEvent(
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 0afc1ce..015c4b6 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -1,4 +1,5 @@
 Landroid/accounts/AccountManager;->mContext:Landroid/content/Context;
+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/LayoutTransition;->cancel()V
@@ -6,6 +7,7 @@
 Landroid/animation/ValueAnimator;->sDurationScale:F
 Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions;
 Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder;
+Landroid/app/ActivityGroup;->mLocalActivityManager:Landroid/app/LocalActivityManager;
 Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo;
 Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z
 Landroid/app/ActivityManager;->getMaxRecentTasksStatic()I
@@ -62,8 +64,10 @@
 Landroid/app/ActivityThread$AppBindData;->appInfo:Landroid/content/pm/ApplicationInfo;
 Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk;
 Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle;
+Landroid/app/ActivityThread$AppBindData;->persistent:Z
 Landroid/app/ActivityThread$AppBindData;->processName:Ljava/lang/String;
 Landroid/app/ActivityThread$AppBindData;->providers:Ljava/util/List;
+Landroid/app/ActivityThread$AppBindData;->restrictedBackupMode:Z
 Landroid/app/ActivityThread$BindServiceData;->intent:Landroid/content/Intent;
 Landroid/app/ActivityThread$BindServiceData;->token:Landroid/os/IBinder;
 Landroid/app/ActivityThread$CreateServiceData;->compatInfo:Landroid/content/res/CompatibilityInfo;
@@ -88,11 +92,13 @@
 Landroid/app/ActivityThread$H;->BIND_SERVICE:I
 Landroid/app/ActivityThread$H;->CREATE_SERVICE:I
 Landroid/app/ActivityThread$H;->DUMP_PROVIDER:I
+Landroid/app/ActivityThread$H;->ENTER_ANIMATION_COMPLETE:I
 Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I
 Landroid/app/ActivityThread$H;->GC_WHEN_IDLE:I
 Landroid/app/ActivityThread$H;->INSTALL_PROVIDER:I
 Landroid/app/ActivityThread$H;->RECEIVER:I
 Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I
+Landroid/app/ActivityThread$H;->SCHEDULE_CRASH:I
 Landroid/app/ActivityThread$H;->SERVICE_ARGS:I
 Landroid/app/ActivityThread$H;->STOP_SERVICE:I
 Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I
@@ -101,6 +107,7 @@
 Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList;
 Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData;
+Landroid/app/ActivityThread;->mConfiguration:Landroid/content/res/Configuration;
 Landroid/app/ActivityThread;->mDensityCompatMode:Z
 Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H;
 Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application;
@@ -109,8 +116,10 @@
 Landroid/app/ActivityThread;->mLocalProviders:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mNumVisibleActivities:I
 Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mPendingConfiguration:Landroid/content/res/Configuration;
 Landroid/app/ActivityThread;->mProviderMap:Landroid/util/ArrayMap;
 Landroid/app/ActivityThread;->mResourcePackages:Landroid/util/ArrayMap;
+Landroid/app/ActivityThread;->mResourcesManager:Landroid/app/ResourcesManager;
 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
@@ -145,6 +154,7 @@
 Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams;
 Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController;
 Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application;
+Landroid/app/AppGlobals;->getInitialPackage()Ljava/lang/String;
 Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager;
 Landroid/app/Application;->attach(Landroid/content/Context;)V
 Landroid/app/Application;->collectActivityLifecycleCallbacks()[Ljava/lang/Object;
@@ -165,21 +175,26 @@
 Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V
 Landroid/app/ApplicationPackageManager;-><init>(Landroid/app/ContextImpl;Landroid/content/pm/IPackageManager;)V
 Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager;
+Landroid/app/ApplicationPackageManager;->shouldShowRequestPermissionRationale(Ljava/lang/String;)Z
 Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I
 Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I
 Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List;
 Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService;
 Landroid/app/AppOpsManager;->noteOp(I)I
 Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I
+Landroid/app/AppOpsManager;->OP_AUDIO_NOTIFICATION_VOLUME:I
 Landroid/app/AppOpsManager;->OP_COARSE_LOCATION:I
 Landroid/app/AppOpsManager$OpEntry;->getDuration()I
 Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J
 Landroid/app/AppOpsManager;->OP_FINE_LOCATION:I
 Landroid/app/AppOpsManager;->OP_GET_USAGE_STATS:I
 Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I
+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_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
@@ -203,6 +218,7 @@
 Landroid/app/ContextImpl;->getPreferencesDir()Ljava/io/File;
 Landroid/app/ContextImpl;->getReceiverRestrictedContext()Landroid/content/Context;
 Landroid/app/ContextImpl;->mBasePackageName:Ljava/lang/String;
+Landroid/app/ContextImpl;->mClassLoader:Ljava/lang/ClassLoader;
 Landroid/app/ContextImpl;->mContentResolver:Landroid/app/ContextImpl$ApplicationContentResolver;
 Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread;
 Landroid/app/ContextImpl;->mOpPackageName:Ljava/lang/String;
@@ -212,6 +228,7 @@
 Landroid/app/ContextImpl;->mResources:Landroid/content/res/Resources;
 Landroid/app/ContextImpl;->mServiceCache:[Ljava/lang/Object;
 Landroid/app/ContextImpl;->mTheme:Landroid/content/res/Resources$Theme;
+Landroid/app/ContextImpl;->mThemeResource:I
 Landroid/app/ContextImpl;->scheduleFinalCleanup(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V
 Landroid/app/ContextImpl;->sSharedPrefsCache:Landroid/util/ArrayMap;
@@ -229,26 +246,37 @@
 Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl;
 Landroid/app/Fragment;->mWho:Ljava/lang/String;
 Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I
+Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I
 Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z
 Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V
 Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V
+Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender;
 Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String;
+Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String;
 Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I
 Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V
 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;->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
 Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration;
 Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I
+Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I
 Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J
 Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z
 Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder;
 Landroid/app/IActivityManager;->unbindService(Landroid/app/IServiceConnection;)Z
+Landroid/app/IActivityManager;->unstableProviderDied(Landroid/os/IBinder;)V
 Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I
 Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I
 Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V
+Landroid/app/INotificationManager;->cancelAllNotifications(Ljava/lang/String;I)V
+Landroid/app/INotificationManager;->cancelNotificationWithTag(Ljava/lang/String;Ljava/lang/String;II)V
+Landroid/app/INotificationManager;->cancelToast(Ljava/lang/String;Landroid/app/ITransientNotification;)V
+Landroid/app/INotificationManager;->enqueueToast(Ljava/lang/String;Landroid/app/ITransientNotification;I)V
 Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager;
 Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/Instrumentation;->execStartActivities(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Landroid/app/Activity;[Landroid/content/Intent;Landroid/os/Bundle;)V
@@ -256,9 +284,12 @@
 Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;)Landroid/app/Instrumentation$ActivityResult;
 Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/Instrumentation$ActivityResult;
 Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler;
+Landroid/app/IProcessObserver$Stub;-><init>()V
 Landroid/app/ISearchManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/ISearchManager;
 Landroid/app/ISearchManager$Stub$Proxy;->getGlobalSearchActivity()Landroid/content/ComponentName;
 Landroid/app/ISearchManager$Stub$Proxy;->getWebSearchActivity()Landroid/content/ComponentName;
+Landroid/app/IServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IServiceConnection;
+Landroid/app/IStopUserCallback;->userStopped(I)V
 Landroid/app/IUiModeManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor;
 Landroid/app/job/IJobScheduler$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/job/IJobScheduler;
@@ -285,16 +316,21 @@
 Landroid/app/LoadedApk;->rewriteRValues(Ljava/lang/ClassLoader;Ljava/lang/String;I)V
 Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map;
 Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList;
+Landroid/app/LocalActivityManager;->mParent:Landroid/app/Activity;
+Landroid/app/LocalActivityManager;->mResumed:Landroid/app/LocalActivityManager$LocalActivityRecord;
+Landroid/app/LocalActivityManager;->mSingleMode:Z
 Landroid/app/NativeActivity;->hideIme(I)V
 Landroid/app/NativeActivity;->setWindowFlags(II)V
 Landroid/app/NativeActivity;->setWindowFormat(I)V
 Landroid/app/NativeActivity;->showIme(I)V
 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;->isGroupSummary()Z
 Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager;
 Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V
 Landroid/app/NotificationManager;->sService:Landroid/app/INotificationManager;
+Landroid/app/Notification;->mChannelId:Ljava/lang/String;
 Landroid/app/Notification;->mGroupKey:Ljava/lang/String;
 Landroid/app/Notification;->mLargeIcon:Landroid/graphics/drawable/Icon;
 Landroid/app/Notification;->mSmallIcon:Landroid/graphics/drawable/Icon;
@@ -307,6 +343,7 @@
 Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
 Landroid/app/QueuedWork;->addFinisher(Ljava/lang/Runnable;)V
 Landroid/app/QueuedWork;->removeFinisher(Ljava/lang/Runnable;)V
+Landroid/app/QueuedWork;->sFinishers:Ljava/util/LinkedList;
 Landroid/app/ResourcesManager;->appendLibAssetForMainAssetPath(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/app/ResourcesManager;->getInstance()Landroid/app/ResourcesManager;
 Landroid/app/ResourcesManager;->mActivityResourceReferences:Ljava/util/WeakHashMap;
@@ -320,7 +357,9 @@
 Landroid/app/Service;->mThread:Landroid/app/ActivityThread;
 Landroid/app/Service;->mToken:Landroid/os/IBinder;
 Landroid/app/Service;->setForeground(Z)V
+Landroid/app/SharedPreferencesImpl;-><init>(Ljava/io/File;I)V
 Landroid/app/SharedPreferencesImpl;->mFile:Ljava/io/File;
+Landroid/app/SharedPreferencesImpl;->startReloadIfChangedUnexpectedly()V
 Landroid/app/StatusBarManager;->collapsePanels()V
 Landroid/app/StatusBarManager;->disable(I)V
 Landroid/app/StatusBarManager;->expandNotificationsPanel()V
@@ -369,6 +408,7 @@
 Landroid/bluetooth/BluetoothGatt;->mAuthRetryState:I
 Landroid/bluetooth/BluetoothGatt;->refresh()Z
 Landroid/bluetooth/BluetoothHeadset;->close()V
+Landroid/bluetooth/BluetoothMapClient;->sendMessage(Landroid/bluetooth/BluetoothDevice;[Landroid/net/Uri;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)Z
 Landroid/bluetooth/BluetoothPan;->isTetheringOn()Z
 Landroid/bluetooth/BluetoothPan;->setBluetoothTethering(Z)V
 Landroid/bluetooth/BluetoothUuid;->RESERVED_UUIDS:[Landroid/os/ParcelUuid;
@@ -426,6 +466,7 @@
 Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String;
 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/pm/ActivityInfo;->resizeMode:I
 Landroid/content/pm/ApplicationInfo;->enabledSetting:I
@@ -436,18 +477,33 @@
 Landroid/content/pm/ApplicationInfo;->privateFlags:I
 Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String;
 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/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
+Landroid/content/pm/IPackageManager;->addPermission(Landroid/content/pm/PermissionInfo;)Z
+Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I
+Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
+Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String;
 Landroid/content/pm/IPackageManager;->getInstallLocation()I
 Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo;
+Landroid/content/pm/IPackageManager;->getProviderInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ProviderInfo;
+Landroid/content/pm/IPackageManager;->getReceiverInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo;
+Landroid/content/pm/IPackageManager;->getServiceInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ServiceInfo;
 Landroid/content/pm/IPackageManager;->setApplicationEnabledSetting(Ljava/lang/String;IIILjava/lang/String;)V
 Landroid/content/pm/IPackageManager;->setComponentEnabledSetting(Landroid/content/ComponentName;III)V
+Landroid/content/pm/IPackageManager;->setInstallerPackageName(Ljava/lang/String;Ljava/lang/String;)V
 Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V
 Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager;
+Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String;
 Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String;
+Landroid/content/pm/IPackageMoveObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageMoveObserver;
+Landroid/content/pm/IPackageMoveObserver$Stub;-><init>()V
 Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
+Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 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
@@ -472,14 +528,29 @@
 Landroid/content/pm/PackageParser$Component;->className:Ljava/lang/String;
 Landroid/content/pm/PackageParser$Component;->getComponentName()Landroid/content/ComponentName;
 Landroid/content/pm/PackageParser$Component;->intents:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo;
 Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo;
 Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo;
+Landroid/content/pm/PackageParser;->generateProviderInfo(Landroid/content/pm/PackageParser$Provider;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ProviderInfo;
+Landroid/content/pm/PackageParser;->generateServiceInfo(Landroid/content/pm/PackageParser$Service;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ServiceInfo;
 Landroid/content/pm/PackageParser;-><init>()V
+Landroid/content/pm/PackageParser$Instrumentation;->info:Landroid/content/pm/InstrumentationInfo;
+Landroid/content/pm/PackageParser$IntentInfo;->banner:I
+Landroid/content/pm/PackageParser$IntentInfo;->hasDefault:Z
+Landroid/content/pm/PackageParser$IntentInfo;->icon:I
+Landroid/content/pm/PackageParser$IntentInfo;-><init>()V
+Landroid/content/pm/PackageParser$IntentInfo;->labelRes:I
+Landroid/content/pm/PackageParser$IntentInfo;->logo:I
+Landroid/content/pm/PackageParser$IntentInfo;->nonLocalizedLabel:Ljava/lang/CharSequence;
 Landroid/content/pm/PackageParser$Package;->activities:Ljava/util/ArrayList;
 Landroid/content/pm/PackageParser$Package;->applicationInfo:Landroid/content/pm/ApplicationInfo;
+Landroid/content/pm/PackageParser$Package;->instrumentation:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser$Package;->mAppMetaData:Landroid/os/Bundle;
 Landroid/content/pm/PackageParser$Package;->mVersionCode:I
 Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String;
 Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String;
+Landroid/content/pm/PackageParser$Package;->permissionGroups:Ljava/util/ArrayList;
+Landroid/content/pm/PackageParser$Package;->permissions:Ljava/util/ArrayList;
 Landroid/content/pm/PackageParser$Package;->providers:Ljava/util/ArrayList;
 Landroid/content/pm/PackageParser$Package;->receivers:Ljava/util/ArrayList;
 Landroid/content/pm/PackageParser$Package;->requestedPermissions:Ljava/util/ArrayList;
@@ -490,6 +561,7 @@
 Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package;
 Landroid/content/pm/PackageParser$Provider;->info:Landroid/content/pm/ProviderInfo;
 Landroid/content/pm/PackageParser$ProviderIntentInfo;->provider:Landroid/content/pm/PackageParser$Provider;
+Landroid/content/pm/PackageParser$Service;->info:Landroid/content/pm/ServiceInfo;
 Landroid/content/pm/PackageParser$ServiceIntentInfo;->service:Landroid/content/pm/PackageParser$Service;
 Landroid/content/pm/PackageUserState;-><init>()V
 Landroid/content/pm/ParceledListSlice;-><init>(Ljava/util/List;)V
@@ -528,14 +600,18 @@
 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;
+Landroid/content/res/DrawableCache;-><init>()V
 Landroid/content/res/ObbInfo;->salt:[B
 Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo;
+Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
 Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
+Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
 Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
 Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache;
 Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration;
 Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache;
 Landroid/content/res/ResourcesImpl;->mPreloading:Z
+Landroid/content/res/ResourcesImpl;->mStateListAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
 Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray;
 Landroid/content/res/ResourcesImpl;->sPreloadedComplexColors:Landroid/util/LongSparseArray;
 Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray;
@@ -551,6 +627,7 @@
 Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
 Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
 Landroid/content/res/StringBlock;-><init>(JZ)V
+Landroid/content/res/ThemedResourceCache;->onConfigurationChange(I)V
 Landroid/content/res/TypedArray;->extractThemeAttrs()[I
 Landroid/content/res/TypedArray;->getNonConfigurationString(II)Ljava/lang/String;
 Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z
@@ -569,7 +646,10 @@
 Landroid/content/res/XmlBlock$Parser;->mBlock:Landroid/content/res/XmlBlock;
 Landroid/content/res/XmlBlock$Parser;->mParseState:J
 Landroid/content/SearchRecentSuggestionsProvider;->mSuggestionProjection:[Ljava/lang/String;
+Landroid/content/SyncContext;->setStatusText(Ljava/lang/String;)V
 Landroid/content/SyncStatusInfo;->lastSuccessTime:J
+Landroid/content/UriMatcher;->mChildren:Ljava/util/ArrayList;
+Landroid/content/UriMatcher;->mText:Ljava/lang/String;
 Landroid/database/AbstractCursor;->mExtras:Landroid/os/Bundle;
 Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri;
 Landroid/database/AbstractCursor;->mRowIdColumnIndex:I
@@ -588,6 +668,7 @@
 Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I
 Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String;
 Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String;
+Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode;
 Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J
 Landroid/graphics/Bitmap$Config;->nativeInt:I
@@ -608,6 +689,11 @@
 Landroid/graphics/Camera;->native_instance:J
 Landroid/graphics/Canvas;-><init>(J)V
 Landroid/graphics/Canvas;->release()V
+Landroid/graphics/Canvas;->save(I)I
+Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I
+Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I
+Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I
+Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I
 Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V
 Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V
 Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray;
@@ -620,6 +706,7 @@
 Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode;
 Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I
 Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V
+Landroid/graphics/drawable/ColorDrawable$ColorState;->mUseColor:I
 Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect;
 Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets;
 Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;
@@ -763,6 +850,7 @@
 Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
 Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay;
 Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay;
+Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/hardware/HardwareBuffer;-><init>(J)V
 Landroid/hardware/HardwareBuffer;->mNativeObject:J
 Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager;
@@ -822,57 +910,28 @@
 Landroid/hardware/usb/UsbRequest;->mLength:I
 Landroid/hardware/usb/UsbRequest;->mNativeContext:J
 Landroid/icu/impl/CurrencyData;-><init>()V
-Landroid/icu/impl/number/DecimalFormatProperties;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/impl/number/DecimalFormatProperties;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/impl/TimeZoneGenericNames;->readObject(Ljava/io/ObjectInputStream;)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/DateFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/DateFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateIntervalFormat;-><init>()V
-Landroid/icu/text/DateIntervalFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DateTimePatternGenerator$DistanceInfo;-><init>()V
-Landroid/icu/text/DecimalFormat_ICU58_Android;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/DecimalFormat_ICU58_Android;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/DecimalFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/icu/text/DecimalFormatSymbols;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/DecimalFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/DecimalFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/MessageFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/MessageFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/NumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/NumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/PluralFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules$FixedDecimal;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules$FixedDecimal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/PluralRules;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/PluralRules;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/RuleBasedCollator;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
-Landroid/icu/text/RuleBasedNumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/RuleBasedNumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/text/SelectFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/SimpleDateFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/SimpleDateFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/SpoofChecker$ScriptSet;->and(I)V
 Landroid/icu/text/SpoofChecker$ScriptSet;-><init>()V
 Landroid/icu/text/SpoofChecker$ScriptSet;->isFull()Z
 Landroid/icu/text/SpoofChecker$ScriptSet;->setAll()V
-Landroid/icu/text/TimeZoneFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/text/TimeZoneFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
 Landroid/icu/text/TimeZoneNames$DefaultTimeZoneNames$FactoryImpl;-><init>()V
 Landroid/icu/text/Transliterator;->createFromRules(Ljava/lang/String;Ljava/lang/String;I)Landroid/icu/text/Transliterator;
 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/Calendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/Calendar;->writeObject(Ljava/io/ObjectOutputStream;)V
-Landroid/icu/util/ChineseCalendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/IslamicCalendar;->readObject(Ljava/io/ObjectInputStream;)V
-Landroid/icu/util/SimpleTimeZone;->readObject(Ljava/io/ObjectInputStream;)V
 Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText;
+Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View;
+Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver;
+Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z
 Landroid/location/CountryDetector;->detectCountry()Landroid/location/Country;
 Landroid/location/Country;->getCountryIso()Ljava/lang/String;
 Landroid/location/Country;->getSource()I
@@ -969,11 +1028,15 @@
 Landroid/media/AudioTrack;->mStreamType:I
 Landroid/media/AudioTrack;->native_release()V
 Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V
+Landroid/media/ExifInterface;->getDateTime()J
 Landroid/media/IAudioService;->getStreamMaxVolume(I)I
 Landroid/media/IAudioService;->getStreamVolume(I)I
 Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V
 Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService;
 Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Landroid/media/IMediaScannerService;->scanFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService;
+Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V
 Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController;
 Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J
 Landroid/media/JetPlayer;->postEventFromNative(Ljava/lang/Object;III)V
@@ -1026,6 +1089,8 @@
 Landroid/media/RemoteDisplay;->notifyDisplayConnected(Landroid/view/Surface;IIII)V
 Landroid/media/RemoteDisplay;->notifyDisplayDisconnected()V
 Landroid/media/RemoteDisplay;->notifyDisplayError(I)V
+Landroid/media/RemoteDisplayState;->displays:Ljava/util/ArrayList;
+Landroid/media/RemoteDisplayState;-><init>()V
 Landroid/media/RingtoneManager;->getRingtone(Landroid/content/Context;Landroid/net/Uri;I)Landroid/media/Ringtone;
 Landroid/media/session/MediaSessionLegacyHelper;->getHelper(Landroid/content/Context;)Landroid/media/session/MediaSessionLegacyHelper;
 Landroid/media/session/MediaSession;->mCallback:Landroid/media/session/MediaSession$CallbackMessageHandler;
@@ -1037,6 +1102,11 @@
 Landroid/media/soundtrigger/SoundTriggerManager;->stopRecognition(Ljava/util/UUID;)I
 Landroid/media/soundtrigger/SoundTriggerManager;->unloadSoundModel(Ljava/util/UUID;)I
 Landroid/media/SubtitleController;->mHandler:Landroid/os/Handler;
+Landroid/media/SubtitleTrack$RenderingWidget;->draw(Landroid/graphics/Canvas;)V
+Landroid/media/SubtitleTrack$RenderingWidget;->onAttachedToWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->onDetachedFromWindow()V
+Landroid/media/SubtitleTrack$RenderingWidget;->setOnChangedListener(Landroid/media/SubtitleTrack$RenderingWidget$OnChangedListener;)V
+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/VolumeShaper$Configuration;-><init>(IIIDI[F[F)V
@@ -1076,11 +1146,16 @@
 Landroid/net/ConnectivityManager;->TYPE_PROXY:I
 Landroid/net/ConnectivityManager;->TYPE_WIFI_P2P:I
 Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkInfo()[Landroid/net/NetworkInfo;
 Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworks()[Landroid/net/Network;
 Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String;
+Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String;
 Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
+Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/IpConfiguration;->httpProxy:Landroid/net/ProxyInfo;
 Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V
 Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor;
@@ -1104,6 +1179,7 @@
 Landroid/net/NetworkStats;->txPackets:[J
 Landroid/net/NetworkStats;->uid:[I
 Landroid/net/NetworkTemplate;->buildTemplateWifi()Landroid/net/NetworkTemplate;
+Landroid/net/Proxy;->getProxy(Landroid/content/Context;Ljava/lang/String;)Ljava/net/Proxy;
 Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;)V
 Landroid/net/SntpClient;-><init>()V
 Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl;
@@ -1130,6 +1206,7 @@
 Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String;
 Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V
 Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache;
+Landroid/net/TrafficStats;->getMobileIfaces()[Ljava/lang/String;
 Landroid/net/TrafficStats;->getRxBytes(Ljava/lang/String;)J
 Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService;
 Landroid/net/TrafficStats;->getTxBytes(Ljava/lang/String;)J
@@ -1138,9 +1215,12 @@
 Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I
 Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel;
+Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I
 Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V
 Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
+Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V
 Landroid/net/wifi/ScanResult;->anqpDomainId:I
 Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List;
 Landroid/net/wifi/ScanResult;->distanceCm:I
@@ -1170,10 +1250,12 @@
 Landroid/net/wifi/WifiConfiguration;->apBand:I
 Landroid/net/wifi/WifiConfiguration;->apChannel:I
 Landroid/net/wifi/WifiConfiguration;->defaultGwMacAddress:Ljava/lang/String;
+Landroid/net/wifi/WifiConfiguration;->lastConnectUid:I
 Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration;
 Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z
 Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String;
 Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String;
+Landroid/net/wifi/WifiInfo;->DEFAULT_MAC_ADDRESS:Ljava/lang/String;
 Landroid/net/wifi/WifiInfo;->getMeteredHint()Z
 Landroid/net/wifi/WifiInfo;->mMacAddress:Ljava/lang/String;
 Landroid/net/wifi/WifiInfo;->removeDoubleQuotes(Ljava/lang/String;)Ljava/lang/String;
@@ -1195,17 +1277,30 @@
 Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable;
 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;->getUidStats()Landroid/util/SparseArray;
 Landroid/os/BatteryStats$HistoryItem;->states2:I
 Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I
 Landroid/os/BatteryStats;->startIteratingHistoryLocked()Z
 Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J
+Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J
+Landroid/os/BatteryStats$Uid;->getPackageStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap;
 Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray;
 Landroid/os/BatteryStats$Uid;->getUid()I
+Landroid/os/BatteryStats$Uid;->getVideoTurnedOnTimer()Landroid/os/BatteryStats$Timer;
 Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J
 Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J
+Landroid/os/BatteryStats$Uid$Pkg;->getServiceStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid$Pkg;->getWakeupAlarmStats()Landroid/util/ArrayMap;
+Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I
+Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStartTime(JI)J
+Landroid/os/BatteryStats$Uid$Proc;->countExcessivePowers()I
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->overTime:J
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->type:I
+Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->usedTime:J
+Landroid/os/BatteryStats$Uid$Proc;->getExcessivePower(I)Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;
 Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J
 Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J
 Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J
@@ -1252,6 +1347,7 @@
 Landroid/os/Debug$MemoryInfo;->otherSwappedOutPss:I
 Landroid/os/Environment;->buildExternalStorageAppDataDirs(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
 Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J
 Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z
@@ -1263,6 +1359,7 @@
 Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I
 Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V
 Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V
+Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z
 Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger;
 Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z
 Landroid/os/Handler;-><init>(Z)V
@@ -1277,6 +1374,7 @@
 Landroid/os/IPowerManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z
 Landroid/os/IPowerManager;->userActivity(JII)V
+Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder;
 Landroid/os/IUserManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue;
@@ -1320,6 +1418,7 @@
 Landroid/os/Process;->isIsolated(I)Z
 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/SELinux;->isSELinuxEnabled()Z
 Landroid/os/SELinux;->isSELinuxEnforced()Z
 Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V
@@ -1481,13 +1580,16 @@
 Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName;
 Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle;
 Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo;
+Landroid/provider/Browser$BookmarkColumns;->DATE:Ljava/lang/String;
 Landroid/provider/Browser;->BOOKMARKS_URI:Landroid/net/Uri;
 Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z
 Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V
 Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V
 Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V
 Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String;
+Landroid/provider/Browser;->SEARCHES_URI:Landroid/net/Uri;
 Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V
+Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V
 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/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider;
@@ -1715,6 +1817,7 @@
 Landroid/R$styleable;->Window:[I
 Landroid/R$styleable;->Window_windowBackground:I
 Landroid/R$styleable;->Window_windowFrame:I
+Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J
 Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore;
 Landroid/security/keystore/KeychainProtectionParams;->clearSecret()V
 Landroid/security/keystore/KeychainProtectionParams;->getKeyDerivationParams()Landroid/security/keystore/KeyDerivationParams;
@@ -1863,6 +1966,7 @@
 Landroid/telephony/SubscriptionManager;->getActiveSubscriptionIdList()[I
 Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoCount()I
 Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoList()Ljava/util/List;
+Landroid/telephony/SubscriptionManager;->getDefaultDataPhoneId()I
 Landroid/telephony/SubscriptionManager;->getDefaultDataSubscriptionInfo()Landroid/telephony/SubscriptionInfo;
 Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I
 Landroid/telephony/SubscriptionManager;->getDefaultVoiceSubscriptionInfo()Landroid/telephony/SubscriptionInfo;
@@ -1902,6 +2006,7 @@
 Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
 Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z
 Landroid/telephony/TelephonyManager;->isVolteAvailable()Z
+Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
 Landroid/text/AndroidBidi;->bidi(I[C[B)I
 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;
@@ -1958,6 +2063,7 @@
 Landroid/text/StaticLayout;->mColumns:I
 Landroid/text/StaticLayout;->mLineCount:I
 Landroid/text/StaticLayout;->mLines:[I
+Landroid/text/StaticLayout;->mMaximumVisibleLineCount:I
 Landroid/text/TextLine;->mCharacterStyleSpanSet:Landroid/text/SpanSet;
 Landroid/text/TextLine;->mMetricAffectingSpanSpanSet:Landroid/text/SpanSet;
 Landroid/text/TextLine;->mReplacementSpanSpanSet:Landroid/text/SpanSet;
@@ -1989,14 +2095,17 @@
 Landroid/util/Pools$SynchronizedPool;-><init>(I)V
 Landroid/util/Rational;->mDenominator:I
 Landroid/util/Rational;->mNumerator:I
-Landroid/util/Rational;->readObject(Ljava/io/ObjectInputStream;)V
+Landroid/util/Singleton;->get()Ljava/lang/Object;
+Landroid/util/Singleton;-><init>()V
 Landroid/util/Singleton;->mInstance:Ljava/lang/Object;
 Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/util/Slog;->w(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/util/SparseIntArray;->mKeys:[I
 Landroid/util/SparseIntArray;->mSize:I
 Landroid/util/SparseIntArray;->mValues:[I
 Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager;
 Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z
+Landroid/view/accessibility/AccessibilityManager;->mAccessibilityStateChangeListeners:Landroid/util/ArrayMap;
 Landroid/view/accessibility/AccessibilityManager;->mIsEnabled:Z
 Landroid/view/accessibility/AccessibilityManager;->mIsHighTextContrastEnabled:Z
 Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager;
@@ -2046,6 +2155,7 @@
 Landroid/view/inputmethod/InputMethodInfo;->mSubtypes:Landroid/view/inputmethod/InputMethodSubtypeArray;
 Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
 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;->mCurId:Ljava/lang/String;
 Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;
@@ -2054,6 +2164,7 @@
 Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View;
 Landroid/view/inputmethod/InputMethodManager;->mService:Lcom/android/internal/view/IInputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V
+Landroid/view/inputmethod/InputMethodManager;->peekInstance()Landroid/view/inputmethod/InputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V
 Landroid/view/inputmethod/InputMethodManager;->sInstance:Landroid/view/inputmethod/InputMethodManager;
 Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V
@@ -2067,6 +2178,7 @@
 Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V
 Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager;
 Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I
+Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I
 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
@@ -2097,6 +2209,7 @@
 Landroid/view/LayoutInflater;->mPrivateFactory:Landroid/view/LayoutInflater$Factory2;
 Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap;
 Landroid/view/LayoutInflater;->setPrivateFactory(Landroid/view/LayoutInflater$Factory2;)V
+Landroid/view/MotionEvent;->getPointerIdBits()I
 Landroid/view/MotionEvent;->HISTORY_CURRENT:I
 Landroid/view/MotionEvent;->mNativePtr:J
 Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F
@@ -2104,6 +2217,7 @@
 Landroid/view/MotionEvent$PointerCoords;->mPackedAxisBits:J
 Landroid/view/MotionEvent$PointerCoords;->mPackedAxisValues:[F
 Landroid/view/MotionEvent;->scale(F)V
+Landroid/view/MotionEvent;->split(I)Landroid/view/MotionEvent;
 Landroid/view/PointerIcon;->load(Landroid/content/Context;)Landroid/view/PointerIcon;
 Landroid/view/PointerIcon;->mBitmapFrames:[Landroid/graphics/Bitmap;
 Landroid/view/PointerIcon;->mBitmap:Landroid/graphics/Bitmap;
@@ -2124,6 +2238,8 @@
 Landroid/view/RemoteAnimationTarget;->taskId:I
 Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration;
 Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V
+Landroid/view/RenderNode;->discardDisplayList()V
+Landroid/view/RenderNode;->output()V
 Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener;
 Landroid/view/ScaleGestureDetector;->mMinSpan:I
 Landroid/view/SurfaceControl$PhysicalDisplayInfo;->appVsyncOffsetNanos:J
@@ -2158,6 +2274,7 @@
 Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier;
 Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z
 Landroid/view/TextureView;->destroyHardwareLayer()V
+Landroid/view/TextureView;->destroyHardwareResources()V
 Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer;
 Landroid/view/TextureView;->mNativeWindow:J
 Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture;
@@ -2169,6 +2286,7 @@
 Landroid/view/VelocityTracker$Estimator;->yCoeff:[F
 Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker;
 Landroid/view/View;->applyDrawableToTransparentRegion(Landroid/graphics/drawable/Drawable;Landroid/graphics/Region;)V
+Landroid/view/View$AttachInfo;->mContentInsets:Landroid/graphics/Rect;
 Landroid/view/View$AttachInfo;->mDrawingTime:J
 Landroid/view/View$AttachInfo;->mStableInsets:Landroid/graphics/Rect;
 Landroid/view/View;->clearAccessibilityFocus()V
@@ -2187,12 +2305,14 @@
 Landroid/view/View;->fitsSystemWindows()Z
 Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate;
 Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V
+Landroid/view/View;->getHorizontalScrollFactor()F
 Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix;
 Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo;
 Landroid/view/View;->getLocationOnScreen()[I
 Landroid/view/View;->getRawTextAlignment()I
 Landroid/view/View;->getRawTextDirection()I
 Landroid/view/View;->getTransitionAlpha()F
+Landroid/view/View;->getVerticalScrollFactor()F
 Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl;
 Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V
 Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V
@@ -2207,18 +2327,23 @@
 Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget;
 Landroid/view/ViewGroup;->mGroupFlags:I
 Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener;
+Landroid/view/ViewGroup;->mPersistentDrawingCache:I
+Landroid/view/ViewGroup;->offsetChildrenTopAndBottom(I)V
 Landroid/view/ViewGroup;->resetResolvedDrawables()V
 Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V
 Landroid/view/ViewGroup;->resetResolvedPadding()V
 Landroid/view/ViewGroup;->resetResolvedTextAlignment()V
 Landroid/view/ViewGroup;->resetResolvedTextDirection()V
 Landroid/view/ViewGroup;->suppressLayout(Z)V
+Landroid/view/View;->includeForAccessibility()Z
 Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
 Landroid/view/View;->internalSetPadding(IIII)V
 Landroid/view/View;->isPaddingResolved()Z
 Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z
 Landroid/view/View;->isVisibleToUser()Z
+Landroid/view/View$ListenerInfo;-><init>()V
 Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener;
+Landroid/view/View$ListenerInfo;->mOnFocusChangeListener:Landroid/view/View$OnFocusChangeListener;
 Landroid/view/View$ListenerInfo;->mOnLongClickListener:Landroid/view/View$OnLongClickListener;
 Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener;
 Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate;
@@ -2231,10 +2356,13 @@
 Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo;
 Landroid/view/View;->mMinHeight:I
 Landroid/view/View;->mMinWidth:I
+Landroid/view/View;->mPaddingBottom:I
 Landroid/view/View;->mPaddingLeft:I
 Landroid/view/View;->mPaddingRight:I
+Landroid/view/View;->mPaddingTop:I
 Landroid/view/View;->mParent:Landroid/view/ViewParent;
 Landroid/view/View;->mPrivateFlags3:I
+Landroid/view/View;->mPrivateFlags:I
 Landroid/view/View;->mRecreateDisplayList:Z
 Landroid/view/View;->mResources:Landroid/content/res/Resources;
 Landroid/view/View;->mRight:I
@@ -2247,6 +2375,8 @@
 Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap;
 Landroid/view/View;->mViewFlags:I
 Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V
+Landroid/view/View;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V
+Landroid/view/View;->performAccessibilityActionInternal(ILandroid/os/Bundle;)Z
 Landroid/view/View;->recomputePadding()V
 Landroid/view/View;->requestAccessibilityFocus()Z
 Landroid/view/View;->resetDisplayList()V
@@ -2307,10 +2437,14 @@
 Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J
 Landroid/view/Window;->mAppName:Ljava/lang/String;
 Landroid/view/Window;->mAppToken:Landroid/os/IBinder;
+Landroid/view/Window;->mCallback:Landroid/view/Window$Callback;
+Landroid/view/Window;->mContext:Landroid/content/Context;
 Landroid/view/Window;->mHardwareAccelerated:Z
+Landroid/view/Window;->mWindowStyle:Landroid/content/res/TypedArray;
 Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Landroid/webkit/WebResourceResponse;->mImmutable:Z
 Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler;
+Landroid/webkit/WebViewClient;->onUnhandledInputEvent(Landroid/webkit/WebView;Landroid/view/InputEvent;)V
 Landroid/webkit/WebView;->debugDump()V
 Landroid/webkit/WebView;->disablePlatformNotifications()V
 Landroid/webkit/WebView;->emulateShiftHeld()V
@@ -2319,6 +2453,7 @@
 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;->getTouchIconUrl()Ljava/lang/String;
 Landroid/webkit/WebView;->getVisibleTitleHeight()I
 Landroid/webkit/WebView;->isPaused()Z
 Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider;
@@ -2340,6 +2475,8 @@
 Landroid/widget/AbsListView;->mMaximumVelocity:I
 Landroid/widget/AbsListView;->mMotionPosition:I
 Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener;
+Landroid/widget/AbsListView;->mPendingCheckForLongPress:Landroid/widget/AbsListView$CheckForLongPress;
+Landroid/widget/AbsListView;->mPendingCheckForTap:Landroid/widget/AbsListView$CheckForTap;
 Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin;
 Landroid/widget/AbsListView;->mSelectionTopPadding:I
 Landroid/widget/AbsListView;->mSelectorPosition:I
@@ -2352,6 +2489,8 @@
 Landroid/widget/AbsListView$RecycleBin;->clear()V
 Landroid/widget/AbsListView$RecycleBin;->mRecyclerListener:Landroid/widget/AbsListView$RecyclerListener;
 Landroid/widget/AbsListView;->reportScrollStateChange(I)V
+Landroid/widget/AbsListView$SavedState;->firstId:J
+Landroid/widget/AbsListView$SavedState;->viewTop:I
 Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V
 Landroid/widget/AbsListView;->trackMotionScroll(II)Z
 Landroid/widget/AbsSeekBar;->mIsDragging:Z
@@ -2370,7 +2509,9 @@
 Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V
 Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow;
 Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V
+Landroid/widget/AutoCompleteTextView;->setForceIgnoreOutsideTouch(Z)V
 Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/CompoundButton;->mOnCheckedChangeListener:Landroid/widget/CompoundButton$OnCheckedChangeListener;
 Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver;
 Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver;
 Landroid/widget/CursorAdapter;->mDataValid:Z
@@ -2381,6 +2522,7 @@
 Landroid/widget/Editor;->mShowCursor:J
 Landroid/widget/Editor;->mShowSoftInputOnFocus:Z
 Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable;
+Landroid/widget/ExpandableListView;->mGroupIndicator:Landroid/graphics/drawable/Drawable;
 Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect;
 Landroid/widget/FastScroller;->mHeaderCount:I
 Landroid/widget/FastScroller;->mLongList:Z
@@ -2487,13 +2629,17 @@
 Landroid/widget/RemoteViews;->mergeRemoteViews(Landroid/widget/RemoteViews;)V
 Landroid/widget/RemoteViews;->mPortrait:Landroid/widget/RemoteViews;
 Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String;
+Landroid/widget/RemoteViews$ReflectionAction;->value:Ljava/lang/Object;
+Landroid/widget/RemoteViews$SetOnClickPendingIntent;->pendingIntent:Landroid/app/PendingIntent;
 Landroid/widget/ScrollBarDrawable;->mVerticalThumb:Landroid/graphics/drawable/Drawable;
 Landroid/widget/ScrollBarDrawable;->setHorizontalThumbDrawable(Landroid/graphics/drawable/Drawable;)V
 Landroid/widget/ScrollBarDrawable;->setVerticalThumbDrawable(Landroid/graphics/drawable/Drawable;)V
+Landroid/widget/Scroller;->mInterpolator:Landroid/view/animation/Interpolator;
 Landroid/widget/ScrollView;->mChildToScrollTo:Landroid/view/View;
 Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect;
 Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect;
 Landroid/widget/ScrollView;->mIsBeingDragged:Z
+Landroid/widget/ScrollView;->mMinimumVelocity:I
 Landroid/widget/ScrollView;->mOverflingDistance:I
 Landroid/widget/ScrollView;->mOverscrollDistance:I
 Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller;
@@ -2507,7 +2653,11 @@
 Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup;
 Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable;
 Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable;
+Landroid/widget/TabHost$IntentContentStrategy;->getContentView()Landroid/view/View;
+Landroid/widget/TabHost$IntentContentStrategy;->tabClosed()V
+Landroid/widget/TabHost;->mTabSpecs:Ljava/util/List;
 Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$ContentStrategy;
+Landroid/widget/TabWidget;->mSelectedTab:I
 Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V
 Landroid/widget/TextView;->assumeLayout()V
 Landroid/widget/TextView;->createEditorIfNeeded()V
@@ -2525,7 +2675,11 @@
 Landroid/widget/TextView;->mTextPaint:Landroid/text/TextPaint;
 Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V
 Landroid/widget/Toast;->getService()Landroid/app/INotificationManager;
+Landroid/widget/Toast;->getWindowParams()Landroid/view/WindowManager$LayoutParams;
+Landroid/widget/Toast;->mTN:Landroid/widget/Toast$TN;
 Landroid/widget/Toast;->sService:Landroid/app/INotificationManager;
+Landroid/widget/Toast$TN;->mNextView:Landroid/view/View;
+Landroid/widget/Toast$TN;->mParams:Landroid/view/WindowManager$LayoutParams;
 Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController;
 Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V
 Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V
@@ -2608,6 +2762,7 @@
 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
 Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->setMode(IILjava/lang/String;I)V
 Lcom/android/internal/app/IBatteryStats;->getStatistics()[B
 Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats;
 Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -2635,10 +2790,12 @@
 Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/os/BatteryStatsImpl;->getBatteryRealtime(J)J
 Lcom/android/internal/os/BatteryStatsImpl;->getDischargeAmount(I)I
 Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J
 Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray;
+Lcom/android/internal/os/BatteryStatsImpl;->getUidStatsLocked(I)Lcom/android/internal/os/BatteryStatsImpl$Uid;
 Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I
 Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J
 Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap;
@@ -2706,6 +2863,18 @@
 Lcom/android/internal/R$string;->megabyteShort:I
 Lcom/android/internal/R$string;->petabyteShort:I
 Lcom/android/internal/R$string;->terabyteShort:I
+Lcom/android/internal/R$styleable;->AbsListView_cacheColorHint:I
+Lcom/android/internal/R$styleable;->AbsListView_choiceMode:I
+Lcom/android/internal/R$styleable;->AbsListView_drawSelectorOnTop:I
+Lcom/android/internal/R$styleable;->AbsListView_fastScrollAlwaysVisible:I
+Lcom/android/internal/R$styleable;->AbsListView_fastScrollEnabled:I
+Lcom/android/internal/R$styleable;->AbsListView:[I
+Lcom/android/internal/R$styleable;->AbsListView_listSelector:I
+Lcom/android/internal/R$styleable;->AbsListView_scrollingCache:I
+Lcom/android/internal/R$styleable;->AbsListView_smoothScrollbar:I
+Lcom/android/internal/R$styleable;->AbsListView_stackFromBottom:I
+Lcom/android/internal/R$styleable;->AbsListView_textFilterEnabled:I
+Lcom/android/internal/R$styleable;->AbsListView_transcriptMode:I
 Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I
 Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I
 Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I
@@ -2713,6 +2882,28 @@
 Lcom/android/internal/R$styleable;->AccountAuthenticator_icon:I
 Lcom/android/internal/R$styleable;->AccountAuthenticator_label:I
 Lcom/android/internal/R$styleable;->AccountAuthenticator_smallIcon:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_allowTaskReparenting:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_configChanges:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_description:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_enabled:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_excludeFromRecents:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_exported:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_hardwareAccelerated:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity:[I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_icon:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_immersive:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_label:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_launchMode:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_logo:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_noHistory:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_permission:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_process:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_screenOrientation:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_taskAffinity:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_theme:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_uiOptions:I
+Lcom/android/internal/R$styleable;->AndroidManifestActivity_windowSoftInputMode:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_enabled:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_hardwareAccelerated:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication:[I
@@ -2724,8 +2915,21 @@
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_supportsRtl:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_theme:I
 Lcom/android/internal/R$styleable;->AndroidManifestApplication_uiOptions:I
+Lcom/android/internal/R$styleable;->AndroidManifestData:[I
 Lcom/android/internal/R$styleable;->AndroidManifest:[I
 Lcom/android/internal/R$styleable;->AndroidManifest_installLocation:I
+Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter:[I
+Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter_priority:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData:[I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_resource:I
+Lcom/android/internal/R$styleable;->AndroidManifestMetaData_value:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_enabled:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_exported:I
+Lcom/android/internal/R$styleable;->AndroidManifestService:[I
+Lcom/android/internal/R$styleable;->AndroidManifestService_name:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_permission:I
+Lcom/android/internal/R$styleable;->AndroidManifestService_process:I
 Lcom/android/internal/R$styleable;->AndroidManifest_sharedUserId:I
 Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission:[I
 Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission_name:I
@@ -2739,12 +2943,26 @@
 Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOff:I
 Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I
 Lcom/android/internal/R$styleable;->CompoundButton_button:I
+Lcom/android/internal/R$styleable;->CompoundButton_checked:I
 Lcom/android/internal/R$styleable;->CompoundButton:[I
+Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I
+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;->IconMenuView:[I
 Lcom/android/internal/R$styleable;->ImageView:[I
 Lcom/android/internal/R$styleable;->ImageView_src:I
+Lcom/android/internal/R$styleable;->ListPreference_entries:I
+Lcom/android/internal/R$styleable;->ListPreference:[I
+Lcom/android/internal/R$styleable;->ListView_dividerHeight:I
+Lcom/android/internal/R$styleable;->ListView_divider:I
+Lcom/android/internal/R$styleable;->ListView_entries:I
+Lcom/android/internal/R$styleable;->ListView_footerDividersEnabled:I
+Lcom/android/internal/R$styleable;->ListView_headerDividersEnabled:I
+Lcom/android/internal/R$styleable;->ListView:[I
+Lcom/android/internal/R$styleable;->ListView_overScrollFooter:I
+Lcom/android/internal/R$styleable;->ListView_overScrollHeader:I
+Lcom/android/internal/R$styleable;->PopupWindow:[I
 Lcom/android/internal/R$styleable;->Preference_defaultValue:I
 Lcom/android/internal/R$styleable;->Preference_dependency:I
 Lcom/android/internal/R$styleable;->Preference_enabled:I
@@ -2773,12 +2991,15 @@
 Lcom/android/internal/R$styleable;->SyncAdapter_supportsUploading:I
 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;->TextView_drawableBottom:I
 Lcom/android/internal/R$styleable;->TextView_drawableLeft:I
 Lcom/android/internal/R$styleable;->TextView_drawableRight:I
 Lcom/android/internal/R$styleable;->TextView_drawableTop:I
 Lcom/android/internal/R$styleable;->TextView:[I
 Lcom/android/internal/R$styleable;->TextView_maxLines:I
+Lcom/android/internal/R$styleable;->TextView_textColorHint:I
+Lcom/android/internal/R$styleable;->TextView_textColor:I
 Lcom/android/internal/R$styleable;->View_background:I
 Lcom/android/internal/R$styleable;->ViewGroup_Layout:[I
 Lcom/android/internal/R$styleable;->ViewGroup_Layout_layout_height:I
@@ -2799,23 +3020,35 @@
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
 Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V
 Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V
+Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V
 Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
 Lcom/android/internal/telephony/ITelephony;->endCall()Z
+Lcom/android/internal/telephony/ITelephony;->getCallState()I
+Lcom/android/internal/telephony/ITelephony;->getDataState()I
 Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
 Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
 Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
+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/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
+Lcom/android/internal/util/AsyncChannel;->sendMessage(III)V
 Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V
 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/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
 Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator;
+Lcom/android/internal/view/menu/MenuBuilder;-><init>(Landroid/content/Context;)V
 Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context;
 Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V
 Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V
@@ -2838,8 +3071,6 @@
 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;->readObject(Ljava/io/ObjectInputStream;)V
-Lcom/android/okhttp/okio/ByteString;->writeObject(Ljava/io/ObjectOutputStream;)V
 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;
@@ -2873,6 +3104,7 @@
 Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V
 Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V
 Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V
+Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket;
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B
 Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String;
@@ -2895,10 +3127,12 @@
 Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;
 Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy;
 Ldalvik/system/BlockGuard$Policy;->onNetwork()V
+Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V
 Ldalvik/system/CloseGuard;->close()V
 Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard;
 Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V
 Ldalvik/system/CloseGuard;->warnIfOpen()V
+Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String;
 Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String;
 Ldalvik/system/DexFile;->isBackedByOatFile()Z
 Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class;
@@ -2907,6 +3141,7 @@
 Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object;
 Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object;
 Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V
+Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader;
 Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element;
 Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile;
 Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V
@@ -2927,6 +3162,7 @@
 Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
 Ldalvik/system/VMRuntime;->clearGrowthLimit()V
 Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
+Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
 Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime;
 Ldalvik/system/VMRuntime;->is64Bit()Z
 Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object;
@@ -2942,26 +3178,24 @@
 Ldalvik/system/VMRuntime;->vmLibrary()Ljava/lang/String;
 Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader;
 Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class;
-Ljava/awt/font/NumericShaper;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/beans/PropertyChangeSupport;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/beans/PropertyChangeSupport;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/io/FileDescriptor;->descriptor:I
 Ljava/io/FileDescriptor;->getInt$()I
+Ljava/io/FileDescriptor;->isSocket$()Z
 Ljava/io/FileDescriptor;->setInt$(I)V
+Ljava/io/File;->fs:Ljava/io/FileSystem;
 Ljava/io/FileInputStream;->fd:Ljava/io/FileDescriptor;
 Ljava/io/FileOutputStream;->fd:Ljava/io/FileDescriptor;
-Ljava/io/File;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/io/File;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J
 Ljava/io/ObjectStreamClass;->newInstance(Ljava/lang/Class;J)Ljava/lang/Object;
 Ljava/io/ObjectStreamClass;->newInstance()Ljava/lang/Object;
-Ljava/io/UncheckedIOException;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/AbstractStringBuilder;->value:[C
 Ljava/lang/Boolean;->value:Z
 Ljava/lang/Byte;->value:B
 Ljava/lang/Character;->value:C
+Ljava/lang/Class;->accessFlags:I
 Ljava/lang/Class;->dexCache:Ljava/lang/Object;
 Ljava/lang/Class;->dexClassDefIndex:I
+Ljava/lang/Class;->ifTable:[Ljava/lang/Object;
 Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader;
 Ljava/lang/Daemons$Daemon;->isRunning()Z
 Ljava/lang/Daemons$Daemon;->start()V
@@ -2976,11 +3210,8 @@
 Ljava/lang/Daemons;->start()V
 Ljava/lang/Daemons;->stop()V
 Ljava/lang/Double;->value:D
-Ljava/lang/Enum;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/Float;->value:F
 Ljava/lang/Integer;->value:I
-Ljava/lang/invoke/MethodType;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/invoke/MethodType;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/Long;->value:J
 Ljava/lang/ref/FinalizerReference;->add(Ljava/lang/Object;)V
 Ljava/lang/ref/FinalizerReference;->head:Ljava/lang/ref/FinalizerReference;
@@ -2996,11 +3227,8 @@
 Ljava/lang/Runtime;->load(Ljava/lang/String;Ljava/lang/ClassLoader;)V
 Ljava/lang/Runtime;->nativeLoad(Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/String;
 Ljava/lang/Short;->value:S
-Ljava/lang/StringBuffer;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/StringBuffer;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/lang/StringBuilder;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/lang/StringBuilder;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/String;-><init>(II[C)V
+Ljava/lang/System;->arraycopy([II[III)V
 Ljava/lang/System;-><init>()V
 Ljava/lang/Thread;->daemon:Z
 Ljava/lang/Thread;->dispatchUncaughtException(Ljava/lang/Throwable;)V
@@ -3023,16 +3251,9 @@
 Ljava/lang/Throwable;->cause:Ljava/lang/Throwable;
 Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;
 Ljava/lang/Throwable;->nativeFillInStackTrace()Ljava/lang/Object;
-Ljava/lang/Throwable;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/lang/Throwable;->stackTrace:[Ljava/lang/StackTraceElement;
 Ljava/lang/Throwable;->suppressedExceptions:Ljava/util/List;
-Ljava/lang/Throwable;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/lang/Void;-><init>()V
-Ljava/math/BigDecimal;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/math/BigDecimal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/math/BigInteger;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/math/BigInteger;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/math/MathContext;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/net/Authenticator;->theAuthenticator:Ljava/net/Authenticator;
 Ljava/net/DatagramSocket;->impl:Ljava/net/DatagramSocketImpl;
 Ljava/net/HttpCookie;->httpOnly:Z
@@ -3044,29 +3265,22 @@
 Ljava/net/Inet6Address$Inet6AddressHolder;->scope_id_set:Z
 Ljava/net/Inet6Address$Inet6AddressHolder;->scope_ifname:Ljava/net/NetworkInterface;
 Ljava/net/Inet6Address;-><init>()V
-Ljava/net/Inet6Address;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/Inet6Address;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/InetAddress;->clearDnsCache()V
 Ljava/net/InetAddress;->holder:Ljava/net/InetAddress$InetAddressHolder;
+Ljava/net/InetAddress;->holder()Ljava/net/InetAddress$InetAddressHolder;
 Ljava/net/InetAddress$InetAddressHolder;->address:I
 Ljava/net/InetAddress$InetAddressHolder;->family:I
 Ljava/net/InetAddress$InetAddressHolder;->hostName:Ljava/lang/String;
 Ljava/net/InetAddress$InetAddressHolder;->originalHostName:Ljava/lang/String;
 Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z
 Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress;
-Ljava/net/InetAddress;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/InetAddress;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/net/InetSocketAddress;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/InetSocketAddress;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/Socket;->getFileDescriptor$()Ljava/io/FileDescriptor;
 Ljava/net/Socket;->impl:Ljava/net/SocketImpl;
+Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket;
+Ljava/net/SocketImpl;->socket:Ljava/net/Socket;
 Ljava/net/URI;->host:Ljava/lang/String;
-Ljava/net/URI;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/URI;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/net/URL;->handler:Ljava/net/URLStreamHandler;
 Ljava/net/URL;->handlers:Ljava/util/Hashtable;
-Ljava/net/URL;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/net/URL;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/nio/Buffer;->address:J
 Ljava/nio/Buffer;->capacity:I
 Ljava/nio/Buffer;->_elementSizeShift:I
@@ -3077,168 +3291,42 @@
 Ljava/nio/ByteBuffer;->offset:I
 Ljava/nio/charset/CharsetEncoder;->canEncode(Ljava/nio/CharBuffer;)Z
 Ljava/nio/DirectByteBuffer;-><init>(JI)V
-Ljava/nio/file/DirectoryIteratorException;->readObject(Ljava/io/ObjectInputStream;)V
 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
-Ljava/security/cert/CertificateRevokedException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/cert/CertificateRevokedException;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/security/cert/CertPathValidatorException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/CodeSigner;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/GuardedObject;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/security/Provider;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/security/SignedObject;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/security/spec/ECParameterSpec;->getCurveName()Ljava/lang/String;
 Ljava/security/spec/ECParameterSpec;->setCurveName(Ljava/lang/String;)V
-Ljava/security/Timestamp;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/ChoiceFormat;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/text/DateFormat;->is24Hour:Ljava/lang/Boolean;
-Ljava/text/DateFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DateFormatSymbols;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/DecimalFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DecimalFormatSymbols;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/DecimalFormatSymbols;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/DecimalFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/MessageFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/NumberFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/text/NumberFormat;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/text/SimpleDateFormat;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/AbstractChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/HijrahChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/HijrahDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/IsoChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/JapaneseEra;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/MinguoChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/MinguoDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/ThaiBuddhistChronology;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/chrono/ThaiBuddhistDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Duration;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/time/Duration;->toSeconds()Ljava/math/BigDecimal;
-Ljava/time/Instant;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalDate;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/LocalTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/MonthDay;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/time/OffsetDateTime;-><init>(Ljava/time/LocalDateTime;Ljava/time/ZoneOffset;)V
-Ljava/time/OffsetDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/OffsetTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Period;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/temporal/ValueRange;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/temporal/WeekFields;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/YearMonth;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/Year;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZonedDateTime;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZoneId;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/ZoneOffset;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneOffsetTransition;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneOffsetTransitionRule;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/time/zone/ZoneRules;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/ArrayDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/ArrayDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/ArrayList;->elementData:[Ljava/lang/Object;
-Ljava/util/ArrayList;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/ArrayList;->size:I
 Ljava/util/ArrayList$SubList;->parent:Ljava/util/AbstractList;
 Ljava/util/ArrayList$SubList;->parentOffset:I
 Ljava/util/ArrayList$SubList;->size:I
-Ljava/util/ArrayList;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Arrays$ArrayList;->a:[Ljava/lang/Object;
-Ljava/util/BitSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/BitSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Calendar;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Calendar;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Calendar;->zone:Ljava/util/TimeZone;
 Ljava/util/Collections$EmptyList;-><init>()V
-Ljava/util/Collections$SetFromMap;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/Collections$SynchronizedCollection;->c:Ljava/util/Collection;
-Ljava/util/Collections$SynchronizedCollection;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Collections$SynchronizedMap;->m:Ljava/util/Map;
-Ljava/util/Collections$SynchronizedMap;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection;
 Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map;
-Ljava/util/concurrent/atomic/AtomicReferenceArray;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/DoubleAccumulator;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/DoubleAdder;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/LongAccumulator;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/atomic/LongAdder;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z
-Ljava/util/concurrent/ConcurrentHashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentLinkedQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ConcurrentSkipListMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ConcurrentSkipListMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/CopyOnWriteArrayList;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/CopyOnWriteArrayList;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ForkJoinTask;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/ForkJoinTask;->writeObject(Ljava/io/ObjectOutputStream;)V
+Ljava/util/concurrent/Executors$RunnableAdapter;->task:Ljava/lang/Runnable;
 Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable;
-Ljava/util/concurrent/LinkedBlockingDeque;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedBlockingDeque;->writeObject(Ljava/io/ObjectOutputStream;)V
+Ljava/util/concurrent/FutureTask;->EXCEPTIONAL:I
+Ljava/util/concurrent/FutureTask;->outcome:Ljava/lang/Object;
+Ljava/util/concurrent/FutureTask;->state:I
 Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I
-Ljava/util/concurrent/LinkedBlockingQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedBlockingQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/LinkedTransferQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/LinkedTransferQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/locks/ReentrantLock$Sync;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/locks/ReentrantReadWriteLock$Sync;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/locks/StampedLock;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/PriorityBlockingQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/PriorityBlockingQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/SynchronousQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/concurrent/SynchronousQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/concurrent/ThreadLocalRandom;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Date;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Date;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/EnumMap;->keyType:Ljava/lang/Class;
-Ljava/util/EnumMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/EnumMap;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/EnumSet;->elementType:Ljava/lang/Class;
-Ljava/util/EnumSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/GregorianCalendar;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/HashMap$HashIterator;->hasNext()Z
-Ljava/util/HashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/HashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/HashSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/HashSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Hashtable;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Hashtable;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/IdentityHashMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/IdentityHashMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/InvalidPropertiesFormatException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/InvalidPropertiesFormatException;->writeObject(Ljava/io/ObjectOutputStream;)V
+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;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/LinkedList;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale;
-Ljava/util/Locale;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/Locale;->readResolve()Ljava/lang/Object;
-Ljava/util/Locale;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/logging/LogRecord;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/logging/LogRecord;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/prefs/NodeChangeEvent;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/prefs/NodeChangeEvent;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/prefs/PreferenceChangeEvent;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/prefs/PreferenceChangeEvent;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/PriorityQueue;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/PriorityQueue;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Random;->readObject(Ljava/io/ObjectInputStream;)V
 Ljava/util/Random;->seedUniquifier()J
-Ljava/util/Random;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/regex/Matcher;->appendPos:I
-Ljava/util/regex/Pattern;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/SimpleTimeZone;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/SimpleTimeZone;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/TreeMap;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/TreeMap;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/TreeSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljava/util/TreeSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljava/util/Vector;->writeObject(Ljava/io/ObjectOutputStream;)V
 Ljava/util/zip/Deflater;->buf:[B
 Ljava/util/zip/Deflater;->finished:Z
 Ljava/util/zip/Deflater;->finish:Z
@@ -3254,23 +3342,17 @@
 Ljava/util/zip/Inflater;->off:I
 Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V
 Ljava/util/zip/ZipFile;->jzfile:J
-Ljavax/crypto/SealedObject;->readObject(Ljava/io/ObjectInputStream;)V
 Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory;
 Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory;
-Ljavax/security/auth/Subject;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/Subject$SecureSet;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/Subject$SecureSet;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/security/auth/Subject;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/security/auth/x500/X500Principal;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/security/auth/x500/X500Principal;->writeObject(Ljava/io/ObjectOutputStream;)V
-Ljavax/xml/datatype/DatatypeConfigurationException;->readObject(Ljava/io/ObjectInputStream;)V
-Ljavax/xml/namespace/QName;->readObject(Ljava/io/ObjectInputStream;)V
+Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap;
 Llibcore/util/ZoneInfo;->mTransitions:[J
-Llibcore/util/ZoneInfo;->readObject(Ljava/io/ObjectInputStream;)V
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V
 Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String;
+Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I
 Lorg/json/JSONArray;->values:Ljava/util/List;
+Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject;
+Lorg/json/JSONObject;->keySet()Ljava/util/Set;
 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/util/ObjectIdentifier;->readObject(Ljava/io/ObjectInputStream;)V
-Lsun/security/util/ObjectIdentifier;->writeObject(Ljava/io/ObjectOutputStream;)V
diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt
index 2f0bba1..61ae6e7 100644
--- a/config/hiddenapi-vendor-list.txt
+++ b/config/hiddenapi-vendor-list.txt
@@ -42,6 +42,7 @@
 Landroid/app/IAssistDataReceiver;->onHandleAssistScreenshot(Landroid/graphics/Bitmap;)V
 Landroid/app/IAssistDataReceiver$Stub;-><init>()V
 Landroid/app/KeyguardManager;->isDeviceLocked(I)Z
+Landroid/app/NotificationManager;->cancelAsUser(Ljava/lang/String;ILandroid/os/UserHandle;)V
 Landroid/app/StatusBarManager;->removeIcon(Ljava/lang/String;)V
 Landroid/app/StatusBarManager;->setIcon(Ljava/lang/String;IILjava/lang/String;)V
 Landroid/app/TaskStackListener;->onActivityDismissingDockedStack()V
@@ -308,6 +309,7 @@
 Landroid/net/SntpClient;->getNtpTime()J
 Landroid/net/SntpClient;->getNtpTimeReference()J
 Landroid/net/SntpClient;->getRoundTripTime()J
+Landroid/net/SntpClient;->requestTime(Ljava/lang/String;I)Z
 Landroid/net/StaticIpConfiguration;->dnsServers:Ljava/util/ArrayList;
 Landroid/net/StaticIpConfiguration;->domains:Ljava/lang/String;
 Landroid/net/StaticIpConfiguration;->gateway:Ljava/net/InetAddress;
@@ -402,6 +404,9 @@
 Landroid/service/dreams/DreamService;->isDozing()Z
 Landroid/service/dreams/DreamService;->startDozing()V
 Landroid/service/dreams/DreamService;->stopDozing()V
+Landroid/service/euicc/EuiccProfileInfo;-><init>(Ljava/lang/String;[Landroid/telephony/UiccAccessRule;Ljava/lang/String;)V
+Landroid/service/euicc/GetDefaultDownloadableSubscriptionListResult;->result:I
+Landroid/service/euicc/GetDownloadableSubscriptionMetadataResult;->result:I
 Landroid/service/vr/VrListenerService;->onCurrentVrActivityChanged(Landroid/content/ComponentName;ZI)V
 Landroid/system/NetlinkSocketAddress;-><init>(II)V
 Landroid/system/Os;->bind(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)V
@@ -418,16 +423,42 @@
 Landroid/telecom/ParcelableCall;->getId()Ljava/lang/String;
 Landroid/telecom/TelecomManager;->from(Landroid/content/Context;)Landroid/telecom/TelecomManager;
 Landroid/telecom/VideoProfile$CameraCapabilities;-><init>(IIZF)V
+Landroid/telephony/euicc/DownloadableSubscription;->encodedActivationCode:Ljava/lang/String;
+Landroid/telephony/euicc/DownloadableSubscription;->setAccessRules([Landroid/telephony/UiccAccessRule;)V
+Landroid/telephony/euicc/DownloadableSubscription;->setCarrierName(Ljava/lang/String;)V
+Landroid/telephony/ims/compat/feature/ImsFeature;->getFeatureState()I
+Landroid/telephony/ims/compat/feature/ImsFeature;->setFeatureState(I)V
 Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V
 Landroid/telephony/ims/compat/ImsService;-><init>()V
+Landroid/telephony/ims/compat/ImsService;->mImsServiceController:Landroid/os/IBinder;
 Landroid/telephony/ims/compat/stub/ImsCallSessionImplBase;-><init>()V
+Landroid/telephony/ims/compat/stub/ImsConfigImplBase;->getIImsConfig()Lcom/android/ims/internal/IImsConfig;
 Landroid/telephony/ims/compat/stub/ImsConfigImplBase;-><init>(Landroid/content/Context;)V
 Landroid/telephony/ims/compat/stub/ImsUtListenerImplBase;-><init>()V
 Landroid/telephony/ims/ImsCallForwardInfo;-><init>()V
+Landroid/telephony/ims/ImsCallForwardInfo;->mCondition:I
+Landroid/telephony/ims/ImsCallForwardInfo;->mNumber:Ljava/lang/String;
+Landroid/telephony/ims/ImsCallForwardInfo;->mServiceClass:I
+Landroid/telephony/ims/ImsCallForwardInfo;->mStatus:I
+Landroid/telephony/ims/ImsCallForwardInfo;->mTimeSeconds:I
+Landroid/telephony/ims/ImsCallForwardInfo;->mToA:I
+Landroid/telephony/ims/ImsCallProfile;->mCallExtras:Landroid/os/Bundle;
+Landroid/telephony/ims/ImsCallProfile;->mCallType:I
+Landroid/telephony/ims/ImsCallProfile;->mMediaProfile:Landroid/telephony/ims/ImsStreamMediaProfile;
+Landroid/telephony/ims/ImsCallProfile;->mRestrictCause:I
 Landroid/telephony/ims/ImsCallProfile;->presentationToOIR(I)I
 Landroid/telephony/ims/ImsExternalCallState;-><init>(ILandroid/net/Uri;ZIIZ)V
 Landroid/telephony/ims/ImsReasonInfo;-><init>(II)V
+Landroid/telephony/ims/ImsReasonInfo;->mCode:I
+Landroid/telephony/ims/ImsReasonInfo;->mExtraCode:I
+Landroid/telephony/ims/ImsReasonInfo;->mExtraMessage:Ljava/lang/String;
+Landroid/telephony/ims/ImsSsInfo;->mIcbNum:Ljava/lang/String;
+Landroid/telephony/ims/ImsSsInfo;->mStatus:I
 Landroid/telephony/ims/ImsStreamMediaProfile;-><init>()V
+Landroid/telephony/ims/ImsStreamMediaProfile;->mAudioDirection:I
+Landroid/telephony/ims/ImsStreamMediaProfile;->mAudioQuality:I
+Landroid/telephony/ims/ImsStreamMediaProfile;->mVideoDirection:I
+Landroid/telephony/ims/ImsVideoCallProvider;->getInterface()Lcom/android/ims/internal/IImsVideoCallProvider;
 Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V
 Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V
 Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri;
@@ -449,6 +480,7 @@
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/telephony/Rlog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
 Landroid/telephony/Rlog;->i(Ljava/lang/String;Ljava/lang/String;)I
+Landroid/telephony/ServiceState;->bitmaskHasTech(II)Z
 Landroid/telephony/ServiceState;->getDataRegState()I
 Landroid/telephony/ServiceState;->getDataRoaming()Z
 Landroid/telephony/ServiceState;->getRilDataRadioTechnology()I
@@ -475,12 +507,19 @@
 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
+Landroid/telephony/TelephonyManager;->getIsimDomain()Ljava/lang/String;
 Landroid/telephony/TelephonyManager;->getNetworkTypeName()Ljava/lang/String;
+Landroid/telephony/TelephonyManager;->getPreferredNetworkType(I)I
+Landroid/telephony/TelephonyManager;->getServiceStateForSubscriber(I)Landroid/telephony/ServiceState;
 Landroid/telephony/TelephonyManager;->getVoiceMessageCount()I
 Landroid/telephony/TelephonyManager;->getVoiceNetworkType(I)I
+Landroid/telephony/TelephonyManager;->isImsRegistered()Z
+Landroid/telephony/TelephonyManager;->isWifiCallingAvailable()Z
 Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDA:Landroid/telephony/TelephonyManager$MultiSimVariants;
 Landroid/telephony/TelephonyManager$MultiSimVariants;->DSDS:Landroid/telephony/TelephonyManager$MultiSimVariants;
+Landroid/telephony/TelephonyManager;->nvResetConfig(I)Z
 Landroid/telephony/TelephonyManager;->putIntAtIndex(Landroid/content/ContentResolver;Ljava/lang/String;II)Z
+Landroid/telephony/TelephonyManager;->setPreferredNetworkType(II)Z
 Landroid/text/TextUtils;->isPrintableAsciiOnly(Ljava/lang/CharSequence;)Z
 Landroid/util/FloatMath;->ceil(F)F
 Landroid/util/FloatMath;->cos(F)F
@@ -498,6 +537,8 @@
 Landroid/util/LongArray;->get(I)J
 Landroid/util/LongArray;-><init>()V
 Landroid/util/LongArray;->size()I
+Landroid/util/RecurrenceRule;->buildRecurringMonthly(ILjava/time/ZoneId;)Landroid/util/RecurrenceRule;
+Landroid/util/RecurrenceRule;->start:Ljava/time/ZonedDateTime;
 Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;)I
 Landroid/util/Slog;->e(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
 Landroid/util/Slog;->println(ILjava/lang/String;Ljava/lang/String;)I
@@ -586,6 +627,7 @@
 Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V
 Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V
+Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V
@@ -598,6 +640,27 @@
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V
 Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V
+Lcom/android/ims/internal/uce/common/CapInfo;->getCapTimestamp()J
+Lcom/android/ims/internal/uce/common/CapInfo;->isCdViaPresenceSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isFtHttpSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isFtSnFSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isFtSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isFtThumbSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isFullSnFGroupChatSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullFtSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPullSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isGeoPushSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isImSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isIpVideoSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isIpVoiceSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isIsSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoCallSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVideoOnlyCallSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isRcsIpVoiceCallSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isSmSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isSpSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isVsDuringCSSupported()Z
+Lcom/android/ims/internal/uce/common/CapInfo;->isVsSupported()Z
 Lcom/android/ims/internal/uce/common/StatusCode;->getStatusCode()I
 Lcom/android/ims/internal/uce/common/UceLong;->getClientId()I
 Lcom/android/ims/internal/uce/common/UceLong;-><init>()V
@@ -617,6 +680,24 @@
 Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
+Lcom/android/ims/internal/uce/options/OptionsCapInfo;->getSdp()Ljava/lang/String;
+Lcom/android/ims/internal/uce/options/OptionsCapInfo;-><init>()V
+Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
+Lcom/android/ims/internal/uce/options/OptionsCapInfo;->setSdp(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/OptionsCmdId;-><init>()V
+Lcom/android/ims/internal/uce/options/OptionsCmdId;->setCmdId(I)V
+Lcom/android/ims/internal/uce/options/OptionsCmdStatus;-><init>()V
+Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCapInfo(Lcom/android/ims/internal/uce/common/CapInfo;)V
+Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
+Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setStatus(Lcom/android/ims/internal/uce/common/StatusCode;)V
+Lcom/android/ims/internal/uce/options/OptionsCmdStatus;->setUserData(I)V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;-><init>()V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setCmdId(Lcom/android/ims/internal/uce/options/OptionsCmdId;)V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setReasonPhrase(Ljava/lang/String;)V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRequestId(I)V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setRetryAfter(I)V
+Lcom/android/ims/internal/uce/options/OptionsSipResponse;->setSipResponseCode(I)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V
 Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V
@@ -635,6 +716,12 @@
 Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode;
 Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V
+Lcom/android/ims/internal/uce/presence/PresCapInfo;->getCapInfo()Lcom/android/ims/internal/uce/common/CapInfo;
+Lcom/android/ims/internal/uce/presence/PresCapInfo;->getContactUri()Ljava/lang/String;
+Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getMediaType()I
+Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceDesc()Ljava/lang/String;
+Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceId()Ljava/lang/String;
+Lcom/android/ims/internal/uce/presence/PresServiceInfo;->getServiceVer()Ljava/lang/String;
 Lcom/android/ims/internal/uce/presence/PresSipResponse;->getCmdId()Lcom/android/ims/internal/uce/presence/PresCmdId;
 Lcom/android/ims/internal/uce/presence/PresSipResponse;->getReasonPhrase()Ljava/lang/String;
 Lcom/android/ims/internal/uce/presence/PresSipResponse;->getRequestId()I
@@ -684,6 +771,9 @@
 Lcom/android/internal/os/BatteryStatsImpl;->getPhoneSignalStrengthTime(IJI)J
 Lcom/android/internal/os/BatteryStatsImpl;->getScreenBrightnessTime(IJI)J
 Lcom/android/internal/os/BatteryStatsImpl;->getWifiOnTime(JI)J
+Lcom/android/internal/os/SomeArgs;->arg1:Ljava/lang/Object;
+Lcom/android/internal/os/SomeArgs;->arg2:Ljava/lang/Object;
+Lcom/android/internal/os/SomeArgs;->arg3:Ljava/lang/Object;
 Lcom/android/internal/os/SomeArgs;->obtain()Lcom/android/internal/os/SomeArgs;
 Lcom/android/internal/os/SomeArgs;->recycle()V
 Lcom/android/internal/R$styleable;->NumberPicker:[I
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 3696eae..20149de 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -425,7 +425,7 @@
  * vs. those targeting prior platforms.  Starting with Honeycomb, an application
  * is not in the killable state until its {@link #onStop} has returned.  This
  * impacts when {@link #onSaveInstanceState(Bundle)} may be called (it may be
- * safely called after {@link #onPause()} and allows and application to safely
+ * safely called after {@link #onPause()}) and allows an application to safely
  * wait until {@link #onStop()} to save persistent state.</p>
  *
  * <p class="note">For applications targeting platforms starting with
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 3047cdb..289a4dd 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -959,8 +959,10 @@
      * @hide
      */
     static public boolean isHighEndGfx() {
-        return !isLowRamDeviceStatic() &&
-                !Resources.getSystem().getBoolean(com.android.internal.R.bool.config_avoidGfxAccel);
+        return !isLowRamDeviceStatic()
+                && !RoSystemProperties.CONFIG_AVOID_GFX_ACCEL
+                && !Resources.getSystem()
+                        .getBoolean(com.android.internal.R.bool.config_avoidGfxAccel);
     }
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f01eee4..1df724e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -30,12 +30,15 @@
 import android.app.assist.AssistContent;
 import android.app.assist.AssistStructure;
 import android.app.backup.BackupAgent;
+import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityLifecycleItem.LifecycleState;
+import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ActivityResultItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
 import android.app.servertransaction.PendingTransactionActions.StopInfo;
 import android.app.servertransaction.TransactionExecutor;
+import android.app.servertransaction.TransactionExecutorHelper;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
@@ -520,6 +523,10 @@
             return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
+        public boolean isVisibleFromServer() {
+            return activity != null && activity.mVisibleFromServer;
+        }
+
         public String toString() {
             ComponentName componentName = intent != null ? intent.getComponent() : null;
             return "ActivityRecord{"
@@ -1797,6 +1804,7 @@
                         // message is handled.
                         transaction.recycle();
                     }
+                    // TODO(lifecycler): Recycle locally scheduled transactions.
                     break;
             }
             Object obj = msg.obj;
@@ -2755,6 +2763,11 @@
         }
     }
 
+    @Override
+    TransactionExecutor getTransactionExecutor() {
+        return mTransactionExecutor;
+    }
+
     void sendMessage(int what, Object obj) {
         sendMessage(what, obj, 0, 0, false);
     }
@@ -4723,15 +4736,22 @@
             return;
         }
 
-        // TODO(b/73747058): Investigate converting this to use transaction to relaunch.
-        handleRelaunchActivityInner(r, 0 /* configChanges */, null /* pendingResults */,
-                null /* pendingIntents */, null /* pendingActions */, prevState != ON_RESUME,
-                r.overrideConfig, "handleRelaunchActivityLocally");
 
-        // Restore back to the previous state before relaunch if needed.
-        if (prevState != r.getLifecycleState()) {
-            mTransactionExecutor.cycleToPath(r, prevState);
-        }
+        // Initialize a relaunch request.
+        final MergedConfiguration mergedConfiguration = new MergedConfiguration(
+                r.createdConfig != null ? r.createdConfig : mConfiguration,
+                r.overrideConfig);
+        final ActivityRelaunchItem activityRelaunchItem = ActivityRelaunchItem.obtain(
+                null /* pendingResults */, null /* pendingIntents */, 0 /* configChanges */,
+                mergedConfiguration, r.mPreserveWindow);
+        // Make sure to match the existing lifecycle state in the end of the transaction.
+        final ActivityLifecycleItem lifecycleRequest =
+                TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
+        // Schedule the transaction.
+        final ClientTransaction transaction = ClientTransaction.obtain(this.mAppThread, r.token);
+        transaction.addCallback(activityRelaunchItem);
+        transaction.setLifecycleStateRequest(lifecycleRequest);
+        executeTransaction(transaction);
     }
 
     private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java
index 4df7379..cfaeec9 100644
--- a/core/java/android/app/AppComponentFactory.java
+++ b/core/java/android/app/AppComponentFactory.java
@@ -36,6 +36,10 @@
      * Allows application to override the creation of the application object. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Application object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -50,6 +54,10 @@
      * Allows application to override the creation of activities. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Activity object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -80,6 +88,10 @@
      * Allows application to override the creation of services. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the Service object. The returned object will not be initialized
+     * as a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -95,6 +107,10 @@
      * Allows application to override the creation of providers. This can be used to
      * perform things such as dependency injection or class loader changes to these
      * classes.
+     * <p>
+     * This method is only intended to provide a hook for instantiation. It does not provide
+     * earlier access to the ContentProvider object. The returned object will not be initialized
+     * with a Context yet and should not be used to interact with other android APIs.
      *
      * @param cl        The default classloader to use for instantiation.
      * @param className The class to be instantiated.
@@ -108,5 +124,5 @@
     /**
      * @hide
      */
-    public static AppComponentFactory DEFAULT = new AppComponentFactory();
+    public static final AppComponentFactory DEFAULT = new AppComponentFactory();
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index f38c80c..a68136b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2167,15 +2167,16 @@
     @Override
     public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
         try {
-            return mPM.getPackageSuspendedAppExtras(packageName, mContext.getUserId());
+            return mPM.getSuspendedPackageAppExtras(packageName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     @Override
-    public PersistableBundle getSuspendedPackageAppExtras() {
-        return getSuspendedPackageAppExtras(mContext.getOpPackageName());
+    public Bundle getSuspendedPackageAppExtras() {
+        final PersistableBundle extras = getSuspendedPackageAppExtras(mContext.getOpPackageName());
+        return extras != null ? new Bundle(extras.deepCopy()) : null;
     }
 
     @Override
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index 961bca2..925080e 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -17,6 +17,7 @@
 
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.PendingTransactionActions;
+import android.app.servertransaction.TransactionExecutor;
 import android.content.pm.ApplicationInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
@@ -43,6 +44,22 @@
         sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
     }
 
+    /**
+     * Execute transaction immediately without scheduling it. This is used for local requests, so
+     * it will also recycle the transaction.
+     */
+    void executeTransaction(ClientTransaction transaction) {
+        transaction.preExecute(this);
+        getTransactionExecutor().execute(transaction);
+        transaction.recycle();
+    }
+
+    /**
+     * Get the {@link TransactionExecutor} that will be performing lifecycle transitions and
+     * callbacks for activities.
+     */
+    abstract TransactionExecutor getTransactionExecutor();
+
     abstract void sendMessage(int what, Object obj);
 
 
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index f5e138c..71b88fa 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1713,6 +1713,9 @@
                 Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                 return PackageManager.PERMISSION_GRANTED;
             }
+            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
+                    + permission);
+            return PackageManager.PERMISSION_DENIED;
         }
 
         try {
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 99efecd..e1a02fa 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -94,6 +94,7 @@
     void registerUidObserver(in IUidObserver observer, int which, int cutpoint,
             String callingPackage);
     void unregisterUidObserver(in IUidObserver observer);
+    boolean isUidActive(int uid, String callingPackage);
     // =============== End of transactions used on native side as well ============================
 
     // Special low-level communication with activity manager.
@@ -322,12 +323,14 @@
     /**
      * Informs ActivityManagerService that the keyguard is showing.
      *
-     * @param showing True if the keyguard is showing, false otherwise.
+     * @param showingKeyguard True if the keyguard is showing, false otherwise.
+     * @param showingAod True if AOD is showing, false otherwise.
      * @param secondaryDisplayShowing The displayId of the secondary display on which the keyguard
      *        is showing, or INVALID_DISPLAY if there is no such display. Only meaningful if
      *        showing is true.
      */
-    void setLockScreenShown(boolean showing, int secondaryDisplayShowing);
+    void setLockScreenShown(boolean showingKeyguard, boolean showingAod,
+            int secondaryDisplayShowing);
     boolean finishActivityAffinity(in IBinder token);
     // This is not public because you need to be very careful in how you
     // manage your activity to make sure it is always the uid you expect.
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d3c1e99..4326ee3 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1219,11 +1219,11 @@
     public static final String EXTRA_SUBSTITUTE_APP_NAME = "android.substName";
 
     /**
-     * This is set on the notification shown by the activity manager about all apps
-     * running in the background.  It indicates that the notification should be shown
-     * only if any of the given apps do not already have a {@link #FLAG_FOREGROUND_SERVICE}
-     * notification currently visible to the user.  This is a string array of all
-     * package names of the apps.
+     * This is set on the notifications shown by system_server about apps running foreground
+     * services. It indicates that the notification should be shown
+     * only if any of the given apps do not already have a properly tagged
+     * {@link #FLAG_FOREGROUND_SERVICE} notification currently visible to the user.
+     * This is a string array of all package names of the apps.
      * @hide
      */
     public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps";
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 30f2697..4a7cf62 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -463,7 +463,11 @@
 
     /**
      * Returns the user specified importance e.g. {@link NotificationManager#IMPORTANCE_LOW} for
-     * notifications posted to this channel.
+     * notifications posted to this channel. Note: This value might be >
+     * {@link NotificationManager#IMPORTANCE_NONE}, but notifications posted to this channel will
+     * not be shown to the user if the parent {@link NotificationChannelGroup} or app is blocked.
+     * See {@link NotificationChannelGroup#isBlocked()} and
+     * {@link NotificationManager#areNotificationsEnabled()}.
      */
     public int getImportance() {
         return mImportance;
diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java
index 16166f7..0fa3c7f 100644
--- a/core/java/android/app/NotificationChannelGroup.java
+++ b/core/java/android/app/NotificationChannelGroup.java
@@ -145,7 +145,9 @@
 
     /**
      * Returns whether or not notifications posted to {@link NotificationChannel channels} belonging
-     * to this group are blocked.
+     * to this group are blocked. This value is independent of
+     * {@link NotificationManager#areNotificationsEnabled()} and
+     * {@link NotificationChannel#getImportance()}.
      */
     public boolean isBlocked() {
         return mBlocked;
diff --git a/core/java/android/app/RemoteAction.java b/core/java/android/app/RemoteAction.java
index e7fe407..47741c0 100644
--- a/core/java/android/app/RemoteAction.java
+++ b/core/java/android/app/RemoteAction.java
@@ -18,14 +18,9 @@
 
 import android.annotation.NonNull;
 import android.graphics.drawable.Icon;
-import android.os.Handler;
-import android.os.Message;
-import android.os.Messenger;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.text.TextUtils;
-import android.util.Log;
 
 import java.io.PrintWriter;
 
@@ -42,6 +37,7 @@
     private final CharSequence mContentDescription;
     private final PendingIntent mActionIntent;
     private boolean mEnabled;
+    private boolean mShouldShowIcon;
 
     RemoteAction(Parcel in) {
         mIcon = Icon.CREATOR.createFromParcel(in);
@@ -49,6 +45,7 @@
         mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mActionIntent = PendingIntent.CREATOR.createFromParcel(in);
         mEnabled = in.readBoolean();
+        mShouldShowIcon = in.readBoolean();
     }
 
     public RemoteAction(@NonNull Icon icon, @NonNull CharSequence title,
@@ -62,6 +59,7 @@
         mContentDescription = contentDescription;
         mActionIntent = intent;
         mEnabled = true;
+        mShouldShowIcon = true;
     }
 
     /**
@@ -79,6 +77,20 @@
     }
 
     /**
+     * Sets whether the icon should be shown.
+     */
+    public void setShouldShowIcon(boolean shouldShowIcon) {
+        mShouldShowIcon = shouldShowIcon;
+    }
+
+    /**
+     * Return whether the icon should be shown.
+     */
+    public boolean shouldShowIcon() {
+        return mShouldShowIcon;
+    }
+
+    /**
      * Return an icon representing the action.
      */
     public @NonNull Icon getIcon() {
@@ -125,6 +137,7 @@
         TextUtils.writeToParcel(mContentDescription, out, flags);
         mActionIntent.writeToParcel(out, flags);
         out.writeBoolean(mEnabled);
+        out.writeBoolean(mShouldShowIcon);
     }
 
     public void dump(String prefix, PrintWriter pw) {
@@ -134,6 +147,7 @@
         pw.print(" contentDescription=" + mContentDescription);
         pw.print(" icon=" + mIcon);
         pw.print(" action=" + mActionIntent.getIntent());
+        pw.print(" shouldShowIcon=" + mShouldShowIcon);
         pw.println();
     }
 
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 8f01685..bd4933a 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.TestApi;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -47,6 +46,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
+
 import libcore.io.IoUtils;
 
 import java.io.IOException;
@@ -876,15 +876,35 @@
     }
 
     /**
+     * Grants a runtime permission to a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to grant the permission.
+     */
+    public void grantRuntimePermission(String packageName, String permission) {
+        grantRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #grantRuntimePermissionAsUser(String, String, UserHandle)}.
+     * @hide
+     */
+    @Deprecated
+    @TestApi
+    public boolean grantRuntimePermission(String packageName, String permission,
+            UserHandle userHandle) {
+        grantRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
      * Grants a runtime permission to a package for a user.
      * @param packageName The package to which to grant.
      * @param permission The permission to grant.
-     * @return Whether granting succeeded.
-     *
-     * @hide
+     * @throws SecurityException if unable to grant the permission.
      */
-    @TestApi
-    public boolean grantRuntimePermission(String packageName, String permission,
+    public void grantRuntimePermissionAsUser(String packageName, String permission,
             UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
@@ -896,25 +916,42 @@
             // Calling out without a lock held.
             mUiAutomationConnection.grantRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error granting runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
-     * Revokes a runtime permission from a package for a user.
-     * @param packageName The package from which to revoke.
-     * @param permission The permission to revoke.
-     * @return Whether revoking succeeded.
-     *
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermission(String packageName, String permission) {
+        revokeRuntimePermissionAsUser(packageName, permission, android.os.Process.myUserHandle());
+    }
+
+    /**
+     * @deprecated replaced by
+     *             {@link #revokeRuntimePermissionAsUser(String, String, UserHandle)}.
      * @hide
      */
+    @Deprecated
     @TestApi
     public boolean revokeRuntimePermission(String packageName, String permission,
             UserHandle userHandle) {
+        revokeRuntimePermissionAsUser(packageName, permission, userHandle);
+        return true;
+    }
+
+    /**
+     * Revokes a runtime permission from a package.
+     * @param packageName The package to which to grant.
+     * @param permission The permission to grant.
+     * @throws SecurityException if unable to revoke the permission.
+     */
+    public void revokeRuntimePermissionAsUser(String packageName, String permission,
+            UserHandle userHandle) {
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
@@ -925,12 +962,9 @@
             // Calling out without a lock held.
             mUiAutomationConnection.revokeRuntimePermission(packageName,
                     permission, userHandle.getIdentifier());
-            // TODO: The package manager API should return boolean.
-            return true;
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error revoking runtime permission", re);
+        } catch (Exception e) {
+            throw new SecurityException("Error granting runtime permission", e);
         }
-        return false;
     }
 
     /**
@@ -949,6 +983,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source = null;
         ParcelFileDescriptor sink = null;
@@ -991,6 +1026,7 @@
         synchronized (mLock) {
             throwIfNotConnectedLocked();
         }
+        warnIfBetterCommand(command);
 
         ParcelFileDescriptor source_read = null;
         ParcelFileDescriptor sink_read = null;
@@ -1056,6 +1092,16 @@
         }
     }
 
+    private void warnIfBetterCommand(String cmd) {
+        if (cmd.startsWith("pm grant ")) {
+            Log.w(LOG_TAG, "UiAutomation.grantRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm grant'");
+        } else if (cmd.startsWith("pm revoke ")) {
+            Log.w(LOG_TAG, "UiAutomation.revokeRuntimePermission() "
+                    + "is more robust and should be used instead of 'pm revoke'");
+        }
+    }
+
     private class IAccessibilityServiceClientImpl extends IAccessibilityServiceClientWrapper {
 
         public IAccessibilityServiceClientImpl(Looper looper) {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 19240e2..465340f 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -57,10 +57,7 @@
 import android.os.Looper;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.service.wallpaper.WallpaperService;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -444,6 +441,9 @@
             synchronized (this) {
                 mCachedWallpaper = null;
                 mCachedWallpaperUserId = 0;
+                if (mDefaultWallpaper != null) {
+                    mDefaultWallpaper.recycle();
+                }
                 mDefaultWallpaper = null;
             }
         }
@@ -915,9 +915,14 @@
     /**
      * Get the primary colors of a wallpaper.
      *
-     * <p>You can expect null if:
-     * • Colors are still being processed by the system.
-     * • A live wallpaper doesn't implement {@link WallpaperService.Engine#onComputeColors()}.
+     * <p>This method can return {@code null} when:
+     * <ul>
+     * <li>Colors are still being processed by the system.</li>
+     * <li>The user has chosen to use a live wallpaper:  live wallpapers might not
+     * implement
+     * {@link android.service.wallpaper.WallpaperService.Engine#onComputeColors()
+     *     WallpaperService.Engine#onComputeColors()}.</li>
+     * </ul>
      *
      * @param which Wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}.
@@ -929,7 +934,7 @@
     }
 
     /**
-     * Get the primary colors of a wallpaper
+     * Get the primary colors of the wallpaper configured in the given user.
      * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}
      * @param userId Owner of the wallpaper.
@@ -1559,11 +1564,13 @@
      * Specify extra padding that the wallpaper should have outside of the display.
      * That is, the given padding supplies additional pixels the wallpaper should extend
      * outside of the display itself.
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER_HINTS}.
+     *
      * @param padding The number of pixels the wallpaper should extend beyond the display,
      * on its left, top, right, and bottom sides.
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_HINTS)
     public void setDisplayPadding(Rect padding) {
         try {
@@ -1600,11 +1607,11 @@
     }
 
     /**
-     * Clear the wallpaper.
+     * Reset all wallpaper to the factory default.
      *
-     * @hide
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#SET_WALLPAPER}.
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.SET_WALLPAPER)
     public void clearWallpaper() {
         clearWallpaper(FLAG_LOCK, mContext.getUserId());
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3015398..b64aae5 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -124,6 +124,7 @@
 @SystemService(Context.DEVICE_POLICY_SERVICE)
 @RequiresFeature(PackageManager.FEATURE_DEVICE_ADMIN)
 public class DevicePolicyManager {
+
     private static String TAG = "DevicePolicyManager";
 
     private final Context mContext;
@@ -1612,8 +1613,6 @@
      *     <li>keyguard
      * </ul>
      *
-     * This is the default configuration for LockTask.
-     *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
     public static final int LOCK_TASK_FEATURE_NONE = 0;
@@ -1631,7 +1630,10 @@
     /**
      * Enable notifications during LockTask mode. This includes notification icons on the status
      * bar, heads-up notifications, and the expandable notification shade. Note that the Quick
-     * Settings panel will still be disabled.
+     * Settings panel remains disabled. This feature flag can only be used in combination with
+     * {@link #LOCK_TASK_FEATURE_HOME}. {@link #setLockTaskFeatures(ComponentName, int)}
+     * throws an {@link IllegalArgumentException} if this feature flag is defined without
+     * {@link #LOCK_TASK_FEATURE_HOME}.
      *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
@@ -1664,6 +1666,9 @@
      * the user long-presses the power button, for example. Note that the user may not be able to
      * power off the device if this flag is not set.
      *
+     * <p>This flag is enabled by default until {@link #setLockTaskFeatures(ComponentName, int)} is
+     * called for the first time.
+     *
      * @see #setLockTaskFeatures(ComponentName, int)
      */
     public static final int LOCK_TASK_FEATURE_GLOBAL_ACTIONS = 1 << 4;
@@ -1747,6 +1752,25 @@
     public static final int ID_TYPE_MEID = 8;
 
     /**
+     * Specifies that the calling app should be granted access to the installed credentials
+     * immediately. Otherwise, access to the credentials will be gated by user approval.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_REQUEST_CREDENTIALS_ACCESS = 1;
+
+    /**
+     * Specifies that a user can select the key via the Certificate Selection prompt.
+     * If this flag is not set when calling {@link #installKeyPair}, the key can only be granted
+     * access by implementing {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * For use with {@link #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)}
+     *
+     * @see #installKeyPair(ComponentName, PrivateKey, Certificate[], String, int)
+     */
+    public static final int INSTALLKEY_SET_USER_SELECTABLE = 2;
+
+    /**
      * Broadcast action: sent when the profile owner is set, changed or cleared.
      *
      * This broadcast is sent only to the user managed by the new profile owner.
@@ -2723,110 +2747,6 @@
     }
 
     /**
-     * The maximum number of characters allowed in the password blacklist.
-     */
-    private static final int PASSWORD_BLACKLIST_CHARACTER_LIMIT = 128 * 1000;
-
-    /**
-     * Throws an exception if the password blacklist is too large.
-     *
-     * @hide
-     */
-    public static void enforcePasswordBlacklistSize(List<String> blacklist) {
-        if (blacklist == null) {
-            return;
-        }
-        long characterCount = 0;
-        for (final String item : blacklist) {
-            characterCount += item.length();
-        }
-        if (characterCount > PASSWORD_BLACKLIST_CHARACTER_LIMIT) {
-            throw new IllegalArgumentException("128 thousand blacklist character limit exceeded by "
-                      + (characterCount - PASSWORD_BLACKLIST_CHARACTER_LIMIT) + " characters");
-        }
-    }
-
-    /**
-     * Called by an application that is administering the device to blacklist passwords.
-     * <p>
-     * Any blacklisted password or PIN is prevented from being enrolled by the user or the admin.
-     * Note that the match against the blacklist is case insensitive. The blacklist applies for all
-     * password qualities requested by {@link #setPasswordQuality} however it is not taken into
-     * consideration by {@link #isActivePasswordSufficient}.
-     * <p>
-     * The blacklist can be cleared by passing {@code null} or an empty list. The blacklist is
-     * given a name that is used to track which blacklist is currently set by calling {@link
-     * #getPasswordBlacklistName}. If the blacklist is being cleared, the name is ignored and {@link
-     * #getPasswordBlacklistName} will return {@code null}. The name can only be {@code null} when
-     * the blacklist is being cleared.
-     * <p>
-     * The blacklist is limited to a total of 128 thousand characters rather than limiting to a
-     * number of entries.
-     * <p>
-     * This method can be called on the {@link DevicePolicyManager} instance returned by
-     * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent
-     * profile.
-     *
-     * @param admin the {@link DeviceAdminReceiver} this request is associated with
-     * @param name name to associate with the blacklist
-     * @param blacklist list of passwords to blacklist or {@code null} to clear the blacklist
-     * @return whether the new blacklist was successfully installed
-     * @throws SecurityException if {@code admin} is not a device or profile owner
-     * @throws IllegalArgumentException if the blacklist surpasses the character limit
-     * @throws NullPointerException if {@code name} is {@code null} when setting a non-empty list
-     *
-     * @see #getPasswordBlacklistName
-     * @see #isActivePasswordSufficient
-     * @see #resetPasswordWithToken
-     */
-    public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name,
-            @Nullable List<String> blacklist) {
-        enforcePasswordBlacklistSize(blacklist);
-
-        try {
-            return mService.setPasswordBlacklist(admin, name, blacklist, mParentInstance);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Get the name of the password blacklist set by the given admin.
-     *
-     * @param admin the {@link DeviceAdminReceiver} this request is associated with
-     * @return the name of the blacklist or {@code null} if no blacklist is set
-     *
-     * @see #setPasswordBlacklist
-     */
-    public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) {
-        try {
-            return mService.getPasswordBlacklistName(admin, myUserId(), mParentInstance);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Test if a given password is blacklisted.
-     *
-     * @param userId the user to valiate for
-     * @param password the password to check against the blacklist
-     * @return whether the password is blacklisted
-     *
-     * @see #setPasswordBlacklist
-     *
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.TEST_BLACKLISTED_PASSWORD)
-    public boolean isPasswordBlacklisted(@UserIdInt int userId, @NonNull String password) {
-        try {
-            return mService.isPasswordBlacklisted(userId, password);
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Determine whether the current password the user has set is sufficient to meet the policy
      * requirements (e.g. quality, minimum length) that have been requested by the admins of this
      * user and its participating profiles. Restrictions on profiles that have a separate challenge
@@ -3742,7 +3662,7 @@
     public static final int KEYGUARD_DISABLE_TRUST_AGENTS = 1 << 4;
 
     /**
-     * Disable fingerprint sensor on keyguard secure screens (e.g. PIN/Pattern/Password).
+     * Disable fingerprint authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
      */
     public static final int KEYGUARD_DISABLE_FINGERPRINT = 1 << 5;
 
@@ -3752,6 +3672,25 @@
     public static final int KEYGUARD_DISABLE_REMOTE_INPUT = 1 << 6;
 
     /**
+     * Disable face authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_FACE = 1 << 7;
+
+    /**
+     * Disable iris authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_IRIS = 1 << 8;
+
+    /**
+     * Disable all biometric authentication on keyguard secure screens (e.g. PIN/Pattern/Password).
+     */
+    public static final int KEYGUARD_DISABLE_BIOMETRICS =
+            DevicePolicyManager.KEYGUARD_DISABLE_FACE
+            | DevicePolicyManager.KEYGUARD_DISABLE_IRIS
+            | DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
+
+
+    /**
      * Disable all current and future keyguard customizations.
      */
     public static final int KEYGUARD_DISABLE_FEATURES_ALL = 0x7fffffff;
@@ -4103,7 +4042,11 @@
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
             @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess) {
-        return installKeyPair(admin, privKey, certs, alias, requestAccess, true);
+        int flags = INSTALLKEY_SET_USER_SELECTABLE;
+        if (requestAccess) {
+            flags |= INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        }
+        return installKeyPair(admin, privKey, certs, alias, flags);
     }
 
     /**
@@ -4127,13 +4070,9 @@
      *        {@link android.security.KeyChain#getCertificateChain}.
      * @param alias The private key alias under which to install the certificate. If a certificate
      *        with that alias already exists, it will be overwritten.
-     * @param requestAccess {@code true} to request that the calling app be granted access to the
-     *        credentials immediately. Otherwise, access to the credentials will be gated by user
-     *        approval.
-     * @param isUserSelectable {@code true} to indicate that a user can select this key via the
-     *        Certificate Selection prompt, false to indicate that this key can only be granted
-     *        access by implementing
-     *        {@link android.app.admin.DeviceAdminReceiver#onChoosePrivateKeyAlias}.
+     * @param flags Flags to request that the calling app be granted access to the credentials
+     *        and set the key to be user-selectable. See {@link #INSTALLKEY_SET_USER_SELECTABLE} and
+     *        {@link #INSTALLKEY_REQUEST_CREDENTIALS_ACCESS}.
      * @return {@code true} if the keys were installed, {@code false} otherwise.
      * @throws SecurityException if {@code admin} is not {@code null} and not a device or profile
      *         owner.
@@ -4142,9 +4081,12 @@
      * @see #DELEGATION_CERT_INSTALL
      */
     public boolean installKeyPair(@Nullable ComponentName admin, @NonNull PrivateKey privKey,
-            @NonNull Certificate[] certs, @NonNull String alias, boolean requestAccess,
-            boolean isUserSelectable) {
+            @NonNull Certificate[] certs, @NonNull String alias, int flags) {
         throwIfParentInstance("installKeyPair");
+        boolean requestAccess = (flags & INSTALLKEY_REQUEST_CREDENTIALS_ACCESS)
+                == INSTALLKEY_REQUEST_CREDENTIALS_ACCESS;
+        boolean isUserSelectable = (flags & INSTALLKEY_SET_USER_SELECTABLE)
+                == INSTALLKEY_SET_USER_SELECTABLE;
         try {
             final byte[] pemCert = Credentials.convertToPem(certs[0]);
             byte[] pemChain = null;
@@ -4223,6 +4165,8 @@
      *         algorithm specification in {@code keySpec} is not {@code RSAKeyGenParameterSpec}
      *         or {@code ECGenParameterSpec}, or if Device ID attestation was requested but the
      *         {@code keySpec} does not contain an attestation challenge.
+     * @throws UnsupportedOperationException if Device ID attestation was requested but the
+     *         underlying hardware does not support it.
      * @see KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])
      */
     public AttestedKeyPair generateKeyPair(@Nullable ComponentName admin,
@@ -6340,6 +6284,7 @@
      * @hide
      */
     @SystemApi
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
     public @Nullable List<String> getPermittedInputMethodsForCurrentUser() {
         throwIfParentInstance("getPermittedInputMethodsForCurrentUser");
         if (mService != null) {
@@ -7167,30 +7112,24 @@
     }
 
     /**
-     * Sets which system features to enable for LockTask mode.
-     * <p>
-     * Feature flags set through this method will only take effect for the duration when the device
-     * is in LockTask mode. If this method is not called, none of the features listed here will be
-     * enabled.
-     * <p>
-     * This function can only be called by the device owner, a profile owner of an affiliated user
-     * or profile, or the profile owner when no device owner is set. See {@link #isAffiliatedUser}.
-     * Any features set via this method will be cleared if the user becomes unaffiliated.
+     * Sets which system features are enabled when the device runs in lock task mode. This method
+     * doesn't affect the features when lock task mode is inactive. Any system features not included
+     * in {@code flags} are implicitly disabled when calling this method. By default, only
+     * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS} is enabled—all the other features are disabled. To
+     * disable the global actions dialog, call this method omitting
+     * {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}.
+     *
+     * <p>This method can only be called by the device owner, a profile owner of an affiliated
+     * user or profile, or the profile owner when no device owner is set. See
+     * {@link #isAffiliatedUser}.
+     * Any features set using this method are cleared if the user becomes unaffiliated.
      *
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
-     * @param flags Bitfield of feature flags:
-     *              {@link #LOCK_TASK_FEATURE_NONE} (default),
-     *              {@link #LOCK_TASK_FEATURE_SYSTEM_INFO},
-     *              {@link #LOCK_TASK_FEATURE_NOTIFICATIONS},
-     *              {@link #LOCK_TASK_FEATURE_HOME},
-     *              {@link #LOCK_TASK_FEATURE_OVERVIEW},
-     *              {@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS},
-     *              {@link #LOCK_TASK_FEATURE_KEYGUARD}
+     * @param flags The system features enabled during lock task mode.
      * @throws SecurityException if {@code admin} is not the device owner, the profile owner of an
      * affiliated user or profile, or the profile owner when no device owner is set.
      * @see #isAffiliatedUser
-     * @throws SecurityException if {@code admin} is not the device owner or the profile owner.
-     */
+     **/
     public void setLockTaskFeatures(@NonNull ComponentName admin, @LockTaskFeature int flags) {
         throwIfParentInstance("setLockTaskFeatures");
         if (mService != null) {
@@ -7283,11 +7222,12 @@
     public @interface SystemSettingsWhitelist {}
 
     /**
-     * Called by device owner to update {@link android.provider.Settings.System} settings.
-     * Validation that the value of the setting is in the correct form for the setting type should
-     * be performed by the caller.
+     * Called by a device or profile owner to update {@link android.provider.Settings.System}
+     * settings. Validation that the value of the setting is in the correct form for the setting
+     * type should be performed by the caller.
      * <p>
-     * The settings that can be updated with this method are:
+     * The settings that can be updated by a device owner or profile owner of secondary user with
+     * this method are:
      * <ul>
      * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS}</li>
      * <li>{@link android.provider.Settings.System#SCREEN_BRIGHTNESS_MODE}</li>
@@ -7299,7 +7239,7 @@
      * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
      * @param setting The name of the setting to update.
      * @param value The value to update the setting to.
-     * @throws SecurityException if {@code admin} is not a device owner.
+     * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
     public void setSystemSetting(@NonNull ComponentName admin,
             @NonNull @SystemSettingsWhitelist String setting, String value) {
@@ -8363,12 +8303,12 @@
      * @return a list of package names which could not be restricted.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public @NonNull List<String> setMeteredDataDisabled(@NonNull ComponentName admin,
+    public @NonNull List<String> setMeteredDataDisabledPackages(@NonNull ComponentName admin,
             @NonNull List<String> packageNames) {
         throwIfParentInstance("setMeteredDataDisabled");
         if (mService != null) {
             try {
-                return mService.setMeteredDataDisabled(admin, packageNames);
+                return mService.setMeteredDataDisabledPackages(admin, packageNames);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -8384,11 +8324,11 @@
      * @return the list of restricted package names.
      * @throws SecurityException if {@code admin} is not a device or profile owner.
      */
-    public @NonNull List<String> getMeteredDataDisabled(@NonNull ComponentName admin) {
+    public @NonNull List<String> getMeteredDataDisabledPackages(@NonNull ComponentName admin) {
         throwIfParentInstance("getMeteredDataDisabled");
         if (mService != null) {
             try {
-                return mService.getMeteredDataDisabled(admin);
+                return mService.getMeteredDataDisabledPackages(admin);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
@@ -8407,12 +8347,12 @@
      * @throws SecurityException if the caller doesn't run with {@link Process#SYSTEM_UID}
      * @hide
      */
-    public boolean isMeteredDataDisabledForUser(@NonNull ComponentName admin, String packageName,
-            @UserIdInt int userId) {
+    public boolean isMeteredDataDisabledPackageForUser(@NonNull ComponentName admin,
+            String packageName, @UserIdInt int userId) {
         throwIfParentInstance("getMeteredDataDisabledForUser");
         if (mService != null) {
             try {
-                return mService.isMeteredDataDisabledForUser(admin, packageName, userId);
+                return mService.isMeteredDataDisabledPackageForUser(admin, packageName, userId);
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index c46402f..37508cd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -79,10 +79,6 @@
 
     long getPasswordExpiration(in ComponentName who, int userHandle, boolean parent);
 
-    boolean setPasswordBlacklist(in ComponentName who, String name, in List<String> blacklist, boolean parent);
-    String getPasswordBlacklistName(in ComponentName who, int userId, boolean parent);
-    boolean isPasswordBlacklisted(int userId, String password);
-
     boolean isActivePasswordSufficient(int userHandle, boolean parent);
     boolean isProfileActivePasswordSufficientForParent(int userHandle);
     boolean isUsingUnifiedPassword(in ComponentName admin);
@@ -404,8 +400,8 @@
     CharSequence getStartUserSessionMessage(in ComponentName admin);
     CharSequence getEndUserSessionMessage(in ComponentName admin);
 
-    List<String> setMeteredDataDisabled(in ComponentName admin, in List<String> packageNames);
-    List<String> getMeteredDataDisabled(in ComponentName admin);
+    List<String> setMeteredDataDisabledPackages(in ComponentName admin, in List<String> packageNames);
+    List<String> getMeteredDataDisabledPackages(in ComponentName admin);
 
     int addOverrideApn(in ComponentName admin, in ApnSetting apnSetting);
     boolean updateOverrideApn(in ComponentName admin, int apnId, in ApnSetting apnSetting);
@@ -414,5 +410,5 @@
     void setOverrideApnsEnabled(in ComponentName admin, boolean enabled);
     boolean isOverrideApnEnabled(in ComponentName admin);
 
-    boolean isMeteredDataDisabledForUser(in ComponentName admin, String packageName, int userId);
+    boolean isMeteredDataDisabledPackageForUser(in ComponentName admin, String packageName, int userId);
 }
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index fb1c2d0..b7a8da5 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -84,6 +84,8 @@
 
     public static final String FLAG_REQUIRED_CLIENT_SIDE_ENCRYPTION = "clientSideEncryption";
     public static final String FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER = "deviceToDeviceTransfer";
+    public static final String FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION =
+            "fakeClientSideEncryption";
 
     /**
      * @hide
@@ -600,6 +602,8 @@
                     case FLAG_REQUIRED_DEVICE_TO_DEVICE_TRANSFER:
                         flags |= BackupAgent.FLAG_DEVICE_TO_DEVICE_TRANSFER;
                         break;
+                    case FLAG_REQUIRED_FAKE_CLIENT_SIDE_ENCRYPTION:
+                        flags |= BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED;
                     default:
                         Log.w(TAG, "Unrecognized requiredFlag provided, value: \"" + f + "\"");
                 }
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index ee13880..02afcc7 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -65,7 +65,6 @@
             NETWORK_TYPE_UNMETERED,
             NETWORK_TYPE_NOT_ROAMING,
             NETWORK_TYPE_CELLULAR,
-            NETWORK_TYPE_METERED,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface NetworkType {}
@@ -253,7 +252,7 @@
     /**
      * @hide
      */
-    public static final int FLAG_IS_PREFETCH = 1 << 2;
+    public static final int FLAG_PREFETCH = 1 << 2;
 
     /**
      * This job needs to be exempted from the app standby throttling. Only the system (UID 1000)
@@ -296,7 +295,8 @@
     private final boolean hasEarlyConstraint;
     private final boolean hasLateConstraint;
     private final NetworkRequest networkRequest;
-    private final long networkBytes;
+    private final long networkDownloadBytes;
+    private final long networkUploadBytes;
     private final long minLatencyMillis;
     private final long maxExecutionDelayMillis;
     private final boolean isPeriodic;
@@ -317,30 +317,28 @@
     }
 
     /**
-     * Bundle of extras which are returned to your application at execution time.
+     * @see JobInfo.Builder#setExtras(PersistableBundle)
      */
     public @NonNull PersistableBundle getExtras() {
         return extras;
     }
 
     /**
-     * Bundle of transient extras which are returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setTransientExtras(Bundle)
      */
     public @NonNull Bundle getTransientExtras() {
         return transientExtras;
     }
 
     /**
-     * ClipData of information that is returned to your application at execution time,
-     * but not persisted by the system.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public @Nullable ClipData getClipData() {
         return clipData;
     }
 
     /**
-     * Permission grants that go along with {@link #getClipData}.
+     * @see JobInfo.Builder#setClipData(ClipData, int)
      */
     public int getClipGrantFlags() {
         return clipGrantFlags;
@@ -369,32 +367,28 @@
     }
 
     /**
-     * Whether this job requires that the device be charging (or be a non-battery-powered
-     * device connected to permanent power, such as Android TV devices).
+     * @see JobInfo.Builder#setRequiresCharging(boolean)
      */
     public boolean isRequireCharging() {
         return (constraintFlags & CONSTRAINT_FLAG_CHARGING) != 0;
     }
 
     /**
-     * Whether this job needs the device's battery level to not be at below the critical threshold.
+     * @see JobInfo.Builder#setRequiresBatteryNotLow(boolean)
      */
     public boolean isRequireBatteryNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_BATTERY_NOT_LOW) != 0;
     }
 
     /**
-     * Whether this job requires that the user <em>not</em> be interacting with the device.
-     *
-     * <p class="note">This is <em>not</em> the same as "doze" or "device idle";
-     * it is purely about the user's direct interactions.</p>
+     * @see JobInfo.Builder#setRequiresDeviceIdle(boolean)
      */
     public boolean isRequireDeviceIdle() {
         return (constraintFlags & CONSTRAINT_FLAG_DEVICE_IDLE) != 0;
     }
 
     /**
-     * Whether this job needs the device's storage to not be low.
+     * @see JobInfo.Builder#setRequiresStorageNotLow(boolean)
      */
     public boolean isRequireStorageNotLow() {
         return (constraintFlags & CONSTRAINT_FLAG_STORAGE_NOT_LOW) != 0;
@@ -410,6 +404,7 @@
     /**
      * Which content: URIs must change for the job to be scheduled.  Returns null
      * if there are none required.
+     * @see JobInfo.Builder#addTriggerContentUri(TriggerContentUri)
      */
     public @Nullable TriggerContentUri[] getTriggerContentUris() {
         return triggerContentUris;
@@ -418,6 +413,7 @@
     /**
      * When triggering on content URI changes, this is the delay from when a change
      * is detected until the job is scheduled.
+     * @see JobInfo.Builder#setTriggerContentUpdateDelay(long)
      */
     public long getTriggerContentUpdateDelay() {
         return triggerContentUpdateDelay;
@@ -426,6 +422,7 @@
     /**
      * When triggering on content URI changes, this is the maximum delay we will
      * use before scheduling the job.
+     * @see JobInfo.Builder#setTriggerContentMaxDelay(long)
      */
     public long getTriggerContentMaxDelay() {
         return triggerContentMaxDelay;
@@ -466,28 +463,59 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (networkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkUploadBytes;
+        } else if (networkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return networkDownloadBytes;
+        } else {
+            return networkDownloadBytes + networkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
      * this job, in bytes.
      *
-     * @return Estimated size of network traffic, or
+     * @return Estimated size of download traffic, or
      *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
-     * @see Builder#setEstimatedNetworkBytes(long)
+     * @see Builder#setEstimatedNetworkBytes(long, long)
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return networkBytes;
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return networkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of upload traffic, or
+     *         {@link #NETWORK_BYTES_UNKNOWN} when unknown.
+     * @see Builder#setEstimatedNetworkBytes(long, long)
+     */
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return networkUploadBytes;
     }
 
     /**
      * Set for a job that does not recur periodically, to specify a delay after which the job
      * will be eligible for execution. This value is not set if the job recurs periodically.
+     * @see JobInfo.Builder#setMinimumLatency(long)
      */
     public long getMinLatencyMillis() {
         return minLatencyMillis;
     }
 
     /**
-     * See {@link Builder#setOverrideDeadline(long)}. This value is not set if the job recurs
-     * periodically.
+     * @see JobInfo.Builder#setOverrideDeadline(long)
      */
     public long getMaxExecutionDelayMillis() {
         return maxExecutionDelayMillis;
@@ -495,13 +523,15 @@
 
     /**
      * Track whether this job will repeat with a given period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public boolean isPeriodic() {
         return isPeriodic;
     }
 
     /**
-     * @return Whether or not this job should be persisted across device reboots.
+     * @see JobInfo.Builder#setPersisted(boolean)
      */
     public boolean isPersisted() {
         return isPersisted;
@@ -510,6 +540,8 @@
     /**
      * Set to the interval between occurrences of this job. This value is <b>not</b> set if the
      * job does not recur periodically.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getIntervalMillis() {
         return intervalMillis;
@@ -518,6 +550,8 @@
     /**
      * Flex time for this job. Only valid if this is a periodic job.  The job can
      * execute at any time in a window of flex length at the end of the period.
+     * @see JobInfo.Builder#setPeriodic(long)
+     * @see JobInfo.Builder#setPeriodic(long, long)
      */
     public long getFlexMillis() {
         return flexMillis;
@@ -527,6 +561,7 @@
      * The amount of time the JobScheduler will wait before rescheduling a failed job. This value
      * will be increased depending on the backoff policy specified at job creation time. Defaults
      * to 30 seconds, minimum is currently 10 seconds.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public long getInitialBackoffMillis() {
         return initialBackoffMillis;
@@ -534,12 +569,27 @@
 
     /**
      * Return the backoff policy of this job.
+     * @see JobInfo.Builder#setBackoffCriteria(long, int)
      */
     public @BackoffPolicy int getBackoffPolicy() {
         return backoffPolicy;
     }
 
     /**
+     * @see JobInfo.Builder#setImportantWhileForeground(boolean)
+     */
+    public boolean isImportantWhileForeground() {
+        return (flags & FLAG_IMPORTANT_WHILE_FOREGROUND) != 0;
+    }
+
+    /**
+     * @see JobInfo.Builder#setPrefetch(boolean)
+     */
+    public boolean isPrefetch() {
+        return (flags & FLAG_PREFETCH) != 0;
+    }
+
+    /**
      * User can specify an early constraint of 0L, which is valid, so we keep track of whether the
      * function was called at all.
      * @hide
@@ -610,7 +660,10 @@
         if (!Objects.equals(networkRequest, j.networkRequest)) {
             return false;
         }
-        if (networkBytes != j.networkBytes) {
+        if (networkDownloadBytes != j.networkDownloadBytes) {
+            return false;
+        }
+        if (networkUploadBytes != j.networkUploadBytes) {
             return false;
         }
         if (minLatencyMillis != j.minLatencyMillis) {
@@ -673,7 +726,8 @@
         if (networkRequest != null) {
             hashCode = 31 * hashCode + networkRequest.hashCode();
         }
-        hashCode = 31 * hashCode + Long.hashCode(networkBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkDownloadBytes);
+        hashCode = 31 * hashCode + Long.hashCode(networkUploadBytes);
         hashCode = 31 * hashCode + Long.hashCode(minLatencyMillis);
         hashCode = 31 * hashCode + Long.hashCode(maxExecutionDelayMillis);
         hashCode = 31 * hashCode + Boolean.hashCode(isPeriodic);
@@ -708,7 +762,8 @@
         } else {
             networkRequest = null;
         }
-        networkBytes = in.readLong();
+        networkDownloadBytes = in.readLong();
+        networkUploadBytes = in.readLong();
         minLatencyMillis = in.readLong();
         maxExecutionDelayMillis = in.readLong();
         isPeriodic = in.readInt() == 1;
@@ -737,7 +792,8 @@
         triggerContentUpdateDelay = b.mTriggerContentUpdateDelay;
         triggerContentMaxDelay = b.mTriggerContentMaxDelay;
         networkRequest = b.mNetworkRequest;
-        networkBytes = b.mNetworkBytes;
+        networkDownloadBytes = b.mNetworkDownloadBytes;
+        networkUploadBytes = b.mNetworkUploadBytes;
         minLatencyMillis = b.mMinLatencyMillis;
         maxExecutionDelayMillis = b.mMaxExecutionDelayMillis;
         isPeriodic = b.mIsPeriodic;
@@ -780,7 +836,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(networkBytes);
+        out.writeLong(networkDownloadBytes);
+        out.writeLong(networkUploadBytes);
         out.writeLong(minLatencyMillis);
         out.writeLong(maxExecutionDelayMillis);
         out.writeInt(isPeriodic ? 1 : 0);
@@ -914,7 +971,8 @@
         // Requirements.
         private int mConstraintFlags;
         private NetworkRequest mNetworkRequest;
-        private long mNetworkBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        private long mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
         private ArrayList<TriggerContentUri> mTriggerContentUris;
         private long mTriggerContentUpdateDelay = -1;
         private long mTriggerContentMaxDelay = -1;
@@ -965,6 +1023,7 @@
         /**
          * Set optional extras. This is persisted, so we only allow primitive types.
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getExtras()
          */
         public Builder setExtras(@NonNull PersistableBundle extras) {
             mExtras = extras;
@@ -979,6 +1038,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.</p>
          *
          * @param extras Bundle containing extras you want the scheduler to hold on to for you.
+         * @see JobInfo#getTransientExtras()
          */
         public Builder setTransientExtras(@NonNull Bundle extras) {
             mTransientExtras = extras;
@@ -1006,6 +1066,8 @@
          * a combination of {@link android.content.Intent#FLAG_GRANT_READ_URI_PERMISSION},
          * {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
          * {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
+         * @see JobInfo#getClipData()
+         * @see JobInfo#getClipGrantFlags()
          */
         public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
             mClipData = clip;
@@ -1096,6 +1158,16 @@
         }
 
         /**
+         * @deprecated replaced by
+         *             {@link #setEstimatedNetworkBytes(long, long)}.
+         * @removed
+         */
+        @Deprecated
+        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
+            return setEstimatedNetworkBytes(networkBytes, NETWORK_BYTES_UNKNOWN);
+        }
+
+        /**
          * Set the estimated size of network traffic that will be performed by
          * this job, in bytes.
          * <p>
@@ -1112,23 +1184,30 @@
          * <li>A job that synchronizes email could end up using an extreme range
          * of data, from under 1KB when nothing has changed, to dozens of MB
          * when there are new emails with attachments. Jobs that cannot provide
-         * reasonable estimates should leave this estimated value undefined.
+         * reasonable estimates should use the sentinel value
+         * {@link JobInfo#NETWORK_BYTES_UNKNOWN}.
          * </ul>
          * Note that the system may choose to delay jobs with large network
          * usage estimates when the device has a poor network connection, in
          * order to save battery.
+         * <p>
+         * The values provided here only reflect the traffic that will be
+         * performed by the base job; if you're using {@link JobWorkItem} then
+         * you also need to define the network traffic used by each work item
+         * when constructing them.
          *
-         * @param networkBytes The estimated size of network traffic that will
-         *            be performed by this job, in bytes. This value only
-         *            reflects the traffic that will be performed by the base
-         *            job; if you're using {@link JobWorkItem} then you also
-         *            need to define the network traffic used by each work item
-         *            when constructing them.
-         * @see JobInfo#getEstimatedNetworkBytes()
-         * @see JobWorkItem#JobWorkItem(android.content.Intent, long)
+         * @param downloadBytes The estimated size of network traffic that will
+         *            be downloaded by this job, in bytes.
+         * @param uploadBytes The estimated size of network traffic that will be
+         *            uploaded by this job, in bytes.
+         * @see JobInfo#getEstimatedNetworkDownloadBytes()
+         * @see JobInfo#getEstimatedNetworkUploadBytes()
+         * @see JobWorkItem#JobWorkItem(android.content.Intent, long, long)
          */
-        public Builder setEstimatedNetworkBytes(@BytesLong long networkBytes) {
-            mNetworkBytes = networkBytes;
+        public Builder setEstimatedNetworkBytes(@BytesLong long downloadBytes,
+                @BytesLong long uploadBytes) {
+            mNetworkDownloadBytes = downloadBytes;
+            mNetworkUploadBytes = uploadBytes;
             return this;
         }
 
@@ -1146,6 +1225,7 @@
          *
          * @param requiresCharging Pass {@code true} to require that the device be
          *     charging in order to run the job.
+         * @see JobInfo#isRequireCharging()
          */
         public Builder setRequiresCharging(boolean requiresCharging) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_CHARGING)
@@ -1159,6 +1239,7 @@
          * is not low, which is generally the point where the user is given a "low battery"
          * warning.
          * @param batteryNotLow Whether or not the device's battery level must not be low.
+         * @see JobInfo#isRequireBatteryNotLow()
          */
         public Builder setRequiresBatteryNotLow(boolean batteryNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_BATTERY_NOT_LOW)
@@ -1183,6 +1264,7 @@
          *
          * @param requiresDeviceIdle Pass {@code true} to prevent the job from running
          *     while the device is being used interactively.
+         * @see JobInfo#isRequireDeviceIdle()
          */
         public Builder setRequiresDeviceIdle(boolean requiresDeviceIdle) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_DEVICE_IDLE)
@@ -1196,6 +1278,7 @@
          * in a low storage state, which is generally the point where the user is given a
          * "low storage" warning.
          * @param storageNotLow Whether or not the device's available storage must not be low.
+         * @see JobInfo#isRequireStorageNotLow()
          */
         public Builder setRequiresStorageNotLow(boolean storageNotLow) {
             mConstraintFlags = (mConstraintFlags&~CONSTRAINT_FLAG_STORAGE_NOT_LOW)
@@ -1228,6 +1311,7 @@
          *      job}
          *
          * @param uri The content: URI to monitor.
+         * @see JobInfo#getTriggerContentUris()
          */
         public Builder addTriggerContentUri(@NonNull TriggerContentUri uri) {
             if (mTriggerContentUris == null) {
@@ -1242,6 +1326,7 @@
          * the job is scheduled.  If there are more changes during that time, the delay
          * will be reset to start at the time of the most recent change.
          * @param durationMs Delay after most recent content change, in milliseconds.
+         * @see JobInfo#getTriggerContentUpdateDelay()
          */
         public Builder setTriggerContentUpdateDelay(long durationMs) {
             mTriggerContentUpdateDelay = durationMs;
@@ -1252,6 +1337,7 @@
          * Set the maximum total delay (in milliseconds) that is allowed from the first
          * time a content change is detected until the job is scheduled.
          * @param durationMs Delay after initial content change, in milliseconds.
+         * @see JobInfo#getTriggerContentMaxDelay()
          */
         public Builder setTriggerContentMaxDelay(long durationMs) {
             mTriggerContentMaxDelay = durationMs;
@@ -1265,6 +1351,8 @@
          * Setting this function on the builder with {@link #setMinimumLatency(long)} or
          * {@link #setOverrideDeadline(long)} will result in an error.
          * @param intervalMillis Millisecond interval for which this job will repeat.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis) {
             return setPeriodic(intervalMillis, intervalMillis);
@@ -1278,6 +1366,8 @@
          * @param flexMillis Millisecond flex for this job. Flex is clamped to be at least
          *                   {@link #getMinFlexMillis()} or 5 percent of the period, whichever is
          *                   higher.
+         * @see JobInfo#getIntervalMillis()
+         * @see JobInfo#getFlexMillis()
          */
         public Builder setPeriodic(long intervalMillis, long flexMillis) {
             final long minPeriod = getMinPeriodMillis();
@@ -1309,6 +1399,7 @@
          * {@link android.app.job.JobInfo.Builder#build()} is called.
          * @param minLatencyMillis Milliseconds before which this job will not be considered for
          *                         execution.
+         * @see JobInfo#getMinLatencyMillis()
          */
         public Builder setMinimumLatency(long minLatencyMillis) {
             mMinLatencyMillis = minLatencyMillis;
@@ -1322,6 +1413,7 @@
          * this property on a periodic job, doing so will throw an
          * {@link java.lang.IllegalArgumentException} when
          * {@link android.app.job.JobInfo.Builder#build()} is called.
+         * @see JobInfo#getMaxExecutionDelayMillis()
          */
         public Builder setOverrideDeadline(long maxExecutionDelayMillis) {
             mMaxExecutionDelayMillis = maxExecutionDelayMillis;
@@ -1341,6 +1433,8 @@
          * mode.
          * @param initialBackoffMillis Millisecond time interval to wait initially when job has
          *                             failed.
+         * @see JobInfo#getInitialBackoffMillis()
+         * @see JobInfo#getBackoffPolicy()
          */
         public Builder setBackoffCriteria(long initialBackoffMillis,
                 @BackoffPolicy int backoffPolicy) {
@@ -1371,6 +1465,7 @@
          *
          * @param importantWhileForeground whether to relax doze restrictions for this job when the
          *                                 app is in the foreground. False by default.
+         * @see JobInfo#isImportantWhileForeground()
          */
         public Builder setImportantWhileForeground(boolean importantWhileForeground) {
             if (importantWhileForeground) {
@@ -1382,6 +1477,15 @@
         }
 
         /**
+         * @removed
+         * @deprecated replaced with {@link #setPrefetch(boolean)}
+         */
+        @Deprecated
+        public Builder setIsPrefetch(boolean isPrefetch) {
+            return setPrefetch(isPrefetch);
+        }
+
+        /**
          * Setting this to true indicates that this job is designed to prefetch
          * content that will make a material improvement to the experience of
          * the specific user of this device. For example, fetching top headlines
@@ -1393,12 +1497,13 @@
          * network when there is a surplus of metered data available. The system
          * may also use this signal in combination with end user usage patterns
          * to ensure data is prefetched before the user launches your app.
+         * @see JobInfo#isPrefetch()
          */
-        public Builder setIsPrefetch(boolean isPrefetch) {
-            if (isPrefetch) {
-                mFlags |= FLAG_IS_PREFETCH;
+        public Builder setPrefetch(boolean prefetch) {
+            if (prefetch) {
+                mFlags |= FLAG_PREFETCH;
             } else {
-                mFlags &= (~FLAG_IS_PREFETCH);
+                mFlags &= (~FLAG_PREFETCH);
             }
             return this;
         }
@@ -1408,6 +1513,7 @@
          *
          * @param isPersisted True to indicate that the job will be written to
          *            disk and loaded at boot.
+         * @see JobInfo#isPersisted()
          */
         @RequiresPermission(android.Manifest.permission.RECEIVE_BOOT_COMPLETED)
         public Builder setPersisted(boolean isPersisted) {
@@ -1427,7 +1533,7 @@
                         "constraints, this is not allowed.");
             }
             // Check that network estimates require network type
-            if (mNetworkBytes > 0 && mNetworkRequest == null) {
+            if ((mNetworkDownloadBytes > 0 || mNetworkUploadBytes > 0) && mNetworkRequest == null) {
                 throw new IllegalArgumentException(
                         "Can't provide estimated network usage without requiring a network");
             }
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 1c46e8e..995f522 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -16,6 +16,8 @@
 
 package android.app.job;
 
+import static android.app.job.JobInfo.NETWORK_BYTES_UNKNOWN;
+
 import android.annotation.BytesLong;
 import android.content.Intent;
 import android.os.Parcel;
@@ -28,7 +30,8 @@
  */
 final public class JobWorkItem implements Parcelable {
     final Intent mIntent;
-    final long mNetworkBytes;
+    final long mNetworkDownloadBytes;
+    final long mNetworkUploadBytes;
     int mDeliveryCount;
     int mWorkId;
     Object mGrants;
@@ -41,22 +44,36 @@
      */
     public JobWorkItem(Intent intent) {
         mIntent = intent;
-        mNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
+        mNetworkDownloadBytes = NETWORK_BYTES_UNKNOWN;
+        mNetworkUploadBytes = NETWORK_BYTES_UNKNOWN;
+    }
+
+    /**
+     * @deprecated replaced by {@link #JobWorkItem(Intent, long, long)}
+     * @removed
+     */
+    @Deprecated
+    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+        this(intent, networkBytes, NETWORK_BYTES_UNKNOWN);
     }
 
     /**
      * Create a new piece of work, which can be submitted to
      * {@link JobScheduler#enqueue JobScheduler.enqueue}.
+     * <p>
+     * See {@link JobInfo.Builder#setEstimatedNetworkBytes(long, long)} for
+     * details about how to estimate network traffic.
      *
      * @param intent The general Intent describing this work.
-     * @param networkBytes The estimated size of network traffic that will be
-     *            performed by this job work item, in bytes. See
-     *            {@link JobInfo.Builder#setEstimatedNetworkBytes(long)} for
-     *            details about how to estimate.
+     * @param downloadBytes The estimated size of network traffic that will be
+     *            downloaded by this job work item, in bytes.
+     * @param uploadBytes The estimated size of network traffic that will be
+     *            uploaded by this job work item, in bytes.
      */
-    public JobWorkItem(Intent intent, @BytesLong long networkBytes) {
+    public JobWorkItem(Intent intent, @BytesLong long downloadBytes, @BytesLong long uploadBytes) {
         mIntent = intent;
-        mNetworkBytes = networkBytes;
+        mNetworkDownloadBytes = downloadBytes;
+        mNetworkUploadBytes = uploadBytes;
     }
 
     /**
@@ -67,14 +84,44 @@
     }
 
     /**
-     * Return the estimated size of network traffic that will be performed by
+     * @deprecated replaced by {@link #getEstimatedNetworkDownloadBytes()} and
+     *             {@link #getEstimatedNetworkUploadBytes()}.
+     * @removed
+     */
+    @Deprecated
+    public @BytesLong long getEstimatedNetworkBytes() {
+        if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN
+                && mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return NETWORK_BYTES_UNKNOWN;
+        } else if (mNetworkDownloadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkUploadBytes;
+        } else if (mNetworkUploadBytes == NETWORK_BYTES_UNKNOWN) {
+            return mNetworkDownloadBytes;
+        } else {
+            return mNetworkDownloadBytes + mNetworkUploadBytes;
+        }
+    }
+
+    /**
+     * Return the estimated size of download traffic that will be performed by
+     * this job, in bytes.
+     *
+     * @return Estimated size of download traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
+     */
+    public @BytesLong long getEstimatedNetworkDownloadBytes() {
+        return mNetworkDownloadBytes;
+    }
+
+    /**
+     * Return the estimated size of upload traffic that will be performed by
      * this job work item, in bytes.
      *
-     * @return estimated size, or {@link JobInfo#NETWORK_BYTES_UNKNOWN} when
-     *         unknown.
+     * @return Estimated size of upload traffic, or
+     *         {@link JobInfo#NETWORK_BYTES_UNKNOWN} when unknown.
      */
-    public @BytesLong long getEstimatedNetworkBytes() {
-        return mNetworkBytes;
+    public @BytesLong long getEstimatedNetworkUploadBytes() {
+        return mNetworkUploadBytes;
     }
 
     /**
@@ -128,9 +175,13 @@
         sb.append(mWorkId);
         sb.append(" intent=");
         sb.append(mIntent);
-        if (mNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
-            sb.append(" networkBytes=");
-            sb.append(mNetworkBytes);
+        if (mNetworkDownloadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" downloadBytes=");
+            sb.append(mNetworkDownloadBytes);
+        }
+        if (mNetworkUploadBytes != NETWORK_BYTES_UNKNOWN) {
+            sb.append(" uploadBytes=");
+            sb.append(mNetworkUploadBytes);
         }
         if (mDeliveryCount != 0) {
             sb.append(" dcount=");
@@ -151,7 +202,8 @@
         } else {
             out.writeInt(0);
         }
-        out.writeLong(mNetworkBytes);
+        out.writeLong(mNetworkDownloadBytes);
+        out.writeLong(mNetworkUploadBytes);
         out.writeInt(mDeliveryCount);
         out.writeInt(mWorkId);
     }
@@ -173,7 +225,8 @@
         } else {
             mIntent = null;
         }
-        mNetworkBytes = in.readLong();
+        mNetworkDownloadBytes = in.readLong();
+        mNetworkUploadBytes = in.readLong();
         mDeliveryCount = in.readInt();
         mWorkId = in.readInt();
     }
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 7e66fd7..01b13a2 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -26,7 +26,7 @@
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
 
-import android.app.ActivityThread;
+import android.app.ActivityThread.ActivityClientRecord;
 import android.util.IntArray;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -124,7 +124,7 @@
      *         {@link ActivityLifecycleItem#UNDEFINED} if there is not path.
      */
     @VisibleForTesting
-    public int getClosestPreExecutionState(ActivityThread.ActivityClientRecord r,
+    public int getClosestPreExecutionState(ActivityClientRecord r,
             int postExecutionState) {
         switch (postExecutionState) {
             case UNDEFINED:
@@ -147,7 +147,7 @@
      *         were provided or there is not path.
      */
     @VisibleForTesting
-    public int getClosestOfStates(ActivityThread.ActivityClientRecord r, int[] finalStates) {
+    public int getClosestOfStates(ActivityClientRecord r, int[] finalStates) {
         if (finalStates == null || finalStates.length == 0) {
             return UNDEFINED;
         }
@@ -168,6 +168,27 @@
         return closestState;
     }
 
+    /** Get the lifecycle state request to match the current state in the end of a transaction. */
+    public static ActivityLifecycleItem getLifecycleRequestForCurrentState(ActivityClientRecord r) {
+        final int prevState = r.getLifecycleState();
+        final ActivityLifecycleItem lifecycleItem;
+        switch (prevState) {
+            // TODO(lifecycler): Extend to support all possible states.
+            case ON_PAUSE:
+                lifecycleItem = PauseActivityItem.obtain();
+                break;
+            case ON_STOP:
+                lifecycleItem = StopActivityItem.obtain(r.isVisibleFromServer(),
+                        0 /* configChanges */);
+                break;
+            default:
+                lifecycleItem = ResumeActivityItem.obtain(false /* isForward */);
+                break;
+        }
+
+        return lifecycleItem;
+    }
+
     /**
      * Check if there is a destruction involved in the path. We want to avoid a lifecycle sequence
      * that involves destruction and recreation if there is another path.
diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl
index 74e3c3e..a2aaf12 100644
--- a/core/java/android/app/slice/ISliceManager.aidl
+++ b/core/java/android/app/slice/ISliceManager.aidl
@@ -25,7 +25,8 @@
     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);
+    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);
 
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 61679cb..fc3b38d 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -66,8 +66,11 @@
             HINT_HORIZONTAL,
             HINT_PARTIAL,
             HINT_SEE_MORE,
-            HINT_KEY_WORDS,
+            HINT_KEYWORDS,
             HINT_ERROR,
+            HINT_TTL,
+            HINT_LAST_UPDATED,
+            HINT_PERMISSION_REQUEST,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface SliceHint {}
@@ -168,12 +171,25 @@
      * related to the parent slice.
      * Expected to be on an item of format {@link SliceItem#FORMAT_SLICE}.
      */
-    public static final String HINT_KEY_WORDS = "key_words";
+    public static final String HINT_KEYWORDS = "keywords";
     /**
      * A hint to indicate that this slice represents an error.
      */
     public static final String HINT_ERROR = "error";
     /**
+     * Hint indicating an item representing a time-to-live for the content.
+     */
+    public static final String HINT_TTL = "ttl";
+    /**
+     * Hint indicating an item representing when the content was created or last updated.
+     */
+    public static final String HINT_LAST_UPDATED = "last_updated";
+    /**
+     * A hint to indicate that this slice represents a permission request for showing
+     * slices.
+     */
+    public static final String HINT_PERMISSION_REQUEST = "permission_request";
+    /**
      * Key to retrieve an extra added to an intent when a control is changed.
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
@@ -243,6 +259,11 @@
      * Expected to be on an item of format {@link SliceItem#FORMAT_TEXT}.
      */
     public static final String SUBTYPE_CONTENT_DESCRIPTION = "content_description";
+    /**
+     * Subtype to tag an item as representing a time in milliseconds since midnight,
+     * January 1, 1970 UTC.
+     */
+    public static final String SUBTYPE_MILLIS = "millis";
 
     private final SliceItem[] mItems;
     private final @SliceHint String[] mHints;
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 1afe53d..d5a98a5 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -81,6 +81,17 @@
      * An activity can be statically linked to a slice uri by including a meta-data item
      * for this key that contains a valid slice uri for the same application declaring
      * the activity.
+     *
+     * <pre class="prettyprint">
+     * {@literal
+     * <activity android:name="com.example.mypkg.MyActivity">
+     *     <meta-data android:name="android.metadata.SLICE_URI"
+     *                android:value="content://com.example.mypkg/main_slice" />
+     *  </activity>}
+     * </pre>
+     *
+     * @see #mapIntentToUri(Intent)
+     * @see SliceProvider#onMapIntentToUri(Intent)
      */
     public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
 
@@ -257,16 +268,17 @@
      * <p>
      * This goes through a several stage resolution process to determine if any slice
      * can represent this intent.
-     *  - If the intent contains data that {@link ContentResolver#getType} is
-     *  {@link SliceProvider#SLICE_TYPE} then the data will be returned.
-     *  - If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then
+     * <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.
-     *  - Lastly, if the intent explicitly points at an activity, and that activity has
+     *  will be returned.</li>
+     *  <li>Lastly, 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.
-     *  - If no slice is found, then {@code null} is returned.
-     *
+     *  returned.</li>
+     *  <li>If no slice is found, then {@code null} is returned.</li>
+     * </ol>
      * @param intent The intent associated with a slice.
      * @return The Slice Uri provided by the app or null if none exists.
      * @see Slice
@@ -392,7 +404,8 @@
      * Does the permission check to see if a caller has access to a specific slice.
      * @hide
      */
-    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) {
+    public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) {
         try {
             if (UserHandle.isSameApp(uid, Process.myUid())) {
                 return;
@@ -400,7 +413,7 @@
             if (pkg == null) {
                 throw new SecurityException("No pkg specified");
             }
-            int result = mService.checkSlicePermission(uri, pkg, pid, uid);
+            int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions);
             if (result == PERMISSION_DENIED) {
                 throw new SecurityException("User " + uid + " does not have slice permission for "
                         + uri + ".");
@@ -412,6 +425,8 @@
                         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/SliceMetrics.java b/core/java/android/app/slice/SliceMetrics.java
index a7069bc..20c1390 100644
--- a/core/java/android/app/slice/SliceMetrics.java
+++ b/core/java/android/app/slice/SliceMetrics.java
@@ -25,7 +25,7 @@
 /**
  * Metrics interface for slices.
  *
- * This is called by SliceView, so Slice develoers should
+ * This is called by SliceView, so Slice developers should
  * not need to reference this class.
  *
  * @see androidx.slice.widget.SliceView
@@ -55,9 +55,18 @@
     }
 
     /**
-     * To be called whenever the use interacts with a slice.
-     *@param subSlice The URI of the sub-slice that is the subject of the interaction.
+     * To be called whenever the user invokes a discrete action via a slice.
+     *
+     * <P>
+     *     Use this for discrete events like a tap or the end of a drag,
+     *     not for a continuous streams of events, such as the motion during a gesture.
+     * </P>
+     *
+     * @see androidx.slice.widget.EventInfo#actionType
+     *
+     * @param actionType The type of the event.
+     * @param subSlice The URI of the sub-slice that is the subject of the interaction.
      */
-    public void logTouch(@NonNull Uri subSlice) {
+    public void logTouch(int actionType, @NonNull Uri subSlice) {
     }
 }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index bf856b7..fe5742d 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -149,10 +149,31 @@
     private static final boolean DEBUG = false;
 
     private static final long SLICE_BIND_ANR = 2000;
+    private final String[] mAutoGrantPermissions;
 
     private String mCallback;
     private SliceManager mSliceManager;
 
+    /**
+     * A version of constructing a SliceProvider that allows autogranting slice permissions
+     * to apps that hold specific platform permissions.
+     * <p>
+     * When an app tries to bind a slice from this provider that it does not have access to,
+     * This provider will check if the caller holds permissions to any of the autoGrantPermissions
+     * specified, if they do they will be granted persisted uri access to all slices of this
+     * provider.
+     *
+     * @param autoGrantPermissions List of permissions that holders are auto-granted access
+     *                             to slices.
+     */
+    public SliceProvider(@NonNull String... autoGrantPermissions) {
+        mAutoGrantPermissions = autoGrantPermissions;
+    }
+
+    public SliceProvider() {
+        mAutoGrantPermissions = new String[0];
+    }
+
     @Override
     public void attachInfo(Context context, ProviderInfo info) {
         super.attachInfo(context, info);
@@ -402,7 +423,7 @@
                 : getContext().getPackageManager().getNameForUid(callingUid);
         try {
             mSliceManager.enforceSlicePermission(sliceUri, pkg,
-                    callingPid, callingUid);
+                    callingPid, callingUid, mAutoGrantPermissions);
         } catch (SecurityException e) {
             return createPermissionSlice(getContext(), sliceUri, pkg);
         }
@@ -428,15 +449,17 @@
         } finally {
             Handler.getMain().removeCallbacks(mAnr);
         }
-        return new Slice.Builder(sliceUri)
-                .addAction(action,
-                        new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
-                                .addText(getPermissionString(context, callingPackage), null,
-                                        Collections.emptyList())
-                                .build(),
-                        null)
-                .addHints(Arrays.asList(Slice.HINT_LIST_ITEM))
-                .build();
+        Slice.Builder parent = new Slice.Builder(sliceUri);
+        Slice.Builder childAction = new Slice.Builder(parent)
+                .addHints(Arrays.asList(Slice.HINT_TITLE, Slice.HINT_SHORTCUT))
+                .addAction(action, new Slice.Builder(parent).build(), null);
+
+        parent.addSubSlice(new Slice.Builder(sliceUri.buildUpon().appendPath("permission").build())
+                .addText(getPermissionString(context, callingPackage), null,
+                        Collections.emptyList())
+                .addSubSlice(childAction.build(), null)
+                .build(), null);
+        return parent.addHints(Arrays.asList(Slice.HINT_PERMISSION_REQUEST)).build();
     }
 
     /**
diff --git a/core/java/android/app/usage/EventStats.java b/core/java/android/app/usage/EventStats.java
new file mode 100644
index 0000000..b799de9
--- /dev/null
+++ b/core/java/android/app/usage/EventStats.java
@@ -0,0 +1,182 @@
+/**
+ * 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.app.usage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Contains usage statistics for an event type for a specific
+ * time range.
+ */
+public final class EventStats implements Parcelable {
+
+    /**
+     * {@hide}
+     */
+    public int mEventType;
+
+    /**
+     * {@hide}
+     */
+    public long mBeginTimeStamp;
+
+    /**
+     * {@hide}
+     */
+    public long mEndTimeStamp;
+
+    /**
+     * Last time used by the user with an explicit action (notification, activity launch).
+     * {@hide}
+     */
+    public long mLastTimeUsed;
+
+    /**
+     * {@hide}
+     */
+    public long mTotalTime;
+
+    /**
+     * {@hide}
+     */
+    public int mCount;
+
+    /**
+     * {@hide}
+     */
+    public EventStats() {
+    }
+
+    public EventStats(EventStats stats) {
+        mEventType = stats.mEventType;
+        mBeginTimeStamp = stats.mBeginTimeStamp;
+        mEndTimeStamp = stats.mEndTimeStamp;
+        mLastTimeUsed = stats.mLastTimeUsed;
+        mTotalTime = stats.mTotalTime;
+        mCount = stats.mCount;
+    }
+
+    /**
+     * Return the type of event this is usage for.  May be one of the event
+     * constants in {@link UsageEvents.Event}.
+     */
+    public int getEventType() {
+        return mEventType;
+    }
+
+    /**
+     * Get the beginning of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getFirstTimeStamp() {
+        return mBeginTimeStamp;
+    }
+
+    /**
+     * Get the end of the time range this {@link android.app.usage.EventStats} represents,
+     * measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeStamp() {
+        return mEndTimeStamp;
+    }
+
+    /**
+     * Get the last time this event was used, measured in milliseconds since the epoch.
+     * <p/>
+     * See {@link System#currentTimeMillis()}.
+     */
+    public long getLastTimeUsed() {
+        return mLastTimeUsed;
+    }
+
+    /**
+     * Return the number of times that this event occurred over the interval.
+     */
+    public int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Get the total time this event was active, measured in milliseconds.
+     */
+    public long getTotalTime() {
+        return mTotalTime;
+    }
+
+    /**
+     * Add the statistics from the right {@link EventStats} to the left. The event type for
+     * both {@link UsageStats} objects must be the same.
+     * @param right The {@link EventStats} object to merge into this one.
+     * @throws java.lang.IllegalArgumentException if the event types of the two
+     *         {@link UsageStats} objects are different.
+     */
+    public void add(EventStats right) {
+        if (mEventType != right.mEventType) {
+            throw new IllegalArgumentException("Can't merge EventStats for event #"
+                    + mEventType + " with EventStats for event #" + right.mEventType);
+        }
+
+        // We use the mBeginTimeStamp due to a bug where UsageStats files can overlap with
+        // regards to their mEndTimeStamp.
+        if (right.mBeginTimeStamp > mBeginTimeStamp) {
+            mLastTimeUsed = Math.max(mLastTimeUsed, right.mLastTimeUsed);
+        }
+        mBeginTimeStamp = Math.min(mBeginTimeStamp, right.mBeginTimeStamp);
+        mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
+        mTotalTime += right.mTotalTime;
+        mCount += right.mCount;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mEventType);
+        dest.writeLong(mBeginTimeStamp);
+        dest.writeLong(mEndTimeStamp);
+        dest.writeLong(mLastTimeUsed);
+        dest.writeLong(mTotalTime);
+        dest.writeInt(mCount);
+    }
+
+    public static final Creator<EventStats> CREATOR = new Creator<EventStats>() {
+        @Override
+        public EventStats createFromParcel(Parcel in) {
+            EventStats stats = new EventStats();
+            stats.mEventType = in.readInt();
+            stats.mBeginTimeStamp = in.readLong();
+            stats.mEndTimeStamp = in.readLong();
+            stats.mLastTimeUsed = in.readLong();
+            stats.mTotalTime = in.readLong();
+            stats.mCount = in.readInt();
+            return stats;
+        }
+
+        @Override
+        public EventStats[] newArray(int size) {
+            return new EventStats[size];
+        }
+    };
+}
diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl
index d52bd37..00d8711 100644
--- a/core/java/android/app/usage/IUsageStatsManager.aidl
+++ b/core/java/android/app/usage/IUsageStatsManager.aidl
@@ -32,6 +32,8 @@
             String callingPackage);
     ParceledListSlice queryConfigurationStats(int bucketType, long beginTime, long endTime,
             String callingPackage);
+    ParceledListSlice queryEventStats(int bucketType, long beginTime, long endTime,
+            String callingPackage);
     UsageEvents queryEvents(long beginTime, long endTime, String callingPackage);
     UsageEvents queryEventsForPackage(long beginTime, long endTime, String callingPackage);
     void setAppInactive(String packageName, boolean inactive, int userId);
diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java
index da36157..e315e91 100644
--- a/core/java/android/app/usage/NetworkStats.java
+++ b/core/java/android/app/usage/NetworkStats.java
@@ -24,7 +24,6 @@
 import android.net.NetworkTemplate;
 import android.net.TrafficStats;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.util.IntArray;
 import android.util.Log;
 
@@ -98,9 +97,8 @@
 
     /** @hide */
     NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
-            long endTimestamp) throws RemoteException, SecurityException {
-        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+            long endTimestamp, INetworkStatsService statsService)
+            throws RemoteException, SecurityException {
         // Open network stats session
         mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
         mCloseGuard.open("close");
diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java
index 5576e86..2357637 100644
--- a/core/java/android/app/usage/NetworkStatsManager.java
+++ b/core/java/android/app/usage/NetworkStatsManager.java
@@ -37,6 +37,8 @@
 import android.os.ServiceManager.ServiceNotFoundException;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Provides access to network usage history and statistics. Usage data is collected in
  * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
@@ -107,9 +109,15 @@
      * {@hide}
      */
     public NetworkStatsManager(Context context) throws ServiceNotFoundException {
+        this(context, INetworkStatsService.Stub.asInterface(
+                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
+    }
+
+    /** @hide */
+    @VisibleForTesting
+    public NetworkStatsManager(Context context, INetworkStatsService service) {
         mContext = context;
-        mService = INetworkStatsService.Stub.asInterface(
-                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+        mService = service;
         setPollOnOpen(true);
     }
 
@@ -135,7 +143,8 @@
     public Bucket querySummaryForDevice(NetworkTemplate template,
             long startTime, long endTime) throws SecurityException, RemoteException {
         Bucket bucket = null;
-        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
+                mService);
         bucket = stats.getDeviceSummaryForNetwork();
 
         stats.close();
@@ -208,7 +217,7 @@
         }
 
         NetworkStats stats;
-        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         stats.startSummaryEnumeration();
 
         stats.close();
@@ -245,7 +254,7 @@
         }
 
         NetworkStats result;
-        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         result.startSummaryEnumeration();
 
         return result;
@@ -295,7 +304,7 @@
 
         NetworkStats result;
         try {
-            result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+            result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
             result.startHistoryEnumeration(uid, tag);
         } catch (RemoteException e) {
             Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
@@ -341,7 +350,7 @@
         }
 
         NetworkStats result;
-        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
+        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
         result.startUserUidEnumeration();
         return result;
     }
@@ -451,19 +460,20 @@
     }
 
     private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
-        NetworkTemplate template = null;
+        final NetworkTemplate template;
         switch (networkType) {
-            case ConnectivityManager.TYPE_MOBILE: {
-                template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
-                } break;
-            case ConnectivityManager.TYPE_WIFI: {
+            case ConnectivityManager.TYPE_MOBILE:
+                template = subscriberId == null
+                        ? NetworkTemplate.buildTemplateMobileWildcard()
+                        : NetworkTemplate.buildTemplateMobileAll(subscriberId);
+                break;
+            case ConnectivityManager.TYPE_WIFI:
                 template = NetworkTemplate.buildTemplateWifiWildcard();
-                } break;
-            default: {
+                break;
+            default:
                 throw new IllegalArgumentException("Cannot create template for network type "
                         + networkType + ", subscriberId '"
                         + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
-            }
         }
         return template;
     }
diff --git a/core/java/android/app/usage/TimeSparseArray.java b/core/java/android/app/usage/TimeSparseArray.java
index 5764fa8..9ef88e4 100644
--- a/core/java/android/app/usage/TimeSparseArray.java
+++ b/core/java/android/app/usage/TimeSparseArray.java
@@ -81,12 +81,17 @@
     @Override
     public void put(long key, E value) {
         final long origKey = key;
-        while (indexOfKey(key) >= 0) {
-            key++;
-        }
-        if (origKey != key) {
-            Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
-                    + " displaced to " + key);
+        int keyIndex = indexOfKey(key);
+        if (keyIndex >= 0) {
+            final long sz = size();
+            while (keyIndex < sz && keyAt(keyIndex) == key) {
+                key++;
+                keyIndex++;
+            }
+            if (key >= origKey + 10) {
+                Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
+                        + " displaced to " + key);
+            }
         }
         super.put(key, value);
     }
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java
index b354e81..a665652 100644
--- a/core/java/android/app/usage/UsageEvents.java
+++ b/core/java/android/app/usage/UsageEvents.java
@@ -139,6 +139,19 @@
         @SystemApi
         public static final int SLICE_PINNED = 14;
 
+        /**
+         * An event type denoting that the screen has gone in to an interactive state (turned
+         * on for full user interaction, not ambient display or other non-interactive state).
+         */
+        public static final int SCREEN_INTERACTIVE = 15;
+
+        /**
+         * An event type denoting that the screen has gone in to a non-interactive state
+         * (completely turned off or turned on only in a non-interactive state like ambient
+         * display).
+         */
+        public static final int SCREEN_NON_INTERACTIVE = 16;
+
         /** @hide */
         public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0;
 
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 4744147..eafe91a 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -179,6 +179,16 @@
     public static final int REASON_SUB_USAGE_ACTIVE_TIMEOUT     = 0x0007;
     /** @hide */
     public static final int REASON_SUB_USAGE_SYNC_ADAPTER       = 0x0008;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SLICE_PINNED       = 0x0009;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_SLICE_PINNED_PRIV  = 0x000A;
+    /** @hide */
+    public static final int REASON_SUB_USAGE_EXEMPTED_SYNC_START = 0x000B;
+
+    /** @hide */
+    public static final int REASON_SUB_PREDICTED_RESTORED       = 0x0001;
+
 
     /** @hide */
     @IntDef(flag = false, prefix = { "STANDBY_BUCKET_" }, value = {
@@ -301,6 +311,44 @@
     }
 
     /**
+     * Gets aggregated event stats for the given time range, aggregated by the specified interval.
+     * <p>The returned list will contain a {@link EventStats} object for each event type that
+     * is being aggregated and has data for an interval that is a subset of the time range given.
+     *
+     * <p>The current event types that will be aggregated here are:</p>
+     * <ul>
+     *     <li>{@link UsageEvents.Event#SCREEN_INTERACTIVE}</li>
+     *     <li>{@link UsageEvents.Event#SCREEN_NON_INTERACTIVE}</li>
+     * </ul>
+     *
+     * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
+     *
+     * @param intervalType The time interval by which the stats are aggregated.
+     * @param beginTime The inclusive beginning of the range of stats to include in the results.
+     * @param endTime The exclusive end of the range of stats to include in the results.
+     * @return A list of {@link EventStats}
+     *
+     * @see #INTERVAL_DAILY
+     * @see #INTERVAL_WEEKLY
+     * @see #INTERVAL_MONTHLY
+     * @see #INTERVAL_YEARLY
+     * @see #INTERVAL_BEST
+     */
+    public List<EventStats> queryEventStats(int intervalType, long beginTime, long endTime) {
+        try {
+            @SuppressWarnings("unchecked")
+            ParceledListSlice<EventStats> slice = mService.queryEventStats(intervalType, beginTime,
+                    endTime, mContext.getOpPackageName());
+            if (slice != null) {
+                return slice.getList();
+            }
+        } catch (RemoteException e) {
+            // fallthrough and return the empty list.
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * Query for events in the given time range. Events are only kept by the system for a few
      * days.
      * <p> The caller must have {@link android.Manifest.permission#PACKAGE_USAGE_STATS} </p>
@@ -578,36 +626,50 @@
                 break;
             case REASON_MAIN_PREDICTED:
                 sb.append("p");
+                switch (standbyReason & REASON_SUB_MASK) {
+                    case REASON_SUB_PREDICTED_RESTORED:
+                        sb.append("-r");
+                        break;
+                }
                 break;
             case REASON_MAIN_TIMEOUT:
                 sb.append("t");
                 break;
             case REASON_MAIN_USAGE:
-                sb.append("u-");
+                sb.append("u");
                 switch (standbyReason & REASON_SUB_MASK) {
                     case REASON_SUB_USAGE_SYSTEM_INTERACTION:
-                        sb.append("si");
+                        sb.append("-si");
                         break;
                     case REASON_SUB_USAGE_NOTIFICATION_SEEN:
-                        sb.append("ns");
+                        sb.append("-ns");
                         break;
                     case REASON_SUB_USAGE_USER_INTERACTION:
-                        sb.append("ui");
+                        sb.append("-ui");
                         break;
                     case REASON_SUB_USAGE_MOVE_TO_FOREGROUND:
-                        sb.append("mf");
+                        sb.append("-mf");
                         break;
                     case REASON_SUB_USAGE_MOVE_TO_BACKGROUND:
-                        sb.append("mb");
+                        sb.append("-mb");
                         break;
                     case REASON_SUB_USAGE_SYSTEM_UPDATE:
-                        sb.append("su");
+                        sb.append("-su");
                         break;
                     case REASON_SUB_USAGE_ACTIVE_TIMEOUT:
-                        sb.append("at");
+                        sb.append("-at");
                         break;
                     case REASON_SUB_USAGE_SYNC_ADAPTER:
-                        sb.append("sa");
+                        sb.append("-sa");
+                        break;
+                    case REASON_SUB_USAGE_SLICE_PINNED:
+                        sb.append("slp");
+                        break;
+                    case REASON_SUB_USAGE_SLICE_PINNED_PRIV:
+                        sb.append("slpp");
+                        break;
+                    case REASON_SUB_USAGE_EXEMPTED_SYNC_START:
+                        sb.append("es");
                         break;
                 }
                 break;
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 09ced26..b8628a4 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -243,4 +243,12 @@
      */
     public abstract void reportAppJobState(String packageName, @UserIdInt int userId,
             int numDeferredJobs, long timeSinceLastJobRun);
+
+    /**
+     * Report a sync that was scheduled by an active app is about to be executed.
+     *
+     * @param packageName name of the package that owns the sync adapter.
+     * @param userId which user the app is associated with
+     */
+    public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);
 }
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index cb1d106..af99bf7 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -701,29 +701,6 @@
     }
 
     /**
-     * Sends Virtual Cable Unplug to currently connected host.
-     *
-     * @return
-     * {@hide}
-     */
-    public boolean unplug(BluetoothDevice device) {
-        boolean result = false;
-
-        final IBluetoothHidDevice service = mService;
-        if (service != null) {
-            try {
-                result = service.unplug(device);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
-        } else {
-            Log.w(TAG, "Proxy not attached to service");
-        }
-
-        return result;
-    }
-
-    /**
      * Initiates connection to host which is currently paired with this device. If the application
      * is not registered, #connect(BluetoothDevice) will fail. The connection state should be
      * tracked by the application by handling callback from Callback#onConnectionStateChanged. The
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index 440103a..9f3df37 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -166,24 +166,13 @@
     public static final String SYNC_EXTRAS_DISALLOW_METERED = "allow_metered";
 
     /**
-     * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
-     * a foreground app.
+     * {@hide} Integer extra containing a SyncExemption flag.
      *
      * Only the system and the shell user can set it.
      *
      * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
      */
-    public static final String SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC = "force_fg_sync";
-
-    /**
-     * {@hide} Flag only used by the requestsync command to treat a request as if it was made by
-     * a background app.
-     *
-     * Only the system and the shell user can set it.
-     *
-     * This extra is "virtual". Once passed to the system server, it'll be removed from the bundle.
-     */
-    public static final String SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC = "force_bg_sync";
+    public static final String SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG = "v_exemption";
 
     /**
      * Set by the SyncManager to request that the SyncAdapter initialize itself for
@@ -525,6 +514,38 @@
      */
     public static final int NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS = 1<<1;
 
+    /**
+     * No exception, throttled by app standby normally.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_NONE = 0;
+
+    /**
+     * When executing a sync with this exemption, we'll put the target app in the ACTIVE bucket
+     * for 10 minutes. This will allow the sync adapter to schedule/run further syncs and jobs.
+     *
+     * Note this will still *not* let RARE apps to run syncs, because they still won't get network
+     * connection.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_ACTIVE = 1;
+
+    /**
+     * In addition to {@link #SYNC_EXEMPTION_ACTIVE}, we put the sync adapter app in the
+     * temp whitelist for 10 minutes, so that even RARE apps can run syncs right away.
+     * @hide
+     */
+    public static final int SYNC_EXEMPTION_ACTIVE_WITH_TEMP = 2;
+
+    /** @hide */
+    @IntDef(flag = false, prefix = { "SYNC_EXEMPTION_" }, value = {
+            SYNC_EXEMPTION_NONE,
+            SYNC_EXEMPTION_ACTIVE,
+            SYNC_EXEMPTION_ACTIVE_WITH_TEMP,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SyncExemption {}
+
     // Always log queries which take 500ms+; shorter queries are
     // sampled accordingly.
     private static final boolean ENABLE_CONTENT_SAMPLE = false;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index ce32278..d4ec5d5 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -266,8 +266,8 @@
  * </ul>
  *
  * <p>For example, consider the Note Pad sample application that
- * allows user to browse through a list of notes data and view details about
- * individual items.  Text in italics indicate places were you would replace a
+ * allows a user to browse through a list of notes data and view details about
+ * individual items.  Text in italics indicates places where you would replace a
  * name with one specific to your own package.</p>
  *
  * <pre> &lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -1814,6 +1814,17 @@
     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}.
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    public static final String EXTRA_SUSPENDED_PACKAGE_EXTRAS = "android.intent.extra.SUSPENDED_PACKAGE_EXTRAS";
+
+    /**
      * Intent extra: An app split name.
      * <p>
      * Type: String
@@ -2237,6 +2248,43 @@
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGES_UNSUSPENDED = "android.intent.action.PACKAGES_UNSUSPENDED";
+
+    /**
+     * Broadcast Action: Sent to a package that has been suspended by the system. This is sent
+     * whenever a package is put into a suspended state or any of it's app extras change while
+     * in the suspended state.
+     * <p> Optionally includes the following extras:
+     * <ul>
+     *     <li> {@link #EXTRA_SUSPENDED_PACKAGE_EXTRAS} which is a {@link Bundle} which will contain
+     *     useful information for the app being suspended.
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
+
+    /**
+     * Broadcast Action: Sent to a package that has been unsuspended.
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system. <em>This will be delivered to {@link BroadcastReceiver} components declared in
+     * the manifest.</em>
+     *
+     * @see #ACTION_MY_PACKAGE_SUSPENDED
+     * @see #EXTRA_SUSPENDED_PACKAGE_EXTRAS
+     * @see PackageManager#isPackageSuspended()
+     * @see PackageManager#getSuspendedPackageAppExtras()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
+
     /**
      * Broadcast Action: A user ID has been removed from the system.  The user
      * ID number is stored in the extra data under {@link #EXTRA_UID}.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d43d80f..277738b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -278,7 +278,7 @@
 
     boolean isPackageSuspendedForUser(String packageName, int userId);
 
-    PersistableBundle getPackageSuspendedAppExtras(String pacakgeName, int userId);
+    PersistableBundle getSuspendedPackageAppExtras(String packageName, int userId);
 
     void setSuspendedPackageAppExtras(String packageName, in PersistableBundle appExtras,
             int userId);
@@ -619,6 +619,8 @@
             in String[] packageNames, int userId);
     void revokeDefaultPermissionsFromDisabledTelephonyDataServices(
             in String[] packageNames, int userId);
+    void grantDefaultPermissionsToActiveLuiApp(in String packageName, int userId);
+    void revokeDefaultPermissionsFromLuiApps(in String[] packageNames, int userId);
 
     boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
 
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 314eb98..491f0af 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5513,7 +5513,7 @@
      * Puts the package in a suspended state, where attempts at starting activities are denied.
      *
      * <p>It doesn't remove the data or the actual package file. The application's notifications
-     * will be hidden, any of the it's started activities will be stopped and it will not be able to
+     * will be hidden, any of its started activities will be stopped and it will not be able to
      * show toasts or dialogs or ring the device. When the user tries to launch a suspended app, a
      * system dialog with the given {@code dialogMessage} will be shown instead.</p>
      *
@@ -5577,11 +5577,26 @@
     }
 
     /**
-     * Apps can query this to know if they have been suspended.
+     * Apps can query this to know if they have been suspended. A system app with the permission
+     * {@code android.permission.SUSPEND_APPS} can put any app on the device into a suspended state.
+     *
+     * <p>While in this state, the application's notifications will be hidden, any of its started
+     * activities will be stopped and it will not be able to show toasts or dialogs or ring the
+     * device. When the user tries to launch a suspended app, the system will, instead, show a
+     * dialog to the user informing them that they cannot use this app while it is suspended.
+     *
+     * <p>When an app is put into this state, the broadcast action
+     * {@link Intent#ACTION_MY_PACKAGE_SUSPENDED} will be delivered to any of its broadcast
+     * receivers that included this action in their intent-filters, <em>including manifest
+     * receivers.</em> Similarly, a broadcast action {@link Intent#ACTION_MY_PACKAGE_UNSUSPENDED}
+     * is delivered when a previously suspended app is taken out of this state.
+     * </p>
      *
      * @return {@code true} if the calling package has been suspended, {@code false} otherwise.
      *
      * @see #getSuspendedPackageAppExtras()
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
      */
     public boolean isPackageSuspended() {
         throw new UnsupportedOperationException("isPackageSuspended not implemented");
@@ -5602,7 +5617,7 @@
      */
     @SystemApi
     @RequiresPermission(Manifest.permission.SUSPEND_APPS)
-    public PersistableBundle getSuspendedPackageAppExtras(String packageName) {
+    public @Nullable PersistableBundle getSuspendedPackageAppExtras(String packageName) {
         throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
     }
 
@@ -5631,15 +5646,17 @@
      * Returns any extra information supplied as {@code appExtras} to the system when the calling
      * app was suspended.
      *
-     * <p> Note: This just returns whatever {@link PersistableBundle} was passed to the system via
-     * {@code setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
-     * String)} when suspending the package, <em> which might be {@code null}. </em></p>
+     * <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>
      *
-     * @return A {@link PersistableBundle} containing the extras for the app, or {@code null} if the
+     * @return A {@link Bundle} containing the extras for the app, or {@code null} if the
      * package is not currently suspended.
+     *
      * @see #isPackageSuspended()
+     * @see Intent#ACTION_MY_PACKAGE_UNSUSPENDED
+     * @see Intent#ACTION_MY_PACKAGE_SUSPENDED
      */
-    public @Nullable PersistableBundle getSuspendedPackageAppExtras() {
+    public @Nullable Bundle getSuspendedPackageAppExtras() {
         throw new UnsupportedOperationException("getSuspendedPackageAppExtras not implemented");
     }
 
diff --git a/core/java/android/content/pm/dex/PackageOptimizationInfo.java b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
index b650457..7e7d29e 100644
--- a/core/java/android/content/pm/dex/PackageOptimizationInfo.java
+++ b/core/java/android/content/pm/dex/PackageOptimizationInfo.java
@@ -22,19 +22,19 @@
  * @hide
  */
 public class PackageOptimizationInfo {
-    private final String mCompilationFilter;
-    private final String mCompilationReason;
+    private final int mCompilationFilter;
+    private final int mCompilationReason;
 
-    public PackageOptimizationInfo(String compilerFilter, String compilationReason) {
+    public PackageOptimizationInfo(int compilerFilter, int compilationReason) {
         this.mCompilationReason = compilationReason;
         this.mCompilationFilter = compilerFilter;
     }
 
-    public String getCompilationReason() {
+    public int getCompilationReason() {
         return mCompilationReason;
     }
 
-    public String getCompilationFilter() {
+    public int getCompilationFilter() {
         return mCompilationFilter;
     }
 
@@ -42,6 +42,6 @@
      * Create a default optimization info object for the case when we have no information.
      */
     public static PackageOptimizationInfo createWithNoInfo() {
-        return new PackageOptimizationInfo("no-info", "no-info");
+        return new PackageOptimizationInfo(-1, -1);
     }
 }
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 56cba79..c6c676f 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -31,7 +31,7 @@
 import java.util.regex.Pattern;
 
 /**
- * This is a convience class that helps build SQL queries to be sent to
+ * This is a convenience class that helps build SQL queries to be sent to
  * {@link SQLiteDatabase} objects.
  */
 public class SQLiteQueryBuilder
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
new file mode 100644
index 0000000..a037289
--- /dev/null
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -0,0 +1,165 @@
+/*
+ * 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.biometrics;
+
+
+/**
+ * Interface containing all of the biometric modality agnostic constants.
+ * @hide
+ */
+public interface BiometricConstants {
+    //
+    // Error messages from biometric hardware during initilization, enrollment, authentication or
+    // removal.
+    //
+
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
+
+    /**
+     * Error state returned when the sensor was unable to process the current image.
+     */
+    int BIOMETRIC_ERROR_UNABLE_TO_PROCESS = 2;
+
+    /**
+     * Error state returned when the current request has been running too long. This is intended to
+     * prevent programs from waiting for the biometric sensor indefinitely. The timeout is platform
+     * and sensor-specific, but is generally on the order of 30 seconds.
+     */
+    int BIOMETRIC_ERROR_TIMEOUT = 3;
+
+    /**
+     * Error state returned for operations like enrollment; the operation cannot be completed
+     * because there's not enough storage remaining to complete the operation.
+     */
+    int BIOMETRIC_ERROR_NO_SPACE = 4;
+
+    /**
+     * The operation was canceled because the biometric sensor is unavailable. For example, this may
+     * happen when the user is switched, the device is locked or another pending operation prevents
+     * or disables it.
+     */
+    int BIOMETRIC_ERROR_CANCELED = 5;
+
+    /**
+     * The {@link BiometricManager#remove} call failed. Typically this will happen when the provided
+     * biometric id was incorrect.
+     *
+     * @hide
+     */
+    int BIOMETRIC_ERROR_UNABLE_TO_REMOVE = 6;
+
+    /**
+     * The operation was canceled because the API is locked out due to too many attempts.
+     * This occurs after 5 failed attempts, and lasts for 30 seconds.
+     */
+    int BIOMETRIC_ERROR_LOCKOUT = 7;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * These messages are typically reserved for internal operations such as enrollment, but may be
+     * used to express vendor errors not otherwise covered. Applications are expected to show the
+     * error message string if they happen, but are advised not to rely on the message id since they
+     * will be device and vendor-specific
+     */
+    int BIOMETRIC_ERROR_VENDOR = 8;
+
+    /**
+     * The operation was canceled because BIOMETRIC_ERROR_LOCKOUT occurred too many times.
+     * Biometric authentication is disabled until the user unlocks with strong authentication
+     * (PIN/Pattern/Password)
+     */
+    int BIOMETRIC_ERROR_LOCKOUT_PERMANENT = 9;
+
+    /**
+     * The user canceled the operation. Upon receiving this, applications should use alternate
+     * authentication (e.g. a password). The application should also provide the means to return to
+     * biometric authentication, such as a "use <biometric>" button.
+     */
+    int BIOMETRIC_ERROR_USER_CANCELED = 10;
+
+    /**
+     * The user does not have any biometrics enrolled.
+     */
+    int BIOMETRIC_ERROR_NO_BIOMETRICS = 11;
+
+    /**
+     * The device does not have a biometric sensor.
+     */
+    int BIOMETRIC_ERROR_HW_NOT_PRESENT = 12;
+
+    /**
+     * @hide
+     */
+    int BIOMETRIC_ERROR_VENDOR_BASE = 1000;
+
+    //
+    // Image acquisition messages.
+    //
+
+    /**
+     * The image acquired was good.
+     */
+    int BIOMETRIC_ACQUIRED_GOOD = 0;
+
+    /**
+     * Only a partial biometric image was detected. During enrollment, the user should be informed
+     * on what needs to happen to resolve this problem, e.g. "press firmly on sensor." (for
+     * fingerprint)
+     */
+    int BIOMETRIC_ACQUIRED_PARTIAL = 1;
+
+    /**
+     * The biometric image was too noisy to process due to a detected condition or a possibly dirty
+     * sensor (See {@link #BIOMETRIC_ACQUIRED_IMAGER_DIRTY}).
+     */
+    int BIOMETRIC_ACQUIRED_INSUFFICIENT = 2;
+
+    /**
+     * The biometric image was too noisy due to suspected or detected dirt on the sensor.  For
+     * example, it's reasonable return this after multiple {@link #BIOMETRIC_ACQUIRED_INSUFFICIENT}
+     * or actual detection of dirt on the sensor (stuck pixels, swaths, etc.). The user is expected
+     * to take action to clean the sensor when this is returned.
+     */
+    int BIOMETRIC_ACQUIRED_IMAGER_DIRTY = 3;
+
+    /**
+     * The biometric image was unreadable due to lack of motion.
+     */
+    int BIOMETRIC_ACQUIRED_TOO_SLOW = 4;
+
+    /**
+     * The biometric image was incomplete due to quick motion. For example, this could also happen
+     * if the user moved during acquisition. The user should be asked to repeat the operation more
+     * slowly.
+     */
+    int BIOMETRIC_ACQUIRED_TOO_FAST = 5;
+
+    /**
+     * Hardware vendors may extend this list if there are conditions that do not fall under one of
+     * the above categories. Vendors are responsible for providing error strings for these errors.
+     * @hide
+     */
+    int BIOMETRIC_ACQUIRED_VENDOR = 6;
+    /**
+     * @hide
+     */
+    int BIOMETRICT_ACQUIRED_VENDOR_BASE = 1000;
+}
diff --git a/core/java/android/hardware/fingerprint/FingerprintDialog.java b/core/java/android/hardware/biometrics/BiometricDialog.java
similarity index 72%
rename from core/java/android/hardware/fingerprint/FingerprintDialog.java
rename to core/java/android/hardware/biometrics/BiometricDialog.java
index 49835963..dd848a3 100644
--- a/core/java/android/hardware/fingerprint/FingerprintDialog.java
+++ b/core/java/android/hardware/biometrics/BiometricDialog.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package android.hardware.fingerprint;
+package android.hardware.biometrics;
 
-import static android.Manifest.permission.USE_FINGERPRINT;
+import static android.Manifest.permission.USE_BIOMETRIC;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -24,10 +24,7 @@
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.pm.PackageManager;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricFingerprintConstants;
-import android.hardware.biometrics.CryptoObject;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.fingerprint.FingerprintManager;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.text.TextUtils;
@@ -39,9 +36,9 @@
 import javax.crypto.Mac;
 
 /**
- * A class that manages a system-provided fingerprint dialog.
+ * A class that manages a system-provided biometric dialog.
  */
-public class FingerprintDialog implements BiometricAuthenticator, BiometricFingerprintConstants {
+public class BiometricDialog implements BiometricAuthenticator, BiometricConstants {
 
     /**
      * @hide
@@ -71,7 +68,7 @@
      * after this amount of time.
      * @hide
      */
-    public static final int HIDE_DIALOG_DELAY = 3000; // ms
+    public static final int HIDE_DIALOG_DELAY = 2000; // ms
     /**
      * @hide
      */
@@ -97,18 +94,21 @@
     }
 
     /**
-     * A builder that collects arguments, to be shown on the system-provided fingerprint dialog.
+     * A builder that collects arguments to be shown on the system-provided biometric dialog.
      **/
     public static class Builder {
-        private final Bundle bundle;
-        private ButtonInfo positiveButtonInfo;
-        private ButtonInfo negativeButtonInfo;
+        private final Bundle mBundle;
+        private ButtonInfo mPositiveButtonInfo;
+        private ButtonInfo mNegativeButtonInfo;
+        private Context mContext;
 
         /**
-         * Creates a builder for a fingerprint dialog.
+         * Creates a builder for a biometric dialog.
+         * @param context
          */
-        public Builder() {
-            bundle = new Bundle();
+        public Builder(Context context) {
+            mBundle = new Bundle();
+            mContext = context;
         }
 
         /**
@@ -117,7 +117,7 @@
          * @return
          */
         public Builder setTitle(@NonNull CharSequence title) {
-            bundle.putCharSequence(KEY_TITLE, title);
+            mBundle.putCharSequence(KEY_TITLE, title);
             return this;
         }
 
@@ -127,7 +127,7 @@
          * @return
          */
         public Builder setSubtitle(@NonNull CharSequence subtitle) {
-            bundle.putCharSequence(KEY_SUBTITLE, subtitle);
+            mBundle.putCharSequence(KEY_SUBTITLE, subtitle);
             return this;
         }
 
@@ -137,7 +137,7 @@
          * @return
          */
         public Builder setDescription(@NonNull CharSequence description) {
-            bundle.putCharSequence(KEY_DESCRIPTION, description);
+            mBundle.putCharSequence(KEY_DESCRIPTION, description);
             return this;
         }
 
@@ -160,13 +160,15 @@
             if (listener == null) {
                 throw new IllegalArgumentException("Listener must not be null");
             }
-            bundle.putCharSequence(KEY_POSITIVE_TEXT, text);
-            positiveButtonInfo = new ButtonInfo(executor, listener);
+            mBundle.putCharSequence(KEY_POSITIVE_TEXT, text);
+            mPositiveButtonInfo = new ButtonInfo(executor, listener);
             return this;
         }
 
         /**
-         * Required: Set the text for the negative button.
+         * Required: Set the text for the negative button. This would typically be used as a
+         * "Cancel" button, but may be also used to show an alternative method for authentication,
+         * such as screen that asks for a backup password.
          * @param text
          * @return
          */
@@ -182,27 +184,26 @@
             if (listener == null) {
                 throw new IllegalArgumentException("Listener must not be null");
             }
-            bundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
-            negativeButtonInfo = new ButtonInfo(executor, listener);
+            mBundle.putCharSequence(KEY_NEGATIVE_TEXT, text);
+            mNegativeButtonInfo = new ButtonInfo(executor, listener);
             return this;
         }
 
         /**
-         * Creates a {@link FingerprintDialog} with the arguments supplied to this builder.
-         * @param context
-         * @return a {@link FingerprintDialog}
+         * Creates a {@link BiometricDialog}.
+         * @return a {@link BiometricDialog}
          * @throws IllegalArgumentException if any of the required fields are not set.
          */
-        public FingerprintDialog build(Context context) {
-            final CharSequence title = bundle.getCharSequence(KEY_TITLE);
-            final CharSequence negative = bundle.getCharSequence(KEY_NEGATIVE_TEXT);
+        public BiometricDialog build() {
+            final CharSequence title = mBundle.getCharSequence(KEY_TITLE);
+            final CharSequence negative = mBundle.getCharSequence(KEY_NEGATIVE_TEXT);
 
             if (TextUtils.isEmpty(title)) {
                 throw new IllegalArgumentException("Title must be set and non-empty");
             } else if (TextUtils.isEmpty(negative)) {
                 throw new IllegalArgumentException("Negative text must be set and non-empty");
             }
-            return new FingerprintDialog(context, bundle, positiveButtonInfo, negativeButtonInfo);
+            return new BiometricDialog(mContext, mBundle, mPositiveButtonInfo, mNegativeButtonInfo);
         }
     }
 
@@ -212,7 +213,7 @@
     private ButtonInfo mPositiveButtonInfo;
     private ButtonInfo mNegativeButtonInfo;
 
-    IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
+    IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
         @Override
         public void onDialogDismissed(int reason) {
             // Check the reason and invoke OnClickListener(s) if necessary
@@ -228,7 +229,7 @@
         }
     };
 
-    private FingerprintDialog(Context context, Bundle bundle,
+    private BiometricDialog(Context context, Bundle bundle,
             ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
         mBundle = bundle;
         mPositiveButtonInfo = positiveButtonInfo;
@@ -238,8 +239,8 @@
     }
 
     /**
-     * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
-     * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
+     * A wrapper class for the crypto objects supported by BiometricDialog. Currently the framework
+     * supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
      */
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
         public CryptoObject(@NonNull Signature signature) {
@@ -280,9 +281,8 @@
     }
 
     /**
-     * Container for callback data from {@link #authenticate(
-     * CancellationSignal, Executor, AuthenticationCallback)} and
-     * {@link #authenticate(CryptoObject, CancellationSignal, Executor,
+     * Container for callback data from {@link #authenticate( CancellationSignal, Executor,
+     * AuthenticationCallback)} and {@link #authenticate(CryptoObject, CancellationSignal, Executor,
      * AuthenticationCallback)}
      */
     public static class AuthenticationResult extends BiometricAuthenticator.AuthenticationResult {
@@ -299,8 +299,8 @@
         }
         /**
          * Obtain the crypto object associated with this transaction
-         * @return crypto object provided to {@link #authenticate(
-         * CryptoObject, CancellationSignal, Executor, AuthenticationCallback)}
+         * @return crypto object provided to {@link #authenticate( CryptoObject, CancellationSignal,
+         * Executor, AuthenticationCallback)}
          */
         public CryptoObject getCryptoObject() {
             return (CryptoObject) super.getCryptoObject();
@@ -308,12 +308,12 @@
     }
 
     /**
-     * Callback structure provided to {@link FingerprintDialog#authenticate(CancellationSignal,
-     * Executor, AuthenticationCallback)} or {@link FingerprintDialog#authenticate(CryptoObject,
+     * Callback structure provided to {@link BiometricDialog#authenticate(CancellationSignal,
+     * Executor, AuthenticationCallback)} or {@link BiometricDialog#authenticate(CryptoObject,
      * CancellationSignal, Executor, AuthenticationCallback)}. Users must provide an implementation
      * of this for listening to authentication events.
      */
-    public static abstract class AuthenticationCallback extends
+    public abstract static class AuthenticationCallback extends
             BiometricAuthenticator.AuthenticationCallback {
         /**
          * Called when an unrecoverable error has been encountered and the operation is complete.
@@ -366,7 +366,6 @@
         }
     }
 
-
     /**
      * @param crypto Object associated with the call
      * @param cancel An object that can be used to cancel authentication
@@ -379,7 +378,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
@@ -396,30 +395,26 @@
     public void authenticate(@NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
-        if (!(callback instanceof FingerprintDialog.AuthenticationCallback)) {
+        if (!(callback instanceof BiometricDialog.AuthenticationCallback)) {
             throw new IllegalArgumentException("Callback cannot be casted");
         }
         authenticate(cancel, executor, (AuthenticationCallback) callback);
     }
 
-
     /**
-     * This call warms up the fingerprint hardware, displays a system-provided dialog,
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int,
-     * CharSequence)} is called, when
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(
-     * AuthenticationResult)}, or when the user dismisses the system-provided dialog, at which point
-     * the crypto object becomes invalid. This operation can be canceled by using the provided
-     * cancel object. The application will receive authentication errors through
-     * {@link AuthenticationCallback}, and button events through the
-     * corresponding callback set in {@link Builder#setNegativeButton(CharSequence,
-     * Executor, DialogInterface.OnClickListener)}. It is safe to reuse the
-     * {@link FingerprintDialog} object, and calling {@link FingerprintDialog#authenticate(
-     * CancellationSignal, Executor, AuthenticationCallback)} while an
-     * existing authentication attempt is occurring will stop the previous client and start a
-     * new authentication. The interrupted client will receive a cancelled notification through
-     * {@link AuthenticationCallback#onAuthenticationError(int,
+     * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+     * scanning for a fingerprint. It terminates when {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+     * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)}, or when the user
+     * dismisses the system-provided dialog, at which point the crypto object becomes invalid. This
+     * operation can be canceled by using the provided cancel object. The application will receive
+     * authentication errors through {@link AuthenticationCallback}, and button events through the
+     * corresponding callback set in {@link Builder#setNegativeButton(CharSequence, Executor,
+     * DialogInterface.OnClickListener)}. It is safe to reuse the {@link BiometricDialog} object,
+     * and calling {@link BiometricDialog#authenticate( CancellationSignal, Executor,
+     * AuthenticationCallback)} while an existing authentication attempt is occurring will stop the
+     * previous client and start a new authentication. The interrupted client will receive a
+     * cancelled notification through {@link AuthenticationCallback#onAuthenticationError(int,
      * CharSequence)}.
      *
      * @throws IllegalArgumentException If any of the arguments are null
@@ -429,7 +424,7 @@
      * @param executor An executor to handle callback events
      * @param callback An object to receive authentication events
      */
-    @RequiresPermission(USE_FINGERPRINT)
+    @RequiresPermission(USE_BIOMETRIC)
     public void authenticate(@NonNull CryptoObject crypto,
             @NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
@@ -442,23 +437,19 @@
     }
 
     /**
-     * This call warms up the fingerprint hardware, displays a system-provided dialog,
-     * and starts scanning for a fingerprint. It terminates when
-     * {@link AuthenticationCallback#onAuthenticationError(int,
-     * CharSequence)} is called, when
-     * {@link AuthenticationCallback#onAuthenticationSucceeded(
-     * AuthenticationResult)} is called, or when the user dismisses the system-provided dialog.
-     * This operation can be canceled by using the provided cancel object. The application will
-     * receive authentication errors through {@link AuthenticationCallback},
-     * and button events through the corresponding callback set in
-     * {@link Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.
-     * It is safe to reuse the {@link FingerprintDialog} object, and calling
-     * {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
-     * AuthenticationCallback)} while an existing authentication attempt is
-     * occurring will stop the previous client and start a new authentication. The interrupted
-     * client will receive a cancelled notification through
-     * {@link AuthenticationCallback#onAuthenticationError(int,
-     * CharSequence)}.
+     * This call warms up the fingerprint hardware, displays a system-provided dialog, and starts
+     * scanning for a fingerprint. It terminates when {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)} is called, when {@link
+     * AuthenticationCallback#onAuthenticationSucceeded( AuthenticationResult)} is called, or when
+     * the user dismisses the system-provided dialog.  This operation can be canceled by using the
+     * provided cancel object. The application will receive authentication errors through {@link
+     * AuthenticationCallback}, and button events through the corresponding callback set in {@link
+     * Builder#setNegativeButton(CharSequence, Executor, DialogInterface.OnClickListener)}.  It is
+     * safe to reuse the {@link BiometricDialog} object, and calling {@link
+     * BiometricDialog#authenticate(CancellationSignal, Executor, AuthenticationCallback)} while
+     * an existing authentication attempt is occurring will stop the previous client and start a new
+     * authentication. The interrupted client will receive a cancelled notification through {@link
+     * AuthenticationCallback#onAuthenticationError(int, CharSequence)}.
      *
      * @throws IllegalArgumentException If any of the arguments are null
      *
@@ -466,7 +457,7 @@
      * @param executor An executor to handle callback events
      * @param callback An object to receive authentication events
      */
-    @RequiresPermission(USE_FINGERPRINT)
+    @RequiresPermission(USE_BIOMETRIC)
     public void authenticate(@NonNull CancellationSignal cancel,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull AuthenticationCallback callback) {
@@ -479,13 +470,16 @@
     private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
             Executor executor) {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
-            sendError(FINGERPRINT_ERROR_HW_NOT_PRESENT, callback, executor);
+            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
+                      executor);
             return true;
         } else if (!mFingerprintManager.isHardwareDetected()) {
-            sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, callback, executor);
+            sendError(BiometricDialog.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
+                      executor);
             return true;
         } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
-            sendError(FINGERPRINT_ERROR_NO_FINGERPRINTS, callback, executor);
+            sendError(BiometricDialog.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
+                      executor);
             return true;
         }
         return false;
diff --git a/core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
similarity index 76%
rename from core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl
rename to core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
index 13e7974..e528aa7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintDialogReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricDialogReceiver.aidl
@@ -13,16 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.hardware.fingerprint;
+package android.hardware.biometrics;
 
-import android.hardware.fingerprint.Fingerprint;
 import android.os.Bundle;
 import android.os.UserHandle;
 
 /**
- * Communication channel from the FingerprintDialog (SysUI) back to AuthenticationClient.
+ * Communication channel from the BiometricDialog (SysUI) back to AuthenticationClient.
  * @hide
  */
-oneway interface IFingerprintDialogReceiver {
+oneway interface IBiometricDialogReceiver {
     void onDialogDismissed(int reason);
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 390b83f..4279b19 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1708,6 +1708,7 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING MOTION_TRACKING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA LOGICAL_MULTI_CAMERA}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME MONOCHROME}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1724,6 +1725,7 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -3392,6 +3394,21 @@
     public static final Key<Integer> LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE =
             new Key<Integer>("android.logicalMultiCamera.sensorSyncType", int.class);
 
+    /**
+     * <p>List of distortion correction modes for {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode} that are
+     * supported by this camera device.</p>
+     * <p>No device is required to support this API; such devices will always list only 'OFF'.
+     * All devices that support this API will list both FAST and HIGH_QUALITY.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#DISTORTION_CORRECTION_MODE android.distortionCorrection.mode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> DISTORTION_CORRECTION_AVAILABLE_MODES =
+            new Key<int[]>("android.distortionCorrection.availableModes", int[].class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 7669c01..1a5d3ac 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -862,6 +862,13 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA = 11;
 
+    /**
+     * <p>The camera device is a monochrome camera that doesn't contain a color filter array,
+     * and the pixel values on U and Y planes are all 128.</p>
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_MONOCHROME = 12;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -2730,6 +2737,31 @@
     public static final int TONEMAP_PRESET_CURVE_REC709 = 1;
 
     //
+    // Enumeration values for CaptureRequest#DISTORTION_CORRECTION_MODE
+    //
+
+    /**
+     * <p>No distortion correction is applied.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_OFF = 0;
+
+    /**
+     * <p>Lens distortion correction is applied without reducing frame rate
+     * relative to sensor output. It may be the same as OFF if distortion correction would
+     * reduce frame rate relative to sensor.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_FAST = 1;
+
+    /**
+     * <p>High-quality distortion correction is applied, at the cost of
+     * possibly reduced frame rate relative to sensor output.</p>
+     * @see CaptureRequest#DISTORTION_CORRECTION_MODE
+     */
+    public static final int DISTORTION_CORRECTION_MODE_HIGH_QUALITY = 2;
+
+    //
     // Enumeration values for CaptureResult#CONTROL_AE_STATE
     //
 
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b0cbec7..2252571 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2853,6 +2853,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -2915,6 +2917,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -3167,6 +3171,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 6331942..8df5447 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -4096,6 +4096,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -4158,6 +4160,8 @@
      * of points can be less than max (that is, the request doesn't have to
      * always provide a curve with number of points equivalent to
      * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}).</p>
+     * <p>For devices with MONOCHROME capability, only red channel is used. Green and blue channels
+     * are ignored.</p>
      * <p>A few examples, and their corresponding graphical mappings; these
      * only specify the red channel and the precision is limited to 4
      * digits, for conciseness.</p>
@@ -4450,6 +4454,49 @@
     public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
             new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
 
+    /**
+     * <p>Mode of operation for the lens distortion correction block.</p>
+     * <p>The lens distortion correction block attempts to improve image quality by fixing
+     * radial, tangential, or other geometric aberrations in the camera device's optics.  If
+     * available, the {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} field documents the lens's distortion parameters.</p>
+     * <p>OFF means no distortion correction is done.</p>
+     * <p>FAST/HIGH_QUALITY both mean camera device determined distortion correction will be
+     * applied. HIGH_QUALITY mode indicates that the camera device will use the highest-quality
+     * correction algorithms, even if it slows down capture rate. FAST means the camera device
+     * will not slow down capture rate when applying correction. FAST may be the same as OFF if
+     * any correction at all would slow down capture rate.  Every output stream will have a
+     * similar amount of enhancement applied.</p>
+     * <p>The correction only applies to processed outputs such as YUV, JPEG, or DEPTH16; it is not
+     * applied to any RAW output.  Metadata coordinates such as face rectangles or metering
+     * regions are also not affected by correction.</p>
+     * <p>Applications enabling distortion correction need to pay extra attention when converting
+     * image coordinates between corrected output buffers and the sensor array. For example, if
+     * the app supports tap-to-focus and enables correction, it then has to apply the distortion
+     * model described in {@link CameraCharacteristics#LENS_DISTORTION android.lens.distortion} to the image buffer tap coordinates to properly
+     * calculate the tap position on the sensor active array to be used with
+     * {@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}. The same applies in reverse to detected face rectangles if
+     * they need to be drawn on top of the corrected output buffers.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_OFF OFF}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_FAST FAST}</li>
+     *   <li>{@link #DISTORTION_CORRECTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES android.distortionCorrection.availableModes}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AF_REGIONS
+     * @see CameraCharacteristics#DISTORTION_CORRECTION_AVAILABLE_MODES
+     * @see CameraCharacteristics#LENS_DISTORTION
+     * @see #DISTORTION_CORRECTION_MODE_OFF
+     * @see #DISTORTION_CORRECTION_MODE_FAST
+     * @see #DISTORTION_CORRECTION_MODE_HIGH_QUALITY
+     */
+    @PublicKey
+    public static final Key<Integer> DISTORTION_CORRECTION_MODE =
+            new Key<Integer>("android.distortionCorrection.mode", int.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index a3b2d22..efb9517 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -535,6 +535,19 @@
     }
 
     /**
+     * Set the level of color saturation to apply to the display.
+     * @param level The amount of saturation to apply, between 0 and 1 inclusive.
+     * 0 produces a grayscale image, 1 is normal.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.CONTROL_DISPLAY_SATURATION)
+    public void setSaturationLevel(float level) {
+        mGlobal.setSaturationLevel(level);
+    }
+
+    /**
      * Creates a virtual display.
      *
      * @see #createVirtualDisplay(String, int, int, int, Surface, int,
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1f67a6b..2d0ef2f 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -384,6 +384,17 @@
         }
     }
 
+    /**
+     * Set the level of color saturation to apply to the display.
+     */
+    public void setSaturationLevel(float level) {
+        try {
+            mDm.setSaturationLevel(level);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
+        }
+    }
+
     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
             String name, int width, int height, int densityDpi, Surface surface, int flags,
             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 9fcb9d3..b77de748 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -65,6 +65,9 @@
     // Requires CONFIGURE_DISPLAY_COLOR_MODE
     void requestColorMode(int displayId, int colorMode);
 
+    // Requires CONTROL_DISPLAY_SATURATION
+    void setSaturationLevel(float level);
+
     // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate
     // MediaProjection token for certain combinations of flags.
     int createVirtualDisplay(in IVirtualDisplayCallback callback,
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 8048099c..5e28570 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
 import android.annotation.CallbackExecutor;
@@ -30,7 +31,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricDialog;
 import android.hardware.biometrics.BiometricFingerprintConstants;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.CancellationSignal;
@@ -54,10 +57,10 @@
 
 /**
  * A class that coordinates access to the fingerprint hardware.
- * @deprecated See {@link FingerprintDialog} which shows a system-provided dialog upon starting
- * authentication. In a world where devices may have in-display fingerprint sensors, it's much
- * more realistic to have a system-provided authentication dialog since the in-display sensor
- * location may vary by vendor/device.
+ * @deprecated See {@link BiometricDialog} which shows a system-provided dialog upon starting
+ * authentication. In a world where devices may have different types of biometric authentication,
+ * it's much more realistic to have a system-provided authentication dialog since the method may
+ * vary by vendor/device.
  */
 @Deprecated
 @SystemService(Context.FINGERPRINT_SERVICE)
@@ -108,7 +111,7 @@
     /**
      * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
      * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
-     * @deprecated See {@link android.hardware.fingerprint.FingerprintDialog.CryptoObject}
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.CryptoObject}
      */
     @Deprecated
     public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -152,7 +155,7 @@
     /**
      * Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
      *     CancellationSignal, int, AuthenticationCallback, Handler)}.
-     * @deprecated See {@link android.hardware.fingerprint.FingerprintDialog.AuthenticationResult}
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationResult}
      */
     @Deprecated
     public static class AuthenticationResult {
@@ -201,7 +204,7 @@
      * FingerprintManager#authenticate(CryptoObject, CancellationSignal,
      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
      * fingerprint events.
-     * @deprecated See {@link android.hardware.fingerprint.FingerprintDialog.AuthenticationCallback}
+     * @deprecated See {@link android.hardware.biometrics.BiometricDialog.AuthenticationCallback}
      */
     @Deprecated
     public static abstract class AuthenticationCallback
@@ -375,13 +378,13 @@
      *         by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
      *         facility</a>.
      * @throws IllegalStateException if the crypto primitive is not initialized.
-     * @deprecated See {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
-     * FingerprintDialog.AuthenticationCallback)} and {@link FingerprintDialog#authenticate(
-     * FingerprintDialog.CryptoObject, CancellationSignal, Executor,
-     * FingerprintDialog.AuthenticationCallback)}
+     * @deprecated See {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)} and {@link BiometricDialog#authenticate(
+     * BiometricDialog.CryptoObject, CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)}
      */
     @Deprecated
-    @RequiresPermission(USE_FINGERPRINT)
+    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
         authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
@@ -405,7 +408,7 @@
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      * @hide
      */
-    @RequiresPermission(USE_FINGERPRINT)
+    @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
             int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
         if (callback == null) {
@@ -441,7 +444,7 @@
 
     /**
      * Per-user version, see {@link FingerprintManager#authenticate(CryptoObject,
-     * CancellationSignal, Bundle, Executor, IFingerprintDialogReceiver, AuthenticationCallback)}
+     * CancellationSignal, Bundle, Executor, IBiometricDialogReceiver, AuthenticationCallback)}
      * @param userId the user ID that the fingerprint hardware will authenticate for.
      */
     private void authenticate(int userId,
@@ -449,7 +452,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
+            @NonNull IBiometricDialogReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         mCryptoObject = crypto;
         if (cancel.isCanceled()) {
@@ -477,8 +480,8 @@
     }
 
     /**
-     * Private method, see {@link FingerprintDialog#authenticate(CancellationSignal, Executor,
-     * AuthenticationCallback)}
+     * Private method, see {@link BiometricDialog#authenticate(CancellationSignal, Executor,
+     * BiometricDialog.AuthenticationCallback)}
      * @param cancel
      * @param executor
      * @param callback
@@ -488,7 +491,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
+            @NonNull IBiometricDialogReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (cancel == null) {
             throw new IllegalArgumentException("Must supply a cancellation signal");
@@ -509,8 +512,8 @@
     }
 
     /**
-     * Private method, see {@link FingerprintDialog#authenticate(CryptoObject, CancellationSignal,
-     * Executor, AuthenticationCallback)}
+     * Private method, see {@link BiometricDialog#authenticate(BiometricDialog.CryptoObject,
+     * CancellationSignal, Executor, BiometricDialog.AuthenticationCallback)}
      * @param crypto
      * @param cancel
      * @param executor
@@ -521,7 +524,7 @@
             @NonNull CancellationSignal cancel,
             @NonNull Bundle bundle,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull IFingerprintDialogReceiver receiver,
+            @NonNull IBiometricDialogReceiver receiver,
             @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
         if (crypto == null) {
             throw new IllegalArgumentException("Must supply a crypto object");
@@ -740,8 +743,8 @@
      * Determine if there is at least one fingerprint enrolled.
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
-     * @deprecated See {@link FingerprintDialog} and
-     * {@link FingerprintDialog#FINGERPRINT_ERROR_NO_FINGERPRINTS}
+     * @deprecated See {@link BiometricDialog} and
+     * {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
      */
     @Deprecated
     @RequiresPermission(USE_FINGERPRINT)
@@ -774,8 +777,8 @@
      * Determine if fingerprint hardware is present and functional.
      *
      * @return true if hardware is present and functional, false otherwise.
-     * @deprecated See {@link FingerprintDialog} and
-     * {@link FingerprintDialog#FINGERPRINT_ERROR_HW_UNAVAILABLE}
+     * @deprecated See {@link BiometricDialog} and
+     * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
      */
     @Deprecated
     @RequiresPermission(USE_FINGERPRINT)
@@ -1155,9 +1158,14 @@
         @Override // binder call
         public void onError(long deviceId, int error, int vendorCode) {
             if (mExecutor != null) {
-                mExecutor.execute(() -> {
-                    sendErrorResult(deviceId, error, vendorCode);
-                });
+                // BiometricDialog case, post a delayed runnable on the FingerprintManager handler
+                // that sends the error message after FingerprintDialog.HIDE_DIALOG_DELAY to send
+                // the error to the application.
+                mHandler.postDelayed(() -> {
+                    mExecutor.execute(() -> {
+                        sendErrorResult(deviceId, error, vendorCode);
+                    });
+                }, BiometricDialog.HIDE_DIALOG_DELAY);
             } else {
                 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
             }
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f1502e4..78d01e5 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -16,8 +16,8 @@
 package android.hardware.fingerprint;
 
 import android.os.Bundle;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
 import android.hardware.fingerprint.Fingerprint;
@@ -31,7 +31,7 @@
     // Authenticate the given sessionId with a fingerprint
     void authenticate(IBinder token, long sessionId, int userId,
             IFingerprintServiceReceiver receiver, int flags, String opPackageName,
-            in Bundle bundle, IFingerprintDialogReceiver dialogReceiver);
+            in Bundle bundle, IBiometricDialogReceiver dialogReceiver);
 
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java
index b2aa9ba..e6f523c 100644
--- a/core/java/android/hardware/radio/ProgramList.java
+++ b/core/java/android/hardware/radio/ProgramList.java
@@ -263,6 +263,17 @@
         /**
          * @hide for framework use only
          */
+        public Filter() {
+            mIdentifierTypes = Collections.emptySet();
+            mIdentifiers = Collections.emptySet();
+            mIncludeCategories = false;
+            mExcludeModifications = false;
+            mVendorFilter = null;
+        }
+
+        /**
+         * @hide for framework use only
+         */
         public Filter(@Nullable Map<String, String> vendorFilter) {
             mIdentifierTypes = Collections.emptySet();
             mIdentifiers = Collections.emptySet();
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 8fde82e..8263bb8 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -211,6 +211,7 @@
         private final String mSerial;
         private final int mNumTuners;
         private final int mNumAudioSources;
+        private final boolean mIsInitializationRequired;
         private final boolean mIsCaptureSupported;
         private final BandDescriptor[] mBands;
         private final boolean mIsBgScanSupported;
@@ -222,7 +223,8 @@
         /** @hide */
         public ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
-                boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
+                boolean isInitializationRequired, boolean isCaptureSupported,
+                BandDescriptor[] bands, boolean isBgScanSupported,
                 @ProgramSelector.ProgramType int[] supportedProgramTypes,
                 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
                 @Nullable Map<String, Integer> dabFrequencyTable,
@@ -236,6 +238,7 @@
             mSerial = serial;
             mNumTuners = numTuners;
             mNumAudioSources = numAudioSources;
+            mIsInitializationRequired = isInitializationRequired;
             mIsCaptureSupported = isCaptureSupported;
             mBands = bands;
             mIsBgScanSupported = isBgScanSupported;
@@ -329,6 +332,18 @@
             return mNumAudioSources;
         }
 
+        /**
+         * Checks, if BandConfig initialization (after {@link RadioManager#openTuner})
+         * is required to be done before other operations or not.
+         *
+         * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged}
+         * callback before executing any other operations. Otherwise, such operation will fail
+         * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code.
+         */
+        public boolean isInitializationRequired() {
+            return mIsInitializationRequired;
+        }
+
         /** {@code true} if audio capture is possible from radio tuner output.
          * This indicates if routing to audio devices not connected to the same HAL as the FM radio
          * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
@@ -419,6 +434,7 @@
             mSerial = in.readString();
             mNumTuners = in.readInt();
             mNumAudioSources = in.readInt();
+            mIsInitializationRequired = in.readInt() == 1;
             mIsCaptureSupported = in.readInt() == 1;
             Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
             mBands = new BandDescriptor[tmp.length];
@@ -454,6 +470,7 @@
             dest.writeString(mSerial);
             dest.writeInt(mNumTuners);
             dest.writeInt(mNumAudioSources);
+            dest.writeInt(mIsInitializationRequired ? 1 : 0);
             dest.writeInt(mIsCaptureSupported ? 1 : 0);
             dest.writeParcelableArray(mBands, flags);
             dest.writeInt(mIsBgScanSupported ? 1 : 0);
@@ -476,6 +493,7 @@
                     + ", mVersion=" + mVersion + ", mSerial=" + mSerial
                     + ", mNumTuners=" + mNumTuners
                     + ", mNumAudioSources=" + mNumAudioSources
+                    + ", mIsInitializationRequired=" + mIsInitializationRequired
                     + ", mIsCaptureSupported=" + mIsCaptureSupported
                     + ", mIsBgScanSupported=" + mIsBgScanSupported
                     + ", mBands=" + Arrays.toString(mBands) + "]";
@@ -484,8 +502,8 @@
         @Override
         public int hashCode() {
             return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
-                mSerial, mNumTuners, mNumAudioSources, mIsCaptureSupported, mBands,
-                mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
+                mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
+                mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
         }
 
         @Override
@@ -503,6 +521,7 @@
             if (!Objects.equals(mSerial, other.mSerial)) return false;
             if (mNumTuners != other.mNumTuners) return false;
             if (mNumAudioSources != other.mNumAudioSources) return false;
+            if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
             if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
             if (!Objects.equals(mBands, other.mBands)) return false;
             if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index 85f3115..be2846f 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -60,6 +60,7 @@
                 mLegacyListProxy.close();
                 mLegacyListProxy = null;
             }
+            mCallback.close();
         }
         try {
             mTuner.close();
@@ -278,6 +279,7 @@
             try {
                 mTuner.startProgramListUpdates(filter);
             } catch (UnsupportedOperationException ex) {
+                Log.i(TAG, "Program list is not supported with this hardware");
                 return null;
             } catch (RemoteException ex) {
                 mCallback.setProgramListObserver(null, () -> { });
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 7437c40..0fb93e5 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -53,6 +53,12 @@
         }
     }
 
+    void close() {
+        synchronized (mLock) {
+            if (mProgramList != null) mProgramList.close();
+        }
+    }
+
     void setProgramListObserver(@Nullable ProgramList programList,
             @NonNull ProgramList.OnCloseListener closeListener) {
         Objects.requireNonNull(closeListener);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e07f586..80b1c3d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -861,6 +861,10 @@
      * You should always check {@link NetworkInfo#isConnected()} before initiating
      * network traffic. This may return {@code null} when there is no default
      * network.
+     * Note that if the default network is a VPN, this method will return the
+     * NetworkInfo for one of its underlying networks instead, or null if the
+     * VPN agent did not specify any. Apps interested in learning about VPNs
+     * should use {@link #getNetworkInfo(android.net.Network)} instead.
      *
      * @return a {@link NetworkInfo} object for the current default network
      *        or {@code null} if no default network is currently active
@@ -1018,7 +1022,11 @@
      *        which you're interested.
      * @return a {@link NetworkInfo} object for the requested
      *        network type or {@code null} if the type is not
-     *        supported by the device.
+     *        supported by the device. If {@code networkType} is
+     *        TYPE_VPN and a VPN is active for the calling app,
+     *        then this method will try to return one of the
+     *        underlying networks for the VPN or null if the
+     *        VPN agent didn't specify any.
      *
      * @deprecated This method does not support multiple connected networks
      *             of the same type. Use {@link #getAllNetworks} and
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 57f0588..8034bb6 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -56,7 +56,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 128 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 128.
      */
     public static final String AUTH_HMAC_MD5 = "hmac(md5)";
 
@@ -65,7 +66,8 @@
      * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b>
      *
      * <p>Keys for this algorithm must be 160 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 160.
      */
     public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
 
@@ -73,7 +75,8 @@
      * SHA256 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 256 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 96 to 256.
      */
     public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
 
@@ -81,7 +84,8 @@
      * SHA384 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 384 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 192 to 384.
      */
     public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
 
@@ -89,7 +93,8 @@
      * SHA512 HMAC Authentication/Integrity Algorithm.
      *
      * <p>Keys for this algorithm must be 512 bits in length.
-     * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+     *
+     * <p>Valid truncation lengths are multiples of 8 bits from 256 to 512.
      */
     public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
 
@@ -112,6 +117,7 @@
         AUTH_HMAC_MD5,
         AUTH_HMAC_SHA1,
         AUTH_HMAC_SHA256,
+        AUTH_HMAC_SHA384,
         AUTH_HMAC_SHA512,
         AUTH_CRYPT_AES_GCM
     })
@@ -126,11 +132,14 @@
      * Creates an IpSecAlgorithm of one of the supported types. Supported algorithm names are
      * defined as constants in this class.
      *
+     * <p>For algorithms that produce an integrity check value, the truncation length is a required
+     * parameter. See {@link #IpSecAlgorithm(String algorithm, byte[] key, int truncLenBits)}
+     *
      * @param algorithm name of the algorithm.
      * @param key key padded to a multiple of 8 bits.
      */
     public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) {
-        this(algorithm, key, key.length * 8);
+        this(algorithm, key, 0);
     }
 
     /**
@@ -228,6 +237,7 @@
             case AUTH_CRYPT_AES_GCM:
                 // The keying material for GCM is a key plus a 32-bit salt
                 isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32;
+                isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128;
                 break;
             default:
                 throw new IllegalArgumentException("Couldn't find an algorithm: " + name);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 972b9c0..c7234e3 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -305,6 +305,19 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
      * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -373,6 +386,19 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransforms}.
      *
+     * <p>Note that when applied to TCP sockets, calling {@link IpSecTransform#close()} on an
+     * applied transform before completion of graceful shutdown may result in the shutdown sequence
+     * failing to complete. As such, applications requiring graceful shutdown MUST close the socket
+     * prior to deactivating the applied transform. Socket closure may be performed asynchronously
+     * (in batches), so the returning of a close function does not guarantee shutdown of a socket.
+     * Setting an SO_LINGER timeout results in socket closure being performed synchronously, and is
+     * sufficient to ensure shutdown.
+     *
+     * Specifically, if the transform is deactivated (by calling {@link IpSecTransform#close()}),
+     * prior to the socket being closed, the standard [FIN - FIN/ACK - ACK], or the reset [RST]
+     * packets are dropped due to the lack of a valid Transform. Similarly, if a socket without the
+     * SO_LINGER option set is closed, the delayed/batched FIN packets may be dropped.
+     *
      * <h4>Rekey Procedure</h4>
      *
      * <p>When applying a new tranform to a socket in the outbound direction, the previous transform
@@ -476,7 +502,7 @@
      * signalling and UDP encapsulated IPsec traffic. Instances can be obtained by calling {@link
      * IpSecManager#openUdpEncapsulationSocket}. The provided socket cannot be re-bound by the
      * caller. The caller should not close the {@code FileDescriptor} returned by {@link
-     * #getSocket}, but should use {@link #close} instead.
+     * #getFileDescriptor}, but should use {@link #close} instead.
      *
      * <p>Allowing the user to close or unbind a UDP encapsulation socket could impact the traffic
      * of the next user who binds to that port. To prevent this scenario, these sockets are held
@@ -515,8 +541,8 @@
             mCloseGuard.open("constructor");
         }
 
-        /** Get the wrapped socket. */
-        public FileDescriptor getSocket() {
+        /** Get the encapsulation socket's file descriptor. */
+        public FileDescriptor getFileDescriptor() {
             if (mPfd == null) {
                 return null;
             }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 5df168d..15a0ee5 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -26,6 +26,8 @@
 import com.android.okhttp.internalandroidapi.Dns;
 import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory;
 
+import libcore.io.IoUtils;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.DatagramSocket;
@@ -77,6 +79,11 @@
             httpKeepAlive ? Integer.parseInt(System.getProperty("http.maxConnections", "5")) : 0;
     private static final long httpKeepAliveDurationMs =
             Long.parseLong(System.getProperty("http.keepAliveDuration", "300000"));  // 5 minutes.
+    // Value used to obfuscate network handle longs.
+    // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+    // value in the native/android/net.c NDK implementation.
+    private static final long HANDLE_MAGIC = 0xcafed00dL;
+    private static final int HANDLE_MAGIC_SIZE = 32;
 
     /**
      * @hide
@@ -137,9 +144,15 @@
             for (int i = 0; i < hostAddresses.length; i++) {
                 try {
                     Socket socket = createSocket();
-                    if (localAddress != null) socket.bind(localAddress);
-                    socket.connect(new InetSocketAddress(hostAddresses[i], port));
-                    return socket;
+                    boolean failed = true;
+                    try {
+                        if (localAddress != null) socket.bind(localAddress);
+                        socket.connect(new InetSocketAddress(hostAddresses[i], port));
+                        failed = false;
+                        return socket;
+                    } finally {
+                        if (failed) IoUtils.closeQuietly(socket);
+                    }
                 } catch (IOException e) {
                     if (i == (hostAddresses.length - 1)) throw e;
                 }
@@ -156,15 +169,27 @@
         public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
                 int localPort) throws IOException {
             Socket socket = createSocket();
-            socket.bind(new InetSocketAddress(localAddress, localPort));
-            socket.connect(new InetSocketAddress(address, port));
+            boolean failed = true;
+            try {
+                socket.bind(new InetSocketAddress(localAddress, localPort));
+                socket.connect(new InetSocketAddress(address, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
         @Override
         public Socket createSocket(InetAddress host, int port) throws IOException {
             Socket socket = createSocket();
-            socket.connect(new InetSocketAddress(host, port));
+            boolean failed = true;
+            try {
+                socket.connect(new InetSocketAddress(host, port));
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
 
@@ -176,7 +201,13 @@
         @Override
         public Socket createSocket() throws IOException {
             Socket socket = new Socket();
-            bindSocket(socket);
+            boolean failed = true;
+            try {
+                bindSocket(socket);
+                failed = false;
+            } finally {
+                if (failed) IoUtils.closeQuietly(socket);
+            }
             return socket;
         }
     }
@@ -335,6 +366,25 @@
     }
 
     /**
+     * Returns a {@link Network} object given a handle returned from {@link #getNetworkHandle}.
+     *
+     * @param networkHandle a handle returned from {@link #getNetworkHandle}.
+     * @return A {@link Network} object derived from {@code networkHandle}.
+     */
+    public static Network fromNetworkHandle(long networkHandle) {
+        if (networkHandle == 0) {
+            throw new IllegalArgumentException(
+                    "Network.fromNetworkHandle refusing to instantiate NETID_UNSET Network.");
+        }
+        if ((networkHandle & ((1L << HANDLE_MAGIC_SIZE) - 1)) != HANDLE_MAGIC
+                || networkHandle < 0) {
+            throw new IllegalArgumentException(
+                    "Value passed to fromNetworkHandle() is not a network handle.");
+        }
+        return new Network((int) (networkHandle >> HANDLE_MAGIC_SIZE));
+    }
+
+    /**
      * Returns a handle representing this {@code Network}, for use with the NDK API.
      */
     public long getNetworkHandle() {
@@ -356,14 +406,10 @@
         // At some future date it may be desirable to realign the handle with
         // Multiple Provisioning Domains API recommendations, as made by the
         // IETF mif working group.
-        //
-        // The handleMagic value MUST be kept in sync with the corresponding
-        // value in the native/android/net.c NDK implementation.
         if (netId == 0) {
             return 0L;  // make this zero condition obvious for debugging
         }
-        final long handleMagic = 0xcafed00dL;
-        return (((long) netId) << 32) | handleMagic;
+        return (((long) netId) << HANDLE_MAGIC_SIZE) | HANDLE_MAGIC;
     }
 
     // implement the Parcelable interface
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ef58f94..374b3ab 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -277,6 +277,7 @@
      * this network can be used by system apps to upload telemetry data.
      * @hide
      */
+    @SystemApi
     public static final int NET_CAPABILITY_OEM_PAID = 22;
 
     private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 4f92fa6..caefd89 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -17,6 +17,8 @@
 package android.net;
 
 import android.annotation.NonNull;
+import android.net.NetworkCapabilities.NetCapability;
+import android.net.NetworkCapabilities.Transport;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -427,6 +429,20 @@
         return type == Type.BACKGROUND_REQUEST;
     }
 
+    /**
+     * @see Builder#addCapability(int)
+     */
+    public boolean hasCapability(@NetCapability int capability) {
+        return networkCapabilities.hasCapability(capability);
+    }
+
+    /**
+     * @see Builder#addTransportType(int)
+     */
+    public boolean hasTransport(@Transport int transportType) {
+        return networkCapabilities.hasTransport(transportType);
+    }
+
     public String toString() {
         return "NetworkRequest [ " + type + " id=" + requestId +
                 (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f528d63..6ebb102 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1585,6 +1585,7 @@
         public static final int STATE2_CAMERA_FLAG = 1<<21;
         public static final int STATE2_BLUETOOTH_SCAN_FLAG = 1 << 20;
         public static final int STATE2_CELLULAR_HIGH_TX_POWER_FLAG = 1 << 19;
+        public static final int STATE2_USB_DATA_LINK_FLAG = 1 << 18;
 
         public static final int MOST_INTERESTING_STATES2 =
                 STATE2_POWER_SAVE_FLAG | STATE2_WIFI_ON_FLAG | STATE2_DEVICE_IDLE_MASK
@@ -2363,8 +2364,7 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
-    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
-            = new BitDescription[] {
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS = new BitDescription[] {
         new BitDescription(HistoryItem.STATE2_POWER_SAVE_FLAG, "power_save", "ps"),
         new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Ww"),
@@ -2375,6 +2375,7 @@
                 new String[] { "off", "light", "full", "???" },
                 new String[] { "off", "light", "full", "???" }),
         new BitDescription(HistoryItem.STATE2_CHARGING_FLAG, "charging", "ch"),
+        new BitDescription(HistoryItem.STATE2_USB_DATA_LINK_FLAG, "usb_data", "Ud"),
         new BitDescription(HistoryItem.STATE2_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
         new BitDescription(HistoryItem.STATE2_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index e22c65f..8378a82 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -295,8 +295,10 @@
 
         /**
          * The current lowest supported value of app target SDK. Applications targeting
-         * lower values will fail to install and run. Its possible values are defined
-         * in {@link Build.VERSION_CODES}.
+         * lower values may not function on devices running this SDK version. Its possible
+         * values are defined in {@link Build.VERSION_CODES}.
+         *
+         * @hide
          */
         public static final int MIN_SUPPORTED_TARGET_SDK_INT = SystemProperties.getInt(
                 "ro.build.version.min_supported_target_sdk", 0);
diff --git a/core/java/android/os/IStatsCompanionService.aidl b/core/java/android/os/IStatsCompanionService.aidl
index 402c995..116262e 100644
--- a/core/java/android/os/IStatsCompanionService.aidl
+++ b/core/java/android/os/IStatsCompanionService.aidl
@@ -47,10 +47,10 @@
       * Uses AlarmManager.setRepeating API, so if the timestamp is in past, alarm fires immediately,
       * and alarm is inexact.
       */
-    oneway void setPullingAlarms(long timestampMs, long intervalMs);
+    oneway void setPullingAlarm(long nextPullTimeMs);
 
     /** Cancel any repeating pulling alarm. */
-    oneway void cancelPullingAlarms();
+    oneway void cancelPullingAlarm();
 
     /**
       * Register an alarm when we want to trigger subscribers at the given
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 62bb385..e3c4870 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -2803,8 +2803,8 @@
                     Class<?> parcelableClass = Class.forName(name, false /* initialize */,
                             parcelableClassLoader);
                     if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
-                        throw new BadParcelableException("Parcelable protocol requires that the "
-                                + "class implements Parcelable");
+                        throw new BadParcelableException("Parcelable protocol requires subclassing "
+                                + "from Parcelable on class " + name);
                     }
                     Field f = parcelableClass.getField("CREATOR");
                     if ((f.getModifiers() & Modifier.STATIC) == 0) {
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 5be72bc..094f004 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -158,7 +158,7 @@
      * @hide
      */
     public static boolean isCore(int uid) {
-        if (uid > 0) {
+        if (uid >= 0) {
             final int appId = getAppId(uid);
             return appId < Process.FIRST_APPLICATION_UID;
         } else {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index ca4c796..b9dd376 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -32,6 +32,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
 
@@ -158,6 +159,13 @@
     private final Object mLock = new Object();
 
     /**
+     * List of exemptions to the API blacklist. These are prefix matches on the runtime format
+     * symbol signature. Any matching symbol is treated by the runtime as being on the light grey
+     * list.
+     */
+    private List<String> mApiBlacklistExemptions = Collections.emptyList();
+
+    /**
      * The state of the connection to the primary zygote.
      */
     private ZygoteState primaryZygoteState;
@@ -175,7 +183,7 @@
      * The process will continue running after this function returns.
      *
      * <p>If processes are not enabled, a new thread in the caller's
-     * process is created and main() of <var>processClass</var> called there.
+     * process is created and main() of <var>processclass</var> called there.
      *
      * <p>The niceName parameter, if not an empty string, is a custom name to
      * give to the process instead of using processClass.  This allows you to
@@ -454,6 +462,49 @@
     }
 
     /**
+     * Push hidden API blacklisting exemptions into the zygote process(es).
+     *
+     * <p>The list of exemptions will take affect for all new processes forked from the zygote after
+     * this call.
+     *
+     * @param exemptions List of hidden API exemption prefixes.
+     */
+    public void setApiBlacklistExemptions(List<String> exemptions) {
+        synchronized (mLock) {
+            mApiBlacklistExemptions = exemptions;
+            maybeSetApiBlacklistExemptions(primaryZygoteState, true);
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, true);
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void maybeSetApiBlacklistExemptions(ZygoteState state, boolean sendIfEmpty) {
+        if (state == null || state.isClosed()) {
+            return;
+        }
+        if (!sendIfEmpty && mApiBlacklistExemptions.isEmpty()) {
+            return;
+        }
+        try {
+            state.writer.write(Integer.toString(mApiBlacklistExemptions.size() + 1));
+            state.writer.newLine();
+            state.writer.write("--set-api-blacklist-exemptions");
+            state.writer.newLine();
+            for (int i = 0; i < mApiBlacklistExemptions.size(); ++i) {
+                state.writer.write(mApiBlacklistExemptions.get(i));
+                state.writer.newLine();
+            }
+            state.writer.flush();
+            int status = state.inputStream.readInt();
+            if (status != 0) {
+                Slog.e(LOG_TAG, "Failed to set API blacklist exemptions; status " + status);
+            }
+        } catch (IOException ioe) {
+            Slog.e(LOG_TAG, "Failed to set API blacklist exemptions", 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.
      */
@@ -467,8 +518,8 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(primaryZygoteState, false);
         }
-
         if (primaryZygoteState.matches(abi)) {
             return primaryZygoteState;
         }
@@ -480,6 +531,7 @@
             } catch (IOException ioe) {
                 throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
             }
+            maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
         }
 
         if (secondaryZygoteState.matches(abi)) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bf20e6a..8905ad1 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -756,10 +756,15 @@
         }
         try {
             for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
-                if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
+                if (vol.path != null && FileUtils.contains(vol.path, pathString)
+                        && vol.type != VolumeInfo.TYPE_PUBLIC) {
                     // TODO: verify that emulated adopted devices have UUID of
                     // underlying volume
-                    return convert(vol.fsUuid);
+                    try {
+                        return convert(vol.fsUuid);
+                    } catch (IllegalArgumentException e) {
+                        continue;
+                    }
                 }
             }
         } catch (RemoteException e) {
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index d3877ca..9e3e386 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -269,22 +269,7 @@
         return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
     }
 
-    public boolean isVisibleForRead(int userId) {
-        if (type == TYPE_PUBLIC) {
-            if (isPrimary() && mountUserId != userId) {
-                // Primary physical is only visible to single user
-                return false;
-            } else {
-                return isVisible();
-            }
-        } else if (type == TYPE_EMULATED) {
-            return isVisible();
-        } else {
-            return false;
-        }
-    }
-
-    public boolean isVisibleForWrite(int userId) {
+    public boolean isVisibleForUser(int userId) {
         if (type == TYPE_PUBLIC && mountUserId == userId) {
             return isVisible();
         } else if (type == TYPE_EMULATED) {
@@ -294,6 +279,14 @@
         }
     }
 
+    public boolean isVisibleForRead(int userId) {
+        return isVisibleForUser(userId);
+    }
+
+    public boolean isVisibleForWrite(int userId) {
+        return isVisibleForUser(userId);
+    }
+
     public File getPath() {
         return (path != null) ? new File(path) : null;
     }
@@ -319,7 +312,7 @@
      * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
      */
     public File getInternalPathForUser(int userId) {
-        if (type == TYPE_PUBLIC) {
+        if (type == TYPE_PUBLIC && !isVisible()) {
             // TODO: plumb through cleaner path from vold
             return new File(path.replace("/storage/", "/mnt/media_rw/"));
         } else {
@@ -409,9 +402,9 @@
      * Build an intent to browse the contents of this volume. Only valid for
      * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
      */
-    public Intent buildBrowseIntent() {
+    public @Nullable Intent buildBrowseIntent() {
         final Uri uri;
-        if (type == VolumeInfo.TYPE_PUBLIC) {
+        if (type == VolumeInfo.TYPE_PUBLIC && mountUserId == UserHandle.myUserId()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
         } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
             uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
diff --git a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
index 219868d..dd97f1e 100644
--- a/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
+++ b/core/java/android/privacy/internal/longitudinalreporting/LongitudinalReportingEncoder.java
@@ -19,6 +19,7 @@
 import android.privacy.DifferentialPrivacyEncoder;
 import android.privacy.internal.rappor.RapporConfig;
 import android.privacy.internal.rappor.RapporEncoder;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -48,6 +49,9 @@
  */
 public class LongitudinalReportingEncoder implements DifferentialPrivacyEncoder {
 
+    private static final String TAG = "LongitudinalEncoder";
+    private static final boolean DEBUG = false;
+
     // Suffix that will be added to Rappor's encoder id. There's a (relatively) small risk some
     // other Rappor encoder may re-use the same encoder id.
     private static final String PRR1_ENCODER_ID = "prr1_encoder_id";
@@ -121,11 +125,18 @@
 
     @Override
     public byte[] encodeBoolean(boolean original) {
+        if (DEBUG) {
+            Log.d(TAG, "encodeBoolean, encoderId:" + mConfig.getEncoderId() + ", original: "
+                    + original);
+        }
         if (mFakeValue != null) {
             // Use the fake value generated in PRR.
             original = mFakeValue.booleanValue();
+            if (DEBUG) Log.d(TAG, "Use fake value: " + original);
         }
-        return mIRREncoder.encodeBoolean(original);
+        byte[] result = mIRREncoder.encodeBoolean(original);
+        if (DEBUG) Log.d(TAG, "result: " + ((result[0] & 0x1) != 0));
+        return result;
     }
 
     @Override
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 8aef012..67c6fb9 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.Uri;
 import android.os.Bundle;
+import android.telecom.Log;
 
 /**
  * <p>
@@ -261,9 +262,16 @@
      */
     @WorkerThread
     public static boolean isBlocked(Context context, String phoneNumber) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
-        return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_IS_BLOCKED, phoneNumber, null);
+            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "isBlocked: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -297,9 +305,16 @@
      * @return {@code true} if the current user can block numbers.
      */
     public static boolean canCurrentUserBlockNumbers(Context context) {
-        final Bundle res = context.getContentResolver().call(
-                AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
-        return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        try {
+            final Bundle res = context.getContentResolver().call(
+                    AUTHORITY_URI, METHOD_CAN_CURRENT_USER_BLOCK_NUMBERS, null, null);
+            return res != null && res.getBoolean(RES_CAN_BLOCK_NUMBERS, false);
+        } catch (NullPointerException | IllegalArgumentException ex) {
+            // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+            // either of these happen.
+            Log.w(null, "canCurrentUserBlockNumbers: provider not ready.");
+            return false;
+        }
     }
 
     /**
@@ -368,8 +383,14 @@
          * the provider unless {@link #endBlockSuppression(Context)} is called.
          */
         public static void notifyEmergencyContact(Context context) {
-            context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            try {
+                context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_NOTIFY_EMERGENCY_CONTACT, null, null);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "notifyEmergencyContact: provider not ready.");
+            }
         }
 
         /**
@@ -394,9 +415,16 @@
          */
         public static boolean shouldSystemBlockNumber(Context context, String phoneNumber,
                 Bundle extras) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
-            return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SYSTEM_BLOCK_NUMBER, phoneNumber, extras);
+                return res != null && res.getBoolean(RES_NUMBER_IS_BLOCKED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldSystemBlockNumber: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -416,9 +444,16 @@
          * @return {@code true} if should show emergency call notification. {@code false} otherwise.
          */
         public static boolean shouldShowEmergencyCallNotification(Context context) {
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
-            return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_SHOULD_SHOW_EMERGENCY_CALL_NOTIFICATION, null, null);
+                return res != null && res.getBoolean(RES_SHOW_EMERGENCY_CALL_NOTIFICATION, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "shouldShowEmergencyCallNotification: provider not ready.");
+                return false;
+            }
         }
 
         /**
@@ -436,9 +471,16 @@
         public static boolean getEnhancedBlockSetting(Context context, String key) {
             Bundle extras = new Bundle();
             extras.putString(EXTRA_ENHANCED_SETTING_KEY, key);
-            final Bundle res = context.getContentResolver().call(
-                    AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
-            return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            try {
+                final Bundle res = context.getContentResolver().call(
+                        AUTHORITY_URI, METHOD_GET_ENHANCED_BLOCK_SETTING, null, extras);
+                return res != null && res.getBoolean(RES_ENHANCED_SETTING_IS_ENABLED, false);
+            } catch (NullPointerException | IllegalArgumentException ex) {
+                // The content resolver can throw an NPE or IAE; we don't want to crash Telecom if
+                // either of these happen.
+                Log.w(null, "getEnhancedBlockSetting: provider not ready.");
+                return false;
+            }
         }
 
         /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9ecae8c..b0367dc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7801,6 +7801,22 @@
                 "low_power_warning_acknowledged";
 
         /**
+         * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been
+         * suppressed.
+         * @hide
+         */
+        public static final String SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION =
+                "suppress_auto_battery_saver_suggestion";
+
+        /**
+         * List of packages, which data need to be unconditionally cleared before full restore.
+         * Type: string
+         * @hide
+         */
+        public static final String PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE =
+                "packages_to_clear_data_before_full_restore";
+
+        /**
          * This are the settings to be backed up.
          *
          * NOTE: Settings are backed up and restored in the order they appear
@@ -11045,7 +11061,14 @@
          */
         public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max";
 
-         /**
+        /**
+         * See com.android.settingslib.fuelgauge.BatterySaverUtils.
+         * @hide
+         */
+        public static final String LOW_POWER_MODE_SUGGESTION_PARAMS =
+                "low_power_mode_suggestion_params";
+
+        /**
          * If not 0, the activity manager will aggressively finish activities and
          * processes as soon as they are no longer needed.  If 0, the normal
          * extended lifetime is used.
diff --git a/core/java/android/se/omapi/Channel.java b/core/java/android/se/omapi/Channel.java
index 65ce67f..c8efede 100644
--- a/core/java/android/se/omapi/Channel.java
+++ b/core/java/android/se/omapi/Channel.java
@@ -47,7 +47,8 @@
     private final SEService mService;
     private final Object mLock = new Object();
 
-    Channel(SEService service, Session session, ISecureElementChannel channel) {
+    Channel(@NonNull SEService service, @NonNull Session session,
+            @NonNull ISecureElementChannel channel) {
         if (service == null || session == null || channel == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -158,7 +159,7 @@
      * @throws SecurityException if the command is filtered by the security policy.
      * @throws NullPointerException if command is NULL.
      */
-    public @NonNull byte[] transmit(byte[] command) throws IOException {
+    public @NonNull byte[] transmit(@NonNull byte[] command) throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
diff --git a/core/java/android/se/omapi/ISecureElementListener.aidl b/core/java/android/se/omapi/ISecureElementListener.aidl
index e0c6e04..e9dd181 100644
--- a/core/java/android/se/omapi/ISecureElementListener.aidl
+++ b/core/java/android/se/omapi/ISecureElementListener.aidl
@@ -24,8 +24,4 @@
  * @hide
  */
 interface ISecureElementListener {
-  /**
-   * Called by the framework when the service is connected.
-   */
-  void serviceConnected();
 }
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 3dec976..9be3da6 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -46,7 +46,7 @@
     private final Object mLock = new Object();
 
 
-    Reader(SEService service, String name, ISecureElementReader reader) {
+    Reader(@NonNull SEService service, @NonNull String name, @NonNull ISecureElementReader reader) {
         if (reader == null || service == null || name == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index d59e86a..311dc4c 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -62,17 +62,32 @@
     /**
      * Interface to send call-backs to the application when the service is connected.
      */
-    public abstract static class SecureElementListener extends ISecureElementListener.Stub {
+    public interface SecureElementListener {
+        /**
+         * Called by the framework when the service is connected.
+         */
+        void onServiceConnected();
+    }
+
+    /**
+     * Listener object that allows the notification of the caller if this
+     * SEService could be bound to the backend.
+     */
+    private class SEListener extends ISecureElementListener.Stub {
+        public SecureElementListener mListener = null;
+
         @Override
         public IBinder asBinder() {
             return this;
         }
 
-        /**
-         * Called by the framework when the service is connected.
-         */
-        public void serviceConnected() {};
+        public void onServiceConnected() {
+            if (mListener != null) {
+                mListener.onServiceConnected();
+            }
+        }
     }
+    private SEListener mSEListener = new SEListener();
 
     private static final String TAG = "OMAPI.SEService";
 
@@ -95,34 +110,28 @@
     private final HashMap<String, Reader> mReaders = new HashMap<String, Reader>();
 
     /**
-     * Listener object that allows the notification of the caller if this
-     * SEService could be bound to the backend.
-     */
-    private ISecureElementListener mSEListener;
-
-    /**
      * Establishes a new connection that can be used to connect to all the
      * Secure Elements available in the system. The connection process can be
      * quite long, so it happens in an asynchronous way. It is usable only if
      * the specified listener is called or if isConnected() returns
      * <code>true</code>. <br>
      * The call-back object passed as a parameter will have its
-     * serviceConnected() method called when the connection actually happen.
+     * onServiceConnected() method called when the connection actually happen.
      *
      * @param context
      *            the context of the calling application. Cannot be
      *            <code>null</code>.
      * @param listener
-     *            a SecureElementListener object. Can be <code>null</code>.
+     *            a SecureElementListener object.
      */
-    public SEService(Context context, SecureElementListener listener) {
+    public SEService(@NonNull Context context, @NonNull SecureElementListener listener) {
 
         if (context == null) {
             throw new NullPointerException("context must not be null");
         }
 
         mContext = context;
-        mSEListener = listener;
+        mSEListener.mListener = listener;
 
         mConnection = new ServiceConnection() {
 
@@ -131,9 +140,7 @@
 
                 mSecureElementService = ISecureElementService.Stub.asInterface(service);
                 if (mSEListener != null) {
-                    try {
-                        mSEListener.serviceConnected();
-                    } catch (RemoteException ignore) { }
+                    mSEListener.onServiceConnected();
                 }
                 Log.i(TAG, "Service onServiceConnected");
             }
@@ -233,7 +240,7 @@
      *
      * @return String containing the OpenMobile API version (e.g. "3.0").
      */
-    public String getVersion() {
+    public @NonNull String getVersion() {
         return "3.2";
     }
 
diff --git a/core/java/android/se/omapi/Session.java b/core/java/android/se/omapi/Session.java
index 3d8b74b..adfeddd 100644
--- a/core/java/android/se/omapi/Session.java
+++ b/core/java/android/se/omapi/Session.java
@@ -47,7 +47,8 @@
     private final ISecureElementSession mSession;
     private static final String TAG = "OMAPI.Session";
 
-    Session(SEService service, ISecureElementSession session, Reader reader) {
+    Session(@NonNull SEService service, @NonNull ISecureElementSession session,
+            @NonNull Reader reader) {
         if (service == null || reader == null || session == null) {
             throw new IllegalArgumentException("Parameters cannot be null");
         }
@@ -195,7 +196,8 @@
      *             supported by the device
      * @return an instance of Channel if available or null.
      */
-    public @Nullable Channel openBasicChannel(byte[] aid, byte p2) throws IOException {
+    public @Nullable Channel openBasicChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -223,32 +225,6 @@
     }
 
     /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openBasicChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array, or null if no Applet is to be selected.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element session is used after
-     *             being closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not available or cannot be
-     *             selected.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device
-     * @return an instance of Channel if available or null.
-     */
-    public @Nullable Channel openBasicChannel(byte[] aid) throws IOException {
-        return openBasicChannel(aid, (byte) 0x00);
-    }
-
-    /**
      * Open a logical channel with the Secure Element, selecting the Applet represented by
      * the given AID. If the AID is null, which means no Applet is to be selected on this
      * channel, the default Applet is used. It's up to the Secure Element to choose which
@@ -300,7 +276,8 @@
      * @return an instance of Channel. Null if the Secure Element is unable to
      *         provide a new logical channel.
      */
-    public @Nullable Channel openLogicalChannel(byte[] aid, byte p2) throws IOException {
+    public @Nullable Channel openLogicalChannel(@Nullable byte[] aid, @Nullable byte p2)
+            throws IOException {
         if (!mService.isConnected()) {
             throw new IllegalStateException("service not connected to system");
         }
@@ -327,32 +304,4 @@
             }
         }
     }
-
-    /**
-     * This method is provided to ease the development of mobile application and for compliancy
-     * with existing applications.
-     * This method is equivalent to openLogicalChannel(aid, P2=0x00)
-     *
-     * @param aid the AID of the Applet to be selected on this channel, as a
-     *            byte array.
-     * @throws IOException if there is a communication problem to the reader or
-     *             the Secure Element.
-     * @throws IllegalStateException if the Secure Element is used after being
-     *             closed.
-     * @throws IllegalArgumentException if the aid's length is not within 5 to
-     *             16 (inclusive).
-     * @throws SecurityException if the calling application cannot be granted
-     *             access to this AID or the default Applet on this
-     *             session.
-     * @throws NoSuchElementException if the AID on the Secure Element is not
-     *             available or cannot be selected or a logical channel is already
-     *             open to a non-multiselectable Applet.
-     * @throws UnsupportedOperationException if the given P2 parameter is not
-     *             supported by the device.
-     * @return an instance of Channel. Null if the Secure Element is unable to
-     *         provide a new logical channel.
-     */
-    public @Nullable Channel openLogicalChannel(byte[] aid) throws IOException {
-        return openLogicalChannel(aid, (byte) 0x00);
-    }
 }
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 1d13335..f4dcce1 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -75,6 +75,7 @@
     public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
     public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507;
     public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508;
+    public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509;
 
     public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
     public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
@@ -216,6 +217,7 @@
     public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58;
     public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59;
     public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66;
+    public static final int KM_ERROR_DEVICE_LOCKED = -72;
     public static final int KM_ERROR_UNIMPLEMENTED = -100;
     public static final int KM_ERROR_VERSION_MISMATCH = -101;
     public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -262,6 +264,7 @@
         sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH,
                 "Invalid MAC or authentication tag length");
         sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids");
+        sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked");
         sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
         sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
     }
diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java
index d50424d..741af12 100644
--- a/core/java/android/security/keystore/RecoveryController.java
+++ b/core/java/android/security/keystore/RecoveryController.java
@@ -443,16 +443,7 @@
      */
     public byte[] generateAndStoreKey(@NonNull String alias)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
-        try {
-            return mBinder.generateAndStoreKey(alias);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_INSECURE_USER) {
-                throw new LockScreenRequiredException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException();
     }
 
     /**
diff --git a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
index 2a66206..4af1af5 100644
--- a/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
+++ b/core/java/android/security/keystore/recovery/KeyChainProtectionParams.java
@@ -50,6 +50,22 @@
  */
 @SystemApi
 public final class KeyChainProtectionParams implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"TYPE_"}, value = {TYPE_LOCKSCREEN})
diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
index 24ff182..9334aa9 100644
--- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
+++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java
@@ -48,6 +48,22 @@
  */
 @SystemApi
 public final class KeyChainSnapshot implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     private static final int DEFAULT_MAX_ATTEMPTS = 10;
     private static final long DEFAULT_COUNTER_ID = 1L;
 
@@ -62,23 +78,8 @@
     private byte[] mEncryptedRecoveryKeyBlob;
 
     /**
-     * @hide
-     * Deprecated, consider using builder.
+     * Use builder to create an instance of the class.
      */
-    public KeyChainSnapshot(
-            int snapshotVersion,
-            @NonNull List<KeyChainProtectionParams> keyChainProtectionParams,
-            @NonNull List<WrappedApplicationKey> wrappedApplicationKeys,
-            @NonNull byte[] encryptedRecoveryKeyBlob) {
-        mSnapshotVersion = snapshotVersion;
-        mKeyChainProtectionParams =
-                Preconditions.checkCollectionElementsNotNull(keyChainProtectionParams,
-                        "KeyChainProtectionParams");
-        mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys,
-                "wrappedApplicationKeys");
-        mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
-    }
-
     private KeyChainSnapshot() {
 
     }
@@ -92,7 +93,7 @@
     }
 
     /**
-     * Number of user secret guesses allowed during Keychain recovery.
+     * Number of user secret guesses allowed during KeyChain recovery.
      */
     public int getMaxAttempts() {
         return mMaxAttempts;
diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
index 225b592..5165f0c 100644
--- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java
+++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java
@@ -35,6 +35,22 @@
  */
 @SystemApi
 public final class KeyDerivationParams implements Parcelable {
+
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     private final int mAlgorithm;
     private final byte[] mSalt;
     private final int mMemoryDifficulty;
@@ -59,7 +75,7 @@
      * Creates instance of the class to to derive keys using salted SHA256 hash.
      *
      * <p>The salted SHA256 hash is computed over the concatenation of four byte strings, salt_len +
-     * salt + key_material_len + key_material, where salt_len and key_material_len are one-byte, and
+     * salt + key_material_len + key_material, where salt_len and key_material_len are 4-byte, and
      * denote the number of bytes for salt and key_material, respectively.
      */
     public static @NonNull KeyDerivationParams createSha256Params(@NonNull byte[] salt) {
@@ -90,7 +106,7 @@
     /**
      * @hide
      */
-    KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
+    private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt,
             int memoryDifficulty) {
         mAlgorithm = algorithm;
         mSalt = Preconditions.checkNotNull(salt);
diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java
index ab52d32..a006fa6 100644
--- a/core/java/android/security/keystore/recovery/RecoveryController.java
+++ b/core/java/android/security/keystore/recovery/RecoveryController.java
@@ -41,21 +41,133 @@
 import java.util.Map;
 
 /**
- * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by
- * other Android devices belonging to the user. The exported keychain is protected by the user's
- * lock screen.
+ * Backs up cryptographic keys to remote secure hardware, encrypted with the user's lock screen.
  *
- * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible
- * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force
- * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10).
- * After that number of incorrect guesses, the trusted hardware no longer allows access to the
- * key chain.
+ * <p>A system app with the {@link android.Manifest#RECOVER_KEYSTORE} permission may generate or
+ * import recoverable keys using this class. To generate a key, the app must call
+ * {@link #generateKey(String)} with the desired alias for the key. This returns an AndroidKeyStore
+ * reference to a 256-bit {@link javax.crypto.SecretKey}, which can be used for AES/GCM/NoPadding.
+ * In order to get the same key again at a later time, the app can call {@link #getKey(String)} with
+ * the same alias. If a key is generated in this way the key's raw material is never directly
+ * exposed to the calling app. The system app may also import key material using
+ * {@link #importKey(String, byte[])}. The app may only generate and import keys for its own
+ * {@code uid}.
  *
- * <p>Only the recovery agent itself is able to create keys, so it is expected that the recovery
- * agent is itself the system app.
+ * <p>The same system app must also register a Recovery Agent to manage syncing recoverable keys to
+ * remote secure hardware. The Recovery Agent is a service that registers itself with the controller
+ * as follows:
  *
- * <p>A recovery agent requires the privileged permission
- * {@code android.Manifest.permission#RECOVER_KEYSTORE}.
+ * <ul>
+ *     <li>Invokes {@link #initRecoveryService(String, byte[], byte[])}
+ *     <ul>
+ *         <li>The first argument is the alias of the root certificate used to verify trusted
+ *         hardware modules. Each trusted hardware module must have a public key signed with this
+ *         root of trust. Roots of trust must be shipped with the framework. The app can list all
+ *         valid roots of trust by calling {@link #getRootCertificates()}.
+ *         <li>The second argument is the UTF-8 bytes of the XML listing file. It lists the X509
+ *         certificates containing the public keys of all available remote trusted hardware modules.
+ *         Each of the X509 certificates can be validated against the chosen root of trust.
+ *         <li>The third argument is the UTF-8 bytes of the XML signing file. The file contains a
+ *         signature of the XML listing file. The signature can be validated against the chosen root
+ *         of trust.
+ *     </ul>
+ *     <p>This will cause the controller to choose a random public key from the list. From then
+ *     on the controller will attempt to sync the key chain with the trusted hardware module to whom
+ *     that key belongs.
+ *     <li>Invokes {@link #setServerParams(byte[])} with a byte string that identifies the device
+ *     to a remote server. This server may act as the front-end to the trusted hardware modules. It
+ *     is up to the Recovery Agent to decide how best to identify devices, but this could be, e.g.,
+ *     based on the <a href="https://developers.google.com/instance-id/">Instance ID</a> of the
+ *     system app.
+ *     <li>Invokes {@link #setRecoverySecretTypes(int[])} with a list of types of secret used to
+ *     secure the recoverable key chain. For now only
+ *     {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} is supported.
+ *     <li>Invokes {@link #setSnapshotCreatedPendingIntent(PendingIntent)} with a
+ *     {@link PendingIntent} that is to be invoked whenever a new snapshot is created. Although the
+ *     controller can create snapshots without the Recovery Agent registering this intent, it is a
+ *     good idea to register the intent so that the Recovery Agent is able to sync this snapshot to
+ *     the trusted hardware module as soon as it is available.
+ * </ul>
+ *
+ * <p>The trusted hardware module's public key MUST be generated on secure hardware with protections
+ * equivalent to those described in the
+ * <a href="https://developer.android.com/preview/features/security/ckv-whitepaper.html">Google
+ * Cloud Key Vault Service whitepaper</a>. The trusted hardware module itself must protect the key
+ * chain from brute-forcing using the methods also described in the whitepaper: i.e., it should
+ * limit the number of allowed attempts to enter the lock screen. If the number of attempts is
+ * exceeded the key material must no longer be recoverable.
+ *
+ * <p>A recoverable key chain snapshot is considered pending if any of the following conditions
+ * are met:
+ *
+ * <ul>
+ *     <li>The system app mutates the key chain. i.e., generates, imports, or removes a key.
+ *     <li>The user changes their lock screen.
+ * </ul>
+ *
+ * <p>Whenever the user unlocks their device, if a snapshot is pending, the Recovery Controller
+ * generates a new snapshot. It follows these steps to do so:
+ *
+ * <ul>
+ *     <li>Generates a 256-bit AES key using {@link java.security.SecureRandom}. This is the
+ *     Recovery Key.
+ *     <li>Wraps the key material of all keys in the recoverable key chain with the Recovery Key.
+ *     <li>Encrypts the Recovery Key with both the public key of the trusted hardware module and a
+ *     symmetric key derived from the user's lock screen.
+ * </ul>
+ *
+ * <p>The controller then writes this snapshot to disk, and uses the {@link PendingIntent} that was
+ * set by the Recovery Agent during initialization to inform it that a new snapshot is available.
+ * The snapshot only contains keys for that Recovery Agent's {@code uid} - i.e., keys the agent's
+ * app itself generated. If multiple Recovery Agents exist on the device, each will be notified of
+ * their new snapshots, and each snapshots' keys will be only those belonging to the same
+ * {@code uid}.
+ *
+ * <p>The Recovery Agent retrieves its most recent snapshot by calling
+ * {@link #getKeyChainSnapshot()}. It syncs the snapshot to the remote server. The snapshot contains
+ * the public key used for encryption, which the server uses to forward the encrypted recovery key
+ * to the correct trusted hardware module. The snapshot also contains the server params, which are
+ * used to identify this device to the server.
+ *
+ * <p>The client uses the server params to identify a device whose key chain it wishes to restore.
+ * This may be on a different device to the device that originally synced the key chain. The client
+ * sends the server params identifying the previous device to the server. The server returns the
+ * X509 certificate identifying the trusted hardware module in which the encrypted Recovery Key is
+ * stored. It also returns some vault parameters identifying that particular Recovery Key to the
+ * trusted hardware module. And it also returns a vault challenge, which is used as part of the
+ * vault opening protocol to ensure the recovery claim is fresh. See the whitepaper for more
+ * details.
+ *
+ * <p>The key chain is recovered via a {@link RecoverySession}. A Recovery Agent creates one by
+ * invoking {@link #createRecoverySession()}. It then invokes
+ * {@link RecoverySession#start(String, CertPath, byte[], byte[], List)} with these arguments:
+ *
+ * <ul>
+ *     <li>The alias of the root of trust used to verify the trusted hardware module.
+ *     <li>The X509 certificate of the trusted hardware module.
+ *     <li>The vault parameters used to identify the Recovery Key to the trusted hardware module.
+ *     <li>The vault challenge, as issued by the trusted hardware module.
+ *     <li>A list of secrets, corresponding to the secrets used to protect the key chain. At the
+ *     moment this is a single {@link KeyChainProtectionParams} containing the lock screen of the
+ *     device whose key chain is to be recovered.
+ * </ul>
+ *
+ * <p>This method returns a byte array containing the Recovery Claim, which can be issued to the
+ * remote trusted hardware module. It is encrypted with the trusted hardware module's public key
+ * (which has itself been certified with the root of trust). It also contains an ephemeral symmetric
+ * key generated for this recovery session, which the remote trusted hardware module uses to encrypt
+ * its responses. This is the Session Key.
+ *
+ * <p>If the lock screen provided is correct, the remote trusted hardware module decrypts one of the
+ * layers of lock-screen encryption from the Recovery Key. It then returns this key, encrypted with
+ * the Session Key to the Recovery Agent. As the Recovery Agent does not know the Session Key, it
+ * must then invoke {@link RecoverySession#recoverKeyChainSnapshot(byte[], List)} with the encrypted
+ * Recovery Key and the list of wrapped application keys. The controller then decrypts the layer of
+ * encryption provided by the Session Key, and uses the lock screen to decrypt the final layer of
+ * encryption. It then uses the Recovery Key to decrypt all of the wrapped application keys, and
+ * imports them into its own KeyStore. The Recovery Agent's app may then access these keys by
+ * calling {@link #getKey(String)}. Only this app's {@code uid} may access the keys that have been
+ * recovered.
  *
  * @hide
  */
@@ -465,16 +577,7 @@
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     public byte[] generateAndStoreKey(@NonNull String alias, byte[] account)
             throws InternalRecoveryServiceException, LockScreenRequiredException {
-        try {
-            return mBinder.generateAndStoreKey(alias);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        } catch (ServiceSpecificException e) {
-            if (e.errorCode == ERROR_INSECURE_USER) {
-                throw new LockScreenRequiredException(e.getMessage());
-            }
-            throw wrapUnexpectedServiceSpecificException(e);
-        }
+        throw new UnsupportedOperationException("Operation is not supported, use generateKey");
     }
 
     /**
diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java
index 0690bd5..80845d9 100644
--- a/core/java/android/security/keystore/recovery/RecoverySession.java
+++ b/core/java/android/security/keystore/recovery/RecoverySession.java
@@ -157,8 +157,8 @@
      * @param vaultChallenge Data passed from server for this recovery session and used to prevent
      *     replay attacks.
      * @param secrets Secrets provided by user, the method only uses type and secret fields.
-     * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is
-     *     encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric
+     * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey
+     * and contains a proof of user secrets possession, session symmetric
      *     key and parameters necessary to identify the counter with the number of failed recovery
      *     attempts.
      * @throws CertificateException if the {@code verifierCertPath} is invalid.
@@ -228,7 +228,8 @@
      *
      * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
      * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
-     *     and session.
+     *     and session key generated by {@link #start}.
+     * @return {@code Map} from recovered keys aliases to their references.
      * @throws SessionExpiredException if {@code session} has since been closed.
      * @throws DecryptionFailedException if unable to decrypt the snapshot.
      * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service.
@@ -288,8 +289,7 @@
     }
 
     /**
-     * Deletes all data associated with {@code session}. Should not be invoked directly but via
-     * {@link RecoverySession#close()}.
+     * Deletes all data associated with {@code session}.
      */
     @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE)
     @Override
diff --git a/core/java/android/security/keystore/recovery/TrustedRootCertificates.java b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
index 383af42..63faac3 100644
--- a/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
+++ b/core/java/android/security/keystore/recovery/TrustedRootCertificates.java
@@ -37,6 +37,40 @@
 
     public static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS =
             "GoogleCloudKeyVaultServiceV1";
+    /**
+     * Certificate used for client-side end-to-end encryption tests.
+     * When recovery controller is initialized with the certificate, recovery snapshots will only
+     * contain application keys started with {@link INSECURE_KEY_ALIAS}.
+     * Recovery snapshot will only be created if device is unlocked with password started with
+     * {@link #INSECURE_PASSWORD_PREFIX}.
+     *
+     * @hide
+     */
+    public static final String TEST_ONLY_INSECURE_CERTIFICATE_ALIAS =
+            "TEST_ONLY_INSECURE_CERTIFICATE_ALIAS";
+
+    /**
+     * TODO: Add insecure certificate to TestApi.
+     * @hide
+     */
+    public static @NonNull X509Certificate getTestOnlyInsecureCertificate() {
+        return parseBase64Certificate(TEST_ONLY_INSECURE_CERTIFICATE_BASE64);
+    }
+    /**
+     * Keys, which alias starts with the prefix are not protected if
+     * recovery agent uses {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+     * @hide
+     */
+    public static final String INSECURE_KEY_ALIAS_PREFIX =
+            "INSECURE_KEY_ALIAS_KEY_MATERIAL_IS_NOT_PROTECTED_";
+    /**
+     * Prefix for insecure passwords with length 14.
+     * Passwords started with the prefix are not protected if recovery agent uses
+     * {@link #TEST_ONLY_INSECURE_CERTIFICATE_ALIAS} root certificate.
+     * @hide
+     */
+    public static final String INSECURE_PASSWORD_PREFIX =
+            "INSECURE_PSWD_";
 
     private static final String GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64 = ""
             + "MIIFJjCCAw6gAwIBAgIJAIobXsJlzhNdMA0GCSqGSIb3DQEBDQUAMCAxHjAcBgNV"
@@ -68,13 +102,43 @@
             + "/oM58v0orUWINtIc2hBlka36PhATYQiLf+AiWKnwhCaaHExoYKfQlMtXBodNvOK8"
             + "xqx69x05q/qbHKEcTHrsss630vxrp1niXvA=";
 
+    private static final String TEST_ONLY_INSECURE_CERTIFICATE_BASE64 = ""
+            + "MIIFMDCCAxigAwIBAgIJAIZ9/G8KQie9MA0GCSqGSIb3DQEBDQUAMCUxIzAhBgNV"
+            + "BAMMGlRlc3QgT25seSBVbnNlY3VyZSBSb290IENBMB4XDTE4MDMyODAwMzIyM1oX"
+            + "DTM4MDMyMzAwMzIyM1owJTEjMCEGA1UEAwwaVGVzdCBPbmx5IFVuc2VjdXJlIFJv"
+            + "b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDGxFNzAEyzSPmw"
+            + "E5gfuBXdXq++bl9Ep62V7Xn1UiejvmS+pRHT39pf/M7sl4Zr9ezanJTrFvf9+B85"
+            + "VGehdsD32TgfEjThcqaoQCI6pKkHYsUo7FZ5n+G3eE8oabWRZJMVo3QDjnnFYp7z"
+            + "20vnpjDofI2oQyxHcb/1yep+ca1+4lIvbUp/ybhNFqhRXAMcDXo7pyH38eUQ1JdK"
+            + "Q/QlBbShpFEqx1Y6KilKfTDf7Wenqr67LkaEim//yLZjlHzn/BpuRTrpo+XmJZx1"
+            + "P9CX9LGOXTtmsaCcYgD4yijOvV8aEsIJaf1kCIO558oH0oQc+0JG5aXeLN7BDlyZ"
+            + "vH0RdSx5nQLS9kj2I6nthOw/q00/L+S6A0m5jyNZOAl1SY78p+wO0d9eHbqQzJwf"
+            + "EsSq3qGAqlgQyyjp6oxHBqT9hZtN4rxw+iq0K1S4kmTLNF1FvmIB1BE+lNvvoGdY"
+            + "5G0b6Pe4R5JFn9LV3C3PEmSYnae7iG0IQlKmRADIuvfJ7apWAVanJPJAAWh2Akfp"
+            + "8Uxr02cHoY6o7vsEhJJOeMkipaBHThESm/XeFVubQzNfZ9gjQnB9ZX2v+lyj+WYZ"
+            + "SAz3RuXx6TlLrmWccMpQDR1ibcgyyjLUtX3kwZl2OxmJXitjuD7xlxvAXYob15N+"
+            + "K4xKHgxUDrbt2zU/tY0vgepAUg/xbwIDAQABo2MwYTAdBgNVHQ4EFgQUwyeNpYgs"
+            + "XXYvh9z0/lFrja7sV+swHwYDVR0jBBgwFoAUwyeNpYgsXXYvh9z0/lFrja7sV+sw"
+            + "DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQENBQAD"
+            + "ggIBAGuOsvMN5SD3RIQnMJtBpcHNrxun+QFjPZFlYCLfIPrUkHpn5O1iIIq8tVLd"
+            + "2V+12VKnToUEANsYBD3MP8XjP+6GZ7ZQ2rwLGvUABKSX4YXvmjEEXZUZp0y3tIV4"
+            + "kUDlbACzguPneZDp5Qo7YWH4orgqzHkn0sD/ikO5XrAqmzc245ewJlrf+V11mjcu"
+            + "ELfDrEejpPhi7Hk/ZNR0ftP737Hs/dNoCLCIaVNgYzBZhgo4kd220TeJu2ttW0XZ"
+            + "ldyShtpcOmyWKBgVseixR6L/3sspPHyAPXkSuRo0Eh1xvzDKCg9ttb0qoacTlXMF"
+            + "GkBpNzmVq67NWFGGa9UElift1mv6RfktPCAGZ+Ai8xUiKAUB0Eookpt/8gX9Senq"
+            + "yP/jMxkxXmHWxUu8+KnLvj6WLrfftuuD7u3cfc7j5kkrheDz3O4h4477GnqL5wdo"
+            + "9DuEsNc4FxJVz8Iy8RS6cJuW4pihYpM1Tyn7uopLnImpYzEY+R5aQqqr+q/A1diq"
+            + "ogbEKPH6oUiqJUwq3nD70gPBUKJmIzS4vLwLouqUHEm1k/MgHV/BkEU0uVHszPFa"
+            + "XUMMCHb0iT9P8LuZ7Ajer3SR/0TRVApCrk/6OV68e+6k/OFpM5kcZnNMD5ANyBri"
+            + "Tsz3NrDwSw4i4+Dsfh6A9dB/cEghw4skLaBxnQLQIgVeqCzK";
+
     /**
      * The X509 certificate of the trusted root CA cert for the recoverable key store service.
      *
      * TODO: Change it to the production certificate root CA before the final launch.
      */
     private static final X509Certificate GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_CERTIFICATE =
-            parseGoogleCloudKeyVaultServiceV1Certificate();
+            parseBase64Certificate(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
 
     private static final int NUMBER_OF_ROOT_CERTIFICATES = 1;
 
@@ -107,9 +171,9 @@
         return certificates;
     }
 
-    private static X509Certificate parseGoogleCloudKeyVaultServiceV1Certificate() {
+    private static X509Certificate parseBase64Certificate(String base64Certificate) {
         try {
-            return decodeBase64Cert(GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_BASE64);
+            return decodeBase64Cert(base64Certificate);
         } catch (CertificateException e) {
             // Should not happen
             throw new RuntimeException(e);
diff --git a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
index 714e35a..32952db 100644
--- a/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
+++ b/core/java/android/security/keystore/recovery/WrappedApplicationKey.java
@@ -42,6 +42,21 @@
     // The only supported format is AES-256 symmetric key.
     private byte[] mEncryptedKeyMaterial;
 
+    // IMPORTANT! PLEASE READ!
+    // -----------------------
+    // If you edit this file (e.g., to add new fields), please MAKE SURE to also do the following:
+    // - Update the #writeToParcel(Parcel) method below
+    // - Update the #(Parcel) constructor below
+    // - Update android.security.keystore.recovery.KeyChainSnapshotTest to make sure nobody
+    //     accidentally breaks your fields in the Parcel in the future.
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly serialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeyChainSnapshotSerializer to correctly deserialize your new field
+    // - Update com.android.server.locksettings.recoverablekeystore.serialization
+    //     .KeychainSnapshotSerializerTest to make sure nobody breaks serialization of your field
+    //     in the future.
+
     /**
      * Builder for creating {@link WrappedApplicationKey}.
      */
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index cf16749..1cd76d2 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -41,11 +41,11 @@
  *
  * <p>A field classification score is a {@code float} representing how well an
  * {@link AutofillValue} filled matches a expected value predicted by an autofill service
- * &mdash;a full-match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
+ * &mdash;a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
  *
- * <p>The exact score depends on the algorithm used to calculate it&mdash; the service must provide
+ * <p>The exact score depends on the algorithm used to calculate it&mdash;the service must provide
  * at least one default algorithm (which is used when the algorithm is not specified or is invalid),
- * but it could provide more (in which case the algorithm name should be specifiied by the caller
+ * but it could provide more (in which case the algorithm name should be specified by the caller
  * when calculating the scores).
  *
  * {@hide}
@@ -113,23 +113,67 @@
     /**
      * Calculates field classification scores in a batch.
      *
-     * <p>See {@link AutofillFieldClassificationService} for more info about field classification
-     * scores.
+     * <p>A field classification score is a {@code float} representing how well an
+     * {@link AutofillValue} filled matches a expected value predicted by an autofill service
+     * &mdash;a full match is {@code 1.0} (representing 100%), while a full mismatch is {@code 0.0}.
      *
-     * @param algorithm name of the algorithm to be used to calculate the scores. If invalid, the
-     * default algorithm will be used instead.
-     * @param args optional arguments to be passed to the algorithm.
+     * <p>The exact score depends on the algorithm used to calculate it&mdash;the service must
+     * provide at least one default algorithm (which is used when the algorithm is not specified
+     * or is invalid), but it could provide more (in which case the algorithm name should be
+     * specified by the caller when calculating the scores).
+     *
+     * <p>For example, if the service provides an algorithm named {@code EXACT_MATCH} that
+     * returns {@code 1.0} if all characters match or {@code 0.0} otherwise, a call to:
+     *
+     * <pre>
+     * service.onGetScores("EXACT_MATCH", null,
+     *   Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+     *   Arrays.asList("email1", "phone1"));
+     * </pre>
+     *
+     * <p>Returns:
+     *
+     * <pre>
+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 0.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * </pre>
+     *
+     * <p>If the same algorithm allows the caller to specify whether the comparisons should be
+     * case sensitive by passing a boolean option named {@code "case_sensitive"}, then a call to:
+     *
+     * <pre>
+     * Bundle algorithmOptions = new Bundle();
+     * algorithmOptions.putBoolean("case_sensitive", false);
+     *
+     * service.onGetScores("EXACT_MATCH", algorithmOptions,
+     *   Arrays.asList(AutofillValue.forText("email1"), AutofillValue.forText("PHONE1")),
+     *   Arrays.asList("email1", "phone1"));
+     * </pre>
+     *
+     * <p>Returns:
+     *
+     * <pre>
+     * [
+     *   [1.0, 0.0], // "email1" compared against ["email1", "phone1"]
+     *   [0.0, 1.0]  // "PHONE1" compared against ["email1", "phone1"]
+     * ];
+     * </pre>
+     *
+     * @param algorithm name of the algorithm to be used to calculate the scores. If invalid or
+     * {@code null}, the default algorithm is used instead.
+     * @param algorithmOptions optional arguments to be passed to the algorithm.
      * @param actualValues values entered by the user.
      * @param userDataValues values predicted from the user data.
-     * @return the calculated scores, with the first dimension representing actual values and the
-     * second dimension values from {@link UserData}.
+     * @return the calculated scores of {@code actualValues} x {@code userDataValues}.
      *
      * {@hide}
      */
     @Nullable
     @SystemApi
     public float[][] onGetScores(@Nullable String algorithm,
-            @Nullable Bundle args, @NonNull List<AutofillValue> actualValues,
+            @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
         Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
         return null;
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index df62446..6e5bacf 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -424,7 +424,7 @@
          * @return map map whose key is the id of the manually-entered field, and value is the
          * ids of the datasets that have that value but were not selected by the user.
          */
-        @Nullable public Map<AutofillId, Set<String>> getManuallyEnteredField() {
+        @NonNull public Map<AutofillId, Set<String>> getManuallyEnteredField() {
             if (mManuallyFilledFieldIds == null || mManuallyFilledDatasetIds == null) {
                 return Collections.emptyMap();
             }
diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java
index 8a7ff4d..0128710 100644
--- a/core/java/android/service/notification/ScheduleCalendar.java
+++ b/core/java/android/service/notification/ScheduleCalendar.java
@@ -144,7 +144,8 @@
         }
         return mSchedule.exitAtAlarm
                 && mSchedule.nextAlarm != 0
-                && time >= mSchedule.nextAlarm;
+                && time >= mSchedule.nextAlarm
+                && isInSchedule(mSchedule.nextAlarm);
     }
 
     private boolean isInSchedule(int daysOffset, long time, long start, long end) {
diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java
index f38482e..7f903b6 100644
--- a/core/java/android/text/InputType.java
+++ b/core/java/android/text/InputType.java
@@ -24,7 +24,7 @@
  * <h3>Examples</h3>
  *
  * <dl>
- * <dt>A password field with with the password visible to the user:
+ * <dt>A password field with the password visible to the user:
  * <dd>inputType = TYPE_CLASS_TEXT |
  *     TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
  *
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 9687009..eecdb74 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -37,7 +37,6 @@
     private static final Map<String, String> DEFAULT_FLAGS;
     static {
         DEFAULT_FLAGS = new HashMap<>();
-        DEFAULT_FLAGS.put("settings_battery_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
         DEFAULT_FLAGS.put("settings_zone_picker_v2", "true");
         DEFAULT_FLAGS.put("settings_about_phone_v2", "true");
diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java
index 3b8fc5c..f15e1a1 100644
--- a/core/java/android/util/apk/ApkVerityBuilder.java
+++ b/core/java/android/util/apk/ApkVerityBuilder.java
@@ -72,22 +72,31 @@
                 signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset;
         long dataSize = apk.length() - signingBlockSize;
         int[] levelOffset = calculateVerityLevelOffset(dataSize);
+        int merkleTreeSize = levelOffset[levelOffset.length - 1];
 
         ByteBuffer output = bufferFactory.create(
-                CHUNK_SIZE_BYTES +  // fsverity header + extensions + padding
-                levelOffset[levelOffset.length - 1]);  // Merkle tree size
+                merkleTreeSize
+                + CHUNK_SIZE_BYTES);  // maximum size of fsverity metadata
         output.order(ByteOrder.LITTLE_ENDIAN);
 
-        ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
-        ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit());
+        ByteBuffer tree = slice(output, 0, merkleTreeSize);
+        ByteBuffer header = slice(output, merkleTreeSize,
+                merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES);
+        ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES,
+                merkleTreeSize + CHUNK_SIZE_BYTES);
         byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES];
         ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes);
         apkDigest.order(ByteOrder.LITTLE_ENDIAN);
 
+        // NB: Buffer limit is set inside once finished.
         calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions);
 
-        output.rewind();
+        // Put the reverse offset to fs-verity header at the end.
+        output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit());
+        output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit()
+                + 4);  // size of this integer right before EOF
+        output.flip();
+
         return new ApkVerityResult(output, apkDigestBytes);
     }
 
@@ -101,7 +110,8 @@
         ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES)
                 .order(ByteOrder.LITTLE_ENDIAN);
         ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES);
-        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES);
+        ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES,
+                CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES);
 
         calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions);
 
@@ -328,10 +338,10 @@
         buffer.put((byte) 12);              // log2(block-size): log2(4096)
         buffer.put((byte) 7);               // log2(leaves-per-node): log2(4096 / 32)
 
-        buffer.putShort((short) 1);         // meta algorithm, SHA256_MODE == 1
-        buffer.putShort((short) 1);         // data algorithm, SHA256_MODE == 1
+        buffer.putShort((short) 1);         // meta algorithm, SHA256 == 1
+        buffer.putShort((short) 1);         // data algorithm, SHA256 == 1
 
-        buffer.putInt(0x0);                 // flags
+        buffer.putInt(0);                   // flags
         buffer.putInt(0);                   // reserved
 
         buffer.putLong(fileSize);           // original file size
@@ -362,12 +372,11 @@
         //
         // struct fsverity_extension_patch {
         //   __le64 offset;
-        //   u8 length;
-        //   u8 reserved[7];
         //   u8 databytes[];
         // };
 
         final int kSizeOfFsverityExtensionHeader = 8;
+        final int kExtensionSizeAlignment = 8;
 
         {
             // struct fsverity_extension #1
@@ -385,24 +394,25 @@
 
         {
             // struct fsverity_extension #2
-            final int kSizeOfFsverityPatchExtension =
-                    8 +  // offset size
-                    1 +  // size of length from offset (up to 255)
-                    7 +  // reserved
-                    ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
-            final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8);
+            final int kTotalSize = kSizeOfFsverityExtensionHeader
+                    + 8 // offset size
+                    + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE;
 
-            buffer.putShort((short)  // total size of extension, padded to 64-bit alignment
-                    (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding));
+            buffer.putShort((short) kTotalSize);
             buffer.put((byte) 1);    // ID of patch extension
             skip(buffer, 5);         // reserved
 
             // struct fsverity_extension_patch
-            buffer.putLong(eocdOffset);                                 // offset
-            buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE);  // length
-            skip(buffer, 7);                                            // reserved
-            buffer.putInt(Math.toIntExact(signingBlockOffset));         // databytes
-            skip(buffer, kPadding);                                     // padding
+            buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);  // offset
+            buffer.putInt(Math.toIntExact(signingBlockOffset));  // databytes
+
+            // The extension needs to be 0-padded at the end, since the length may not be multiple
+            // of 8.
+            int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment;
+            if (kPadding == kExtensionSizeAlignment) {
+                kPadding = 0;
+            }
+            skip(buffer, kPadding);                              // padding
         }
 
         buffer.flip();
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index d3b1e5c..df81a31 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -250,6 +250,18 @@
     }
 
     /**
+     * Destroys the HwuiContext without completely
+     * releasing the Surface.
+     * @hide
+     */
+    public void hwuiDestroy() {
+        if (mHwuiContext != null) {
+            mHwuiContext.destroy();
+            mHwuiContext = null;
+        }
+    }
+
+    /**
      * Returns true if this object holds a valid surface.
      *
      * @return True if it holds a physical surface, so lockCanvas() will succeed.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6393127..f6c669b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14927,10 +14927,6 @@
      * ImageView with only the foreground image. The default implementation returns true; subclasses
      * should override if they have cases which can be optimized.</p>
      *
-     * <p>The current implementation of the saveLayer and saveLayerAlpha methods in {@link Canvas}
-     * necessitates that a View return true if it uses the methods internally without passing the
-     * {@link Canvas#CLIP_TO_LAYER_SAVE_FLAG}.</p>
-     *
      * <p><strong>Note:</strong> The return value of this method is ignored if {@link
      * #forceHasOverlappingRendering(boolean)} has been called on this view.</p>
      *
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index abc19d0..f6181d7 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -236,6 +236,18 @@
     int TRANSIT_KEYGUARD_UNOCCLUDE = 23;
 
     /**
+     * A translucent activity is being opened.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+
+    /**
+     * A translucent activity is being closed.
+     * @hide
+     */
+    int TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
+
+    /**
      * @hide
      */
     @IntDef(prefix = { "TRANSIT_" }, value = {
@@ -258,7 +270,9 @@
             TRANSIT_KEYGUARD_GOING_AWAY,
             TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER,
             TRANSIT_KEYGUARD_OCCLUDE,
-            TRANSIT_KEYGUARD_UNOCCLUDE
+            TRANSIT_KEYGUARD_UNOCCLUDE,
+            TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+            TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface TransitionType {}
@@ -1833,7 +1847,9 @@
         public static final int SOFT_INPUT_MASK_STATE = 0x0f;
 
         /**
-         * Visibility state for {@link #softInputMode}: no state has been specified.
+         * Visibility state for {@link #softInputMode}: no state has been specified. The system may
+         * show or hide the software keyboard for better user experience when the window gains
+         * focus.
          */
         public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
 
@@ -2220,7 +2236,7 @@
         @IntDef(
                 flag = true,
                 value = {LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT,
-                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS,
+                        LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES,
                         LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER})
         @interface LayoutInDisplayCutoutMode {}
 
@@ -2231,10 +2247,11 @@
          * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
          *
          * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
+         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
          * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
          * @see DisplayCutout
-         * @see android.R.attr#layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         @LayoutInDisplayCutoutMode
         public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
@@ -2245,10 +2262,10 @@
          * laid out such that it does not overlap with the {@link DisplayCutout} area.
          *
          * <p>
-         * In practice, this means that if the window did not set FLAG_FULLSCREEN or
-         * SYSTEM_UI_FLAG_FULLSCREEN, it can extend into the cutout area in portrait if the cutout
-         * is at the top edge. Similarly for SYSTEM_UI_FLAG_HIDE_NAVIGATION and a cutout at the
-         * bottom of the screen.
+         * In practice, this means that if the window did not set {@link #FLAG_FULLSCREEN} or
+         * {@link View#SYSTEM_UI_FLAG_FULLSCREEN}, it can extend into the cutout area in portrait
+         * if the cutout is at the top edge. Similarly for
+         * {@link View#SYSTEM_UI_FLAG_HIDE_NAVIGATION} and a cutout at the bottom of the screen.
          * Otherwise (i.e. fullscreen or landscape) it is laid out such that it does not overlap the
          * cutout area.
          *
@@ -2258,6 +2275,9 @@
          *
          * @see DisplayCutout
          * @see WindowInsets
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT = 0;
 
@@ -2279,8 +2299,40 @@
          * The window must make sure that no important content overlaps with the
          * {@link DisplayCutout}.
          *
+         * <p>
+         * In this mode, the window extends under cutouts on the short edge of the display in both
+         * portrait and landscape, regardless of whether the window is hiding the system bars:<br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/fullscreen_top_no_letterbox.png"
+         * height="720"
+         * alt="Screenshot of a fullscreen activity on a display with a cutout at the top edge in
+         *         portrait, no letterbox is applied."/>
+         *
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/landscape_top_no_letterbox.png"
+         * width="720"
+         * alt="Screenshot of an activity on a display with a cutout at the top edge in landscape,
+         *         no letterbox is applied."/>
+         *
+         * <p>
+         * A cutout in the corner is considered to be on the short edge: <br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png"
+         * height="720"
+         * alt="Screenshot of a fullscreen activity on a display with a cutout in the corner in
+         *         portrait, no letterbox is applied."/>
+         *
+         * <p>
+         * On the other hand, should the cutout be on the long edge of the display, a letterbox will
+         * be applied such that the window does not extend into the cutout on either long edge:
+         * <br/>
+         * <img src="{@docRoot}reference/android/images/display_cutout/short_edge/portrait_side_letterbox.png"
+         * height="720"
+         * alt="Screenshot of an activity on a display with a cutout on the long edge in portrait,
+         *         letterbox is applied."/>
+         *
          * @see DisplayCutout
          * @see WindowInsets#getDisplayCutout()
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES = 1;
 
@@ -2288,12 +2340,14 @@
          * The window is never allowed to overlap with the DisplayCutout area.
          *
          * <p>
-         * This should be used with windows that transiently set SYSTEM_UI_FLAG_FULLSCREEN to
-         * avoid a relayout of the window when the flag is set or cleared.
+         * This should be used with windows that transiently set
+         * {@link View#SYSTEM_UI_FLAG_FULLSCREEN} or {@link View#SYSTEM_UI_FLAG_HIDE_NAVIGATION}
+         * to avoid a relayout of the window when the respective flag is set or cleared.
          *
          * @see DisplayCutout
-         * @see View#SYSTEM_UI_FLAG_FULLSCREEN SYSTEM_UI_FLAG_FULLSCREEN
-         * @see View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+         * @see #layoutInDisplayCutoutMode
+         * @see android.R.attr#windowLayoutInDisplayCutoutMode
+         *         android:windowLayoutInDisplayCutoutMode
          */
         public static final int LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER = 2;
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 01fd090..88300db 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -1819,13 +1819,22 @@
             final View[] views = client.autofillClientFindViewsByAutofillIdTraversal(
                     Helper.toArray(ids));
 
+            ArrayList<AutofillId> failedIds = null;
+
             for (int i = 0; i < itemCount; i++) {
                 final AutofillId id = ids.get(i);
                 final AutofillValue value = values.get(i);
                 final int viewId = id.getViewId();
                 final View view = views[i];
                 if (view == null) {
-                    Log.w(TAG, "autofill(): no View with id " + viewId);
+                    // Most likely view has been removed after the initial request was sent to the
+                    // the service; this is fine, but we need to update the view status in the
+                    // server side so it can be triggered again.
+                    Log.d(TAG, "autofill(): no View with id " + id);
+                    if (failedIds == null) {
+                        failedIds = new ArrayList<>();
+                    }
+                    failedIds.add(id);
                     continue;
                 }
                 if (id.isVirtual()) {
@@ -1859,12 +1868,28 @@
                 }
             }
 
+            if (failedIds != null) {
+                if (sVerbose) {
+                    Log.v(TAG, "autofill(): total failed views: " + failedIds);
+                }
+                try {
+                    mService.setAutofillFailure(mSessionId, failedIds, mContext.getUserId());
+                } catch (RemoteException e) {
+                    // In theory, we could ignore this error since it's not a big deal, but
+                    // in reality, we rather crash the app anyways, as the failure could be
+                    // a consequence of something going wrong on the server side...
+                    e.rethrowFromSystemServer();
+                }
+            }
+
             if (virtualValues != null) {
                 for (int i = 0; i < virtualValues.size(); i++) {
                     final View parent = virtualValues.keyAt(i);
                     final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
                     parent.autofill(childrenValues);
                     numApplied += childrenValues.size();
+                    // TODO: we should provide a callback so the parent can call failures; something
+                    // like notifyAutofillFailed(View view, int[] childrenIds);
                 }
             }
 
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 56f79ab..176df73 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -16,6 +16,8 @@
 
 package android.view.autofill;
 
+import java.util.List;
+
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -47,6 +49,7 @@
             in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
             boolean hasCallback, int flags, in ComponentName componentName, int sessionId,
             int action, boolean compatMode);
+    void setAutofillFailure(int sessionId, in List<AutofillId> ids, int userId);
     void finishSession(int sessionId, int userId);
     void cancelSession(int sessionId, int userId);
     void setAuthenticationResult(in Bundle data, int sessionId, int authenticationId, int userId);
diff --git a/core/java/android/view/textclassifier/DefaultLogger.java b/core/java/android/view/textclassifier/DefaultLogger.java
index b2f4e39..46ff442 100644
--- a/core/java/android/view/textclassifier/DefaultLogger.java
+++ b/core/java/android/view/textclassifier/DefaultLogger.java
@@ -39,7 +39,7 @@
 public final class DefaultLogger extends Logger {
 
     private static final String LOG_TAG = "DefaultLogger";
-    private static final String CLASSIFIER_ID = "androidtc";
+    static final String CLASSIFIER_ID = "androidtc";
 
     private static final int START_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_START;
     private static final int PREV_EVENT_DELTA = MetricsEvent.FIELD_SELECTION_SINCE_PREVIOUS;
diff --git a/core/java/android/view/textclassifier/Logger.java b/core/java/android/view/textclassifier/Logger.java
index 9c92fd4..c29d3e6 100644
--- a/core/java/android/view/textclassifier/Logger.java
+++ b/core/java/android/view/textclassifier/Logger.java
@@ -18,56 +18,25 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.StringDef;
 import android.content.Context;
-import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.text.BreakIterator;
 import java.util.Locale;
 import java.util.Objects;
-import java.util.UUID;
 
 /**
  * A helper for logging TextClassifier related events.
+ * @hide
  */
 public abstract class Logger {
 
-    /**
-     * Use this to specify an indeterminate positive index.
-     */
-    public static final int OUT_OF_BOUNDS = Integer.MAX_VALUE;
-
-    /**
-     * Use this to specify an indeterminate negative index.
-     */
-    public static final int OUT_OF_BOUNDS_NEGATIVE = Integer.MIN_VALUE;
-
     private static final String LOG_TAG = "Logger";
     /* package */ static final boolean DEBUG_LOG_ENABLED = true;
 
     private static final String NO_SIGNATURE = "";
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @StringDef({WIDGET_TEXTVIEW, WIDGET_WEBVIEW, WIDGET_EDITTEXT,
-            WIDGET_EDIT_WEBVIEW, WIDGET_CUSTOM_TEXTVIEW, WIDGET_CUSTOM_EDITTEXT,
-            WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW, WIDGET_UNKNOWN})
-    public @interface WidgetType {}
-
-    public static final String WIDGET_TEXTVIEW = "textview";
-    public static final String WIDGET_EDITTEXT = "edittext";
-    public static final String WIDGET_UNSELECTABLE_TEXTVIEW = "nosel-textview";
-    public static final String WIDGET_WEBVIEW = "webview";
-    public static final String WIDGET_EDIT_WEBVIEW = "edit-webview";
-    public static final String WIDGET_CUSTOM_TEXTVIEW = "customview";
-    public static final String WIDGET_CUSTOM_EDITTEXT = "customedit";
-    public static final String WIDGET_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
-    public static final String WIDGET_UNKNOWN = "unknown";
-
     private @SelectionEvent.InvocationMethod int mInvocationMethod;
     private SelectionEvent mPrevEvent;
     private SelectionEvent mSmartEvent;
@@ -108,7 +77,6 @@
         return false;
     }
 
-
     /**
      * Returns a token iterator for tokenizing text for logging purposes.
      */
@@ -299,6 +267,9 @@
                     // Selection did not change. Ignore event.
                     return;
                 }
+                break;
+            default:
+                // do nothing.
         }
 
         event.setEventTime(now);
@@ -325,9 +296,9 @@
         }
     }
 
-    private String startNewSession() {
+    private TextClassificationSessionId startNewSession() {
         endSession();
-        return UUID.randomUUID().toString();
+        return new TextClassificationSessionId();
     }
 
     private void endSession() {
@@ -372,12 +343,12 @@
         /**
          * @param context Context of the widget the logger logs for
          * @param widgetType a name for the widget being logged for. e.g.
-         *      {@link #WIDGET_TEXTVIEW}
+         *      {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
          * @param widgetVersion a string version info for the widget the logger logs for
          */
         public Config(
                 @NonNull Context context,
-                @WidgetType String widgetType,
+                @TextClassifier.WidgetType String widgetType,
                 @Nullable String widgetVersion) {
             mPackageName = Preconditions.checkNotNull(context).getPackageName();
             mWidgetType = widgetType;
@@ -392,7 +363,8 @@
         }
 
         /**
-         * Returns the name for the widget being logged for. e.g. {@link #WIDGET_TEXTVIEW}.
+         * Returns the name for the widget being logged for. e.g.
+         * {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}.
          */
         public String getWidgetType() {
             return mWidgetType;
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 7ac094e..5a4d2cf 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -17,10 +17,12 @@
 package android.view.textclassifier;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.textclassifier.TextClassifier.EntityType;
+import android.view.textclassifier.TextClassifier.WidgetType;
 
 import com.android.internal.util.Preconditions;
 
@@ -103,30 +105,33 @@
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({INVOCATION_MANUAL, INVOCATION_LINK})
+    @IntDef({INVOCATION_MANUAL, INVOCATION_LINK, INVOCATION_UNKNOWN})
     public @interface InvocationMethod {}
 
     /** Selection was invoked by the user long pressing, double tapping, or dragging to select. */
     public static final int INVOCATION_MANUAL = 1;
     /** Selection was invoked by the user tapping on a link. */
     public static final int INVOCATION_LINK = 2;
+    /** Unknown invocation method */
+    public static final int INVOCATION_UNKNOWN = 0;
+
+    private static final String NO_SIGNATURE = "";
 
     private final int mAbsoluteStart;
     private final int mAbsoluteEnd;
-    private final @EventType int mEventType;
     private final @EntityType String mEntityType;
-    @Nullable private final String mWidgetVersion;
-    private final String mPackageName;
-    private final String mWidgetType;
-    private final @InvocationMethod int mInvocationMethod;
 
-    // These fields should only be set by creator of a SelectionEvent.
-    private String mSignature;
+    private @EventType int mEventType;
+    private String mPackageName = "";
+    private String mWidgetType = TextClassifier.WIDGET_TYPE_UNKNOWN;
+    private @InvocationMethod int mInvocationMethod;
+    @Nullable private String mWidgetVersion;
+    private String mSignature;  // TODO: Rename to resultId.
     private long mEventTime;
     private long mDurationSinceSessionStart;
     private long mDurationSincePreviousEvent;
     private int mEventIndex;
-    @Nullable private String mSessionId;
+    @Nullable private TextClassificationSessionId mSessionId;
     private int mStart;
     private int mEnd;
     private int mSmartStart;
@@ -135,20 +140,29 @@
     SelectionEvent(
             int start, int end,
             @EventType int eventType, @EntityType String entityType,
-            @InvocationMethod int invocationMethod, String signature, Logger.Config config) {
+            @InvocationMethod int invocationMethod, String signature) {
         Preconditions.checkArgument(end >= start, "end cannot be less than start");
         mAbsoluteStart = start;
         mAbsoluteEnd = end;
         mEventType = eventType;
         mEntityType = Preconditions.checkNotNull(entityType);
         mSignature = Preconditions.checkNotNull(signature);
-        Preconditions.checkNotNull(config);
-        mWidgetVersion = config.getWidgetVersion();
-        mPackageName = Preconditions.checkNotNull(config.getPackageName());
-        mWidgetType = Preconditions.checkNotNull(config.getWidgetType());
         mInvocationMethod = invocationMethod;
     }
 
+    SelectionEvent(
+            int start, int end,
+            @EventType int eventType, @EntityType String entityType,
+            @InvocationMethod int invocationMethod, String signature, Logger.Config config) {
+        this(start, end, eventType, entityType, invocationMethod, signature);
+        Preconditions.checkNotNull(config);
+        setTextClassificationSessionContext(
+                new TextClassificationContext.Builder(
+                        config.getPackageName(), config.getWidgetType())
+                        .setWidgetVersion(config.getWidgetVersion())
+                        .build());
+    }
+
     private SelectionEvent(Parcel in) {
         mAbsoluteStart = in.readInt();
         mAbsoluteEnd = in.readInt();
@@ -163,7 +177,8 @@
         mDurationSinceSessionStart = in.readLong();
         mDurationSincePreviousEvent = in.readLong();
         mEventIndex = in.readInt();
-        mSessionId = in.readInt() > 0 ? in.readString() : null;
+        mSessionId = in.readInt() > 0
+                ? TextClassificationSessionId.CREATOR.createFromParcel(in) : null;
         mStart = in.readInt();
         mEnd = in.readInt();
         mSmartStart = in.readInt();
@@ -190,7 +205,7 @@
         dest.writeInt(mEventIndex);
         dest.writeInt(mSessionId != null ? 1 : 0);
         if (mSessionId != null) {
-            dest.writeString(mSessionId);
+            mSessionId.writeToParcel(dest, flags);
         }
         dest.writeInt(mStart);
         dest.writeInt(mEnd);
@@ -203,6 +218,156 @@
         return 0;
     }
 
+    /**
+     * Creates a "selection started" event.
+     *
+     * @param invocationMethod  the way the selection was triggered
+     * @param start  the index of the selected text
+     */
+    @NonNull
+    public static SelectionEvent createSelectionStartedEvent(
+            @SelectionEvent.InvocationMethod int invocationMethod, int start) {
+        return new SelectionEvent(
+                start, start + 1, SelectionEvent.EVENT_SELECTION_STARTED,
+                TextClassifier.TYPE_UNKNOWN, invocationMethod, NO_SIGNATURE);
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when the user modifies the selection.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(int start, int end) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN, NO_SIGNATURE);
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when the user modifies the selection and the selection's entity type is known.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(
+            int start, int end, @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_SELECTION_MODIFIED,
+                entityType, INVOCATION_UNKNOWN, classification.getSignature());
+    }
+
+    /**
+     * Creates a "selection modified" event.
+     * Use when a TextClassifier modifies the selection.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param selection  the TextSelection object returned by the TextClassifier for the
+     *      specified selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionModifiedEvent(
+            int start, int end, @NonNull TextSelection selection) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(selection);
+        final String entityType = selection.getEntityCount() > 0
+                ? selection.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(
+                start, end, SelectionEvent.EVENT_AUTO_SELECTION,
+                entityType, INVOCATION_UNKNOWN, selection.getSignature());
+    }
+
+    /**
+     * Creates an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     *
+     * @throws IllegalArgumentException if end is less than start
+     */
+    @NonNull
+    public static SelectionEvent createSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        checkActionType(actionType);
+        return new SelectionEvent(
+                start, end, actionType, TextClassifier.TYPE_UNKNOWN, INVOCATION_UNKNOWN,
+                NO_SIGNATURE);
+    }
+
+    /**
+     * Creates an event specifying an action taken on a selection.
+     * Use when the user clicks on an action to act on the selected text and the selection's
+     * entity type is known.
+     *
+     * @param start  the start (inclusive) index of the selection
+     * @param end  the end (exclusive) index of the selection
+     * @param actionType  the action that was performed on the selection
+     * @param classification  the TextClassification object returned by the TextClassifier that
+     *      classified the selected text
+     *
+     * @throws IllegalArgumentException if end is less than start
+     * @throws IllegalArgumentException If actionType is not a valid SelectionEvent actionType
+     */
+    @NonNull
+    public static SelectionEvent createSelectionActionEvent(
+            int start, int end, @SelectionEvent.ActionType int actionType,
+            @NonNull TextClassification classification) {
+        Preconditions.checkArgument(end >= start, "end cannot be less than start");
+        Preconditions.checkNotNull(classification);
+        checkActionType(actionType);
+        final String entityType = classification.getEntityCount() > 0
+                ? classification.getEntity(0)
+                : TextClassifier.TYPE_UNKNOWN;
+        return new SelectionEvent(start, end, actionType, entityType, INVOCATION_UNKNOWN,
+                classification.getSignature());
+    }
+
+    /**
+     * @throws IllegalArgumentException If eventType is not an {@link SelectionEvent.ActionType}
+     */
+    private static void checkActionType(@SelectionEvent.EventType int eventType)
+            throws IllegalArgumentException {
+        switch (eventType) {
+            case SelectionEvent.ACTION_OVERTYPE:  // fall through
+            case SelectionEvent.ACTION_COPY:  // fall through
+            case SelectionEvent.ACTION_PASTE:  // fall through
+            case SelectionEvent.ACTION_CUT:  // fall through
+            case SelectionEvent.ACTION_SHARE:  // fall through
+            case SelectionEvent.ACTION_SMART_SHARE:  // fall through
+            case SelectionEvent.ACTION_DRAG:  // fall through
+            case SelectionEvent.ACTION_ABANDON:  // fall through
+            case SelectionEvent.ACTION_SELECT_ALL:  // fall through
+            case SelectionEvent.ACTION_RESET:  // fall through
+                return;
+            default:
+                throw new IllegalArgumentException(
+                        String.format(Locale.US, "%d is not an eventType", eventType));
+        }
+    }
+
     int getAbsoluteStart() {
         return mAbsoluteStart;
     }
@@ -214,15 +379,24 @@
     /**
      * Returns the type of event that was triggered. e.g. {@link #ACTION_COPY}.
      */
+    @EventType
     public int getEventType() {
         return mEventType;
     }
 
     /**
+     * Sets the event type.
+     */
+    void setEventType(@EventType int eventType) {
+        mEventType = eventType;
+    }
+
+    /**
      * Returns the type of entity that is associated with this event. e.g.
      * {@link android.view.textclassifier.TextClassifier#TYPE_EMAIL}.
      */
     @EntityType
+    @NonNull
     public String getEntityType() {
         return mEntityType;
     }
@@ -230,6 +404,7 @@
     /**
      * Returns the package name of the app that this event originated in.
      */
+    @NonNull
     public String getPackageName() {
         return mPackageName;
     }
@@ -237,6 +412,8 @@
     /**
      * Returns the type of widget that was involved in triggering this event.
      */
+    @WidgetType
+    @NonNull
     public String getWidgetType() {
         return mWidgetType;
     }
@@ -244,11 +421,21 @@
     /**
      * Returns a string version info for the widget this event was triggered in.
      */
+    @Nullable
     public String getWidgetVersion() {
         return mWidgetVersion;
     }
 
     /**
+     * Sets the {@link TextClassificationContext} for this event.
+     */
+    void setTextClassificationSessionContext(TextClassificationContext context) {
+        mPackageName = context.getPackageName();
+        mWidgetType = context.getWidgetType();
+        mWidgetVersion = context.getWidgetVersion();
+    }
+
+    /**
      * Returns the way the selection mode was invoked.
      */
     public @InvocationMethod int getInvocationMethod() {
@@ -256,6 +443,13 @@
     }
 
     /**
+     * Sets the invocationMethod for this event.
+     */
+    void setInvocationMethod(@InvocationMethod int invocationMethod) {
+        mInvocationMethod = invocationMethod;
+    }
+
+    /**
      * Returns the signature of the text classifier result associated with this event.
      */
     public String getSignature() {
@@ -320,17 +514,18 @@
     /**
      * Returns the selection session id.
      */
-    public String getSessionId() {
+    @Nullable
+    public TextClassificationSessionId getSessionId() {
         return mSessionId;
     }
 
-    SelectionEvent setSessionId(String id) {
+    SelectionEvent setSessionId(TextClassificationSessionId id) {
         mSessionId = id;
         return this;
     }
 
     /**
-     * Returns the start index of this events token relative to the index of the start selection
+     * Returns the start index of this events relative to the index of the start selection
      * event in the selection session.
      */
     public int getStart() {
@@ -343,7 +538,7 @@
     }
 
     /**
-     * Returns the end index of this events token relative to the index of the start selection
+     * Returns the end index of this events relative to the index of the start selection
      * event in the selection session.
      */
     public int getEnd() {
@@ -356,7 +551,7 @@
     }
 
     /**
-     * Returns the start index of this events token relative to the index of the smart selection
+     * Returns the start index of this events relative to the index of the smart selection
      * event in the selection session.
      */
     public int getSmartStart() {
@@ -369,7 +564,7 @@
     }
 
     /**
-     * Returns the end index of this events token relative to the index of the smart selection
+     * Returns the end index of this events relative to the index of the smart selection
      * event in the selection session.
      */
     public int getSmartEnd() {
@@ -382,7 +577,15 @@
     }
 
     boolean isTerminal() {
-        switch (mEventType) {
+        return isTerminal(mEventType);
+    }
+
+    /**
+     * Returns true if the eventType is a terminal event type. Otherwise returns false.
+     * A terminal event is an event that ends a selection interaction.
+     */
+    public static boolean isTerminal(@EventType int eventType) {
+        switch (eventType) {
             case ACTION_OVERTYPE:  // fall through
             case ACTION_COPY:  // fall through
             case ACTION_PASTE:  // fall through
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index b5c9de9..b413d48 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -21,6 +21,8 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -41,8 +43,9 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
-import java.util.Calendar;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -77,25 +80,16 @@
  *   view.startActionMode(new ActionMode.Callback() {
  *
  *       public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- *           // Add the "primary" action.
- *           if (thisAppHasPermissionToInvokeIntent(classification.getIntent())) {
- *              menu.add(Menu.NONE, 0, 20, classification.getLabel())
- *                 .setIcon(classification.getIcon())
- *                 .setIntent(classification.getIntent());
- *           }
- *           // Add the "secondary" actions.
- *           for (int i = 0; i < classification.getSecondaryActionsCount(); i++) {
- *               if (thisAppHasPermissionToInvokeIntent(classification.getSecondaryIntent(i))) {
- *                  menu.add(Menu.NONE, i + 1, 20, classification.getSecondaryLabel(i))
- *                      .setIcon(classification.getSecondaryIcon(i))
- *                      .setIntent(classification.getSecondaryIntent(i));
- *               }
+ *           for (int i = 0; i < classification.getActions().size(); ++i) {
+ *              RemoteAction action = classification.getActions().get(i);
+ *              menu.add(Menu.NONE, i, 20, action.getTitle())
+ *                 .setIcon(action.getIcon());
  *           }
  *           return true;
  *       }
  *
  *       public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- *           context.startActivity(item.getIntent());
+ *           classification.getActions().get(item.getItemId()).getActionIntent().send();
  *           return true;
  *       }
  *
@@ -110,9 +104,9 @@
      */
     static final TextClassification EMPTY = new TextClassification.Builder().build();
 
+    private static final String LOG_TAG = "TextClassification";
     // TODO(toki): investigate a way to derive this based on device properties.
-    private static final int MAX_PRIMARY_ICON_SIZE = 192;
-    private static final int MAX_SECONDARY_ICON_SIZE = 144;
+    private static final int MAX_LEGACY_ICON_SIZE = 192;
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(value = {IntentType.UNSUPPORTED, IntentType.ACTIVITY, IntentType.SERVICE})
@@ -123,37 +117,29 @@
     }
 
     @NonNull private final String mText;
-    @Nullable private final Drawable mPrimaryIcon;
-    @Nullable private final String mPrimaryLabel;
-    @Nullable private final Intent mPrimaryIntent;
-    @Nullable private final OnClickListener mPrimaryOnClickListener;
-    @NonNull private final List<Drawable> mSecondaryIcons;
-    @NonNull private final List<String> mSecondaryLabels;
-    @NonNull private final List<Intent> mSecondaryIntents;
+    @Nullable private final Drawable mLegacyIcon;
+    @Nullable private final String mLegacyLabel;
+    @Nullable private final Intent mLegacyIntent;
+    @Nullable private final OnClickListener mLegacyOnClickListener;
+    @NonNull private final List<RemoteAction> mActions;
     @NonNull private final EntityConfidence mEntityConfidence;
     @NonNull private final String mSignature;
 
     private TextClassification(
             @Nullable String text,
-            @Nullable Drawable primaryIcon,
-            @Nullable String primaryLabel,
-            @Nullable Intent primaryIntent,
-            @Nullable OnClickListener primaryOnClickListener,
-            @NonNull List<Drawable> secondaryIcons,
-            @NonNull List<String> secondaryLabels,
-            @NonNull List<Intent> secondaryIntents,
+            @Nullable Drawable legacyIcon,
+            @Nullable String legacyLabel,
+            @Nullable Intent legacyIntent,
+            @Nullable OnClickListener legacyOnClickListener,
+            @NonNull List<RemoteAction> actions,
             @NonNull Map<String, Float> entityConfidence,
             @NonNull String signature) {
-        Preconditions.checkArgument(secondaryLabels.size() == secondaryIntents.size());
-        Preconditions.checkArgument(secondaryIcons.size() == secondaryIntents.size());
         mText = text;
-        mPrimaryIcon = primaryIcon;
-        mPrimaryLabel = primaryLabel;
-        mPrimaryIntent = primaryIntent;
-        mPrimaryOnClickListener = primaryOnClickListener;
-        mSecondaryIcons = secondaryIcons;
-        mSecondaryLabels = secondaryLabels;
-        mSecondaryIntents = secondaryIntents;
+        mLegacyIcon = legacyIcon;
+        mLegacyLabel = legacyLabel;
+        mLegacyIntent = legacyIntent;
+        mLegacyOnClickListener = legacyOnClickListener;
+        mActions = Collections.unmodifiableList(actions);
         mEntityConfidence = new EntityConfidence(entityConfidence);
         mSignature = signature;
     }
@@ -197,108 +183,57 @@
     }
 
     /**
-     * Returns the number of <i>secondary</i> actions that are available to act on the classified
-     * text.
-     *
-     * <p><strong>Note: </strong> that there may or may not be a <i>primary</i> action.
-     *
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryLabel(int)
-     * @see #getSecondaryIcon(int)
+     * Returns a list of actions that may be performed on the text. The list is ordered based on
+     * the likelihood that a user will use the action, with the most likely action appearing first.
      */
-    @IntRange(from = 0)
-    public int getSecondaryActionsCount() {
-        return mSecondaryIntents.size();
+    public List<RemoteAction> getActions() {
+        return mActions;
     }
 
     /**
-     * Returns one of the <i>secondary</i> icons that maybe rendered on a widget used to act on the
-     * classified text.
+     * Returns an icon that may be rendered on a widget used to act on the classified text.
      *
-     * @param index Index of the action to get the icon for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount() for the number of actions available.
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryLabel(int)
-     * @see #getIcon()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public Drawable getSecondaryIcon(int index) {
-        return mSecondaryIcons.get(index);
-    }
-
-    /**
-     * Returns an icon for the <i>primary</i> intent that may be rendered on a widget used to act
-     * on the classified text.
-     *
-     * @see #getSecondaryIcon(int)
-     */
+    @Deprecated
     @Nullable
     public Drawable getIcon() {
-        return mPrimaryIcon;
+        return mLegacyIcon;
     }
 
     /**
-     * Returns one of the <i>secondary</i> labels that may be rendered on a widget used to act on
-     * the classified text.
+     * Returns a label that may be rendered on a widget used to act on the classified text.
      *
-     * @param index Index of the action to get the label for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount()
-     * @see #getSecondaryIntent(int)
-     * @see #getSecondaryIcon(int)
-     * @see #getLabel()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public CharSequence getSecondaryLabel(int index) {
-        return mSecondaryLabels.get(index);
-    }
-
-    /**
-     * Returns a label for the <i>primary</i> intent that may be rendered on a widget used to act
-     * on the classified text.
-     *
-     * @see #getSecondaryLabel(int)
-     */
+    @Deprecated
     @Nullable
     public CharSequence getLabel() {
-        return mPrimaryLabel;
+        return mLegacyLabel;
     }
 
     /**
-     * Returns one of the <i>secondary</i> intents that may be fired to act on the classified text.
+     * Returns an intent that may be fired to act on the classified text.
      *
-     * @param index Index of the action to get the intent for.
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getSecondaryActionsCount()
-     * @see #getSecondaryLabel(int)
-     * @see #getSecondaryIcon(int)
-     * @see #getIntent()
+     * @deprecated Use {@link #getActions()} instead.
      */
-    @Nullable
-    public Intent getSecondaryIntent(int index) {
-        return mSecondaryIntents.get(index);
-    }
-
-    /**
-     * Returns the <i>primary</i> intent that may be fired to act on the classified text.
-     *
-     * @see #getSecondaryIntent(int)
-     */
+    @Deprecated
     @Nullable
     public Intent getIntent() {
-        return mPrimaryIntent;
+        return mLegacyIntent;
     }
 
     /**
-     * Returns the <i>primary</i> OnClickListener that may be triggered to act on the classified
-     * text. This field is not parcelable and will be null for all objects read from a parcel.
-     * Instead, call Context#startActivity(Intent) with the result of #getSecondaryIntent(int).
-     * Note that this may fail if the activity doesn't have permission to send the intent.
+     * Returns the OnClickListener that may be triggered to act on the classified text. This field
+     * is not parcelable and will be null for all objects read from a parcel. Instead, call
+     * Context#startActivity(Intent) with the result of #getSecondaryIntent(int). Note that this may
+     * fail if the activity doesn't have permission to send the intent.
+     *
+     * @deprecated Use {@link #getActions()} instead.
      */
     @Nullable
     public OnClickListener getOnClickListener() {
-        return mPrimaryOnClickListener;
+        return mLegacyOnClickListener;
     }
 
     /**
@@ -313,32 +248,42 @@
 
     @Override
     public String toString() {
-        return String.format(Locale.US, "TextClassification {"
-                        + "text=%s, entities=%s, "
-                        + "primaryLabel=%s, secondaryLabels=%s, "
-                        + "primaryIntent=%s, secondaryIntents=%s, "
-                        + "signature=%s}",
-                mText, mEntityConfidence,
-                mPrimaryLabel, mSecondaryLabels,
-                mPrimaryIntent, mSecondaryIntents,
-                mSignature);
+        return String.format(Locale.US,
+                "TextClassification {text=%s, entities=%s, actions=%s, signature=%s}",
+                mText, mEntityConfidence, mActions, mSignature);
     }
 
     /**
-     * Creates an OnClickListener that triggers the specified intent.
+     * Creates an OnClickListener that triggers the specified PendingIntent.
+     *
+     * @hide
+     */
+    public static OnClickListener createIntentOnClickListener(@NonNull final PendingIntent intent) {
+        Preconditions.checkNotNull(intent);
+        return v -> {
+            try {
+                intent.send();
+            } catch (PendingIntent.CanceledException e) {
+                Log.e(LOG_TAG, "Error creating OnClickListener from PendingIntent", e);
+            }
+        };
+    }
+
+    /**
+     * Creates a PendingIntent for the specified intent.
      * Returns null if the intent is not supported for the specified context.
      *
      * @throws IllegalArgumentException if context or intent is null
      * @hide
      */
     @Nullable
-    public static OnClickListener createIntentOnClickListener(
+    public static PendingIntent createPendingIntent(
             @NonNull final Context context, @NonNull final Intent intent) {
         switch (getIntentType(intent, context)) {
             case IntentType.ACTIVITY:
-                return v -> context.startActivity(intent);
+                return PendingIntent.getActivity(context, 0, intent, 0);
             case IntentType.SERVICE:
-                return v -> context.startService(intent);
+                return PendingIntent.getService(context, 0, intent, 0);
             default:
                 return null;
         }
@@ -434,33 +379,6 @@
     }
 
     /**
-     * Returns a list of drawables converted to Bitmaps
-     *
-     * @param drawables The drawables to convert.
-     * @param maxDims The maximum edge length of the resulting bitmaps (in pixels).
-     */
-    private static List<Bitmap> drawablesToBitmaps(List<Drawable> drawables, int maxDims) {
-        final List<Bitmap> bitmaps = new ArrayList<>(drawables.size());
-        for (Drawable drawable : drawables) {
-            bitmaps.add(drawableToBitmap(drawable, maxDims));
-        }
-        return bitmaps;
-    }
-
-    /** Returns a list of drawable wrappers for a list of bitmaps. */
-    private static List<Drawable> bitmapsToDrawables(List<Bitmap> bitmaps) {
-        final List<Drawable> drawables = new ArrayList<>(bitmaps.size());
-        for (Bitmap bitmap : bitmaps) {
-            if (bitmap != null) {
-                drawables.add(new BitmapDrawable(Resources.getSystem(), bitmap));
-            } else {
-                drawables.add(null);
-            }
-        }
-        return drawables;
-    }
-
-    /**
      * Builder for building {@link TextClassification} objects.
      *
      * <p>e.g.
@@ -470,23 +388,20 @@
      *          .setText(classifiedText)
      *          .setEntityType(TextClassifier.TYPE_EMAIL, 0.9)
      *          .setEntityType(TextClassifier.TYPE_OTHER, 0.1)
-     *          .setPrimaryAction(intent, label, icon)
-     *          .addSecondaryAction(intent1, label1, icon1)
-     *          .addSecondaryAction(intent2, label2, icon2)
+     *          .addAction(remoteAction1)
+     *          .addAction(remoteAction2)
      *          .build();
      * }</pre>
      */
     public static final class Builder {
 
         @NonNull private String mText;
-        @NonNull private final List<Drawable> mSecondaryIcons = new ArrayList<>();
-        @NonNull private final List<String> mSecondaryLabels = new ArrayList<>();
-        @NonNull private final List<Intent> mSecondaryIntents = new ArrayList<>();
+        @NonNull private List<RemoteAction> mActions = new ArrayList<>();
         @NonNull private final Map<String, Float> mEntityConfidence = new ArrayMap<>();
-        @Nullable Drawable mPrimaryIcon;
-        @Nullable String mPrimaryLabel;
-        @Nullable Intent mPrimaryIntent;
-        @Nullable OnClickListener mPrimaryOnClickListener;
+        @Nullable Drawable mLegacyIcon;
+        @Nullable String mLegacyLabel;
+        @Nullable Intent mLegacyIntent;
+        @Nullable OnClickListener mLegacyOnClickListener;
         @NonNull private String mSignature = "";
 
         /**
@@ -514,60 +429,25 @@
         }
 
         /**
-         * Adds an <i>secondary</i> action that may be performed on the classified text.
-         * Secondary actions are in addition to the <i>primary</i> action which may or may not
-         * exist.
-         *
-         * <p>The label and icon are used for rendering of widgets that offer the intent.
-         * Actions should be added in order of priority.
-         *
-         * <p><stong>Note: </stong> If all input parameters are set to null, this method will be a
-         * no-op.
-         *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * Adds an action that may be performed on the classified text. Actions should be added in
+         * order of likelihood that the user will use them, with the most likely action being added
+         * first.
          */
-        public Builder addSecondaryAction(
-                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
-            if (intent != null || label != null || icon != null) {
-                mSecondaryIntents.add(intent);
-                mSecondaryLabels.add(label);
-                mSecondaryIcons.add(icon);
-            }
+        public Builder addAction(@NonNull RemoteAction action) {
+            Preconditions.checkArgument(action != null);
+            mActions.add(action);
             return this;
         }
 
         /**
-         * Removes all the <i>secondary</i> actions.
-         */
-        public Builder clearSecondaryActions() {
-            mSecondaryIntents.clear();
-            mSecondaryLabels.clear();
-            mSecondaryIcons.clear();
-            return this;
-        }
-
-        /**
-         * Sets the <i>primary</i> action that may be performed on the classified text. This is
-         * equivalent to calling {@code setIntent(intent).setLabel(label).setIcon(icon)}.
-         *
-         * <p><strong>Note: </strong>If all input parameters are null, there will be no
-         * <i>primary</i> action but there may still be <i>secondary</i> actions.
-         *
-         * @see #addSecondaryAction(Intent, String, Drawable)
-         */
-        public Builder setPrimaryAction(
-                @Nullable Intent intent, @Nullable String label, @Nullable Drawable icon) {
-            return setIntent(intent).setLabel(label).setIcon(icon);
-        }
-
-        /**
          * Sets the icon for the <i>primary</i> action that may be rendered on a widget used to act
          * on the classified text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
         public Builder setIcon(@Nullable Drawable icon) {
-            mPrimaryIcon = icon;
+            mLegacyIcon = icon;
             return this;
         }
 
@@ -575,10 +455,11 @@
          * Sets the label for the <i>primary</i> action that may be rendered on a widget used to
          * act on the classified text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
         public Builder setLabel(@Nullable String label) {
-            mPrimaryLabel = label;
+            mLegacyLabel = label;
             return this;
         }
 
@@ -586,10 +467,11 @@
          * Sets the intent for the <i>primary</i> action that may be fired to act on the classified
          * text.
          *
-         * @see #setPrimaryAction(Intent, String, Drawable)
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
+        @Deprecated
         public Builder setIntent(@Nullable Intent intent) {
-            mPrimaryIntent = intent;
+            mLegacyIntent = intent;
             return this;
         }
 
@@ -597,9 +479,11 @@
          * Sets the OnClickListener for the <i>primary</i> action that may be triggered to act on
          * the classified text. This field is not parcelable and will always be null when the
          * object is read from a parcel.
+         *
+         * @deprecated Use {@link #addAction(RemoteAction)} instead.
          */
         public Builder setOnClickListener(@Nullable OnClickListener onClickListener) {
-            mPrimaryOnClickListener = onClickListener;
+            mLegacyOnClickListener = onClickListener;
             return this;
         }
 
@@ -617,11 +501,8 @@
          * Builds and returns a {@link TextClassification} object.
          */
         public TextClassification build() {
-            return new TextClassification(
-                    mText,
-                    mPrimaryIcon, mPrimaryLabel, mPrimaryIntent, mPrimaryOnClickListener,
-                    mSecondaryIcons, mSecondaryLabels, mSecondaryIntents,
-                    mEntityConfidence, mSignature);
+            return new TextClassification(mText, mLegacyIcon, mLegacyLabel, mLegacyIntent,
+                    mLegacyOnClickListener, mActions, mEntityConfidence, mSignature);
         }
     }
 
@@ -631,7 +512,7 @@
     public static final class Options implements Parcelable {
 
         private @Nullable LocaleList mDefaultLocales;
-        private @Nullable Calendar mReferenceTime;
+        private @Nullable ZonedDateTime mReferenceTime;
 
         public Options() {}
 
@@ -650,7 +531,7 @@
          *      be interpreted. This should usually be the time when the text was originally
          *      composed. If no reference time is set, now is used.
          */
-        public Options setReferenceTime(Calendar referenceTime) {
+        public Options setReferenceTime(ZonedDateTime referenceTime) {
             mReferenceTime = referenceTime;
             return this;
         }
@@ -669,7 +550,7 @@
          *      interpreted.
          */
         @Nullable
-        public Calendar getReferenceTime() {
+        public ZonedDateTime getReferenceTime() {
             return mReferenceTime;
         }
 
@@ -686,7 +567,7 @@
             }
             dest.writeInt(mReferenceTime != null ? 1 : 0);
             if (mReferenceTime != null) {
-                dest.writeSerializable(mReferenceTime);
+                dest.writeString(mReferenceTime.toString());
             }
         }
 
@@ -708,7 +589,7 @@
                 mDefaultLocales = LocaleList.CREATOR.createFromParcel(in);
             }
             if (in.readInt() > 0) {
-                mReferenceTime = (Calendar) in.readSerializable();
+                mReferenceTime = ZonedDateTime.parse(in.readString());
             }
         }
     }
@@ -721,20 +602,18 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mText);
-        final Bitmap primaryIconBitmap = drawableToBitmap(mPrimaryIcon, MAX_PRIMARY_ICON_SIZE);
-        dest.writeInt(primaryIconBitmap != null ? 1 : 0);
-        if (primaryIconBitmap != null) {
-            primaryIconBitmap.writeToParcel(dest, flags);
+        final Bitmap legacyIconBitmap = drawableToBitmap(mLegacyIcon, MAX_LEGACY_ICON_SIZE);
+        dest.writeInt(legacyIconBitmap != null ? 1 : 0);
+        if (legacyIconBitmap != null) {
+            legacyIconBitmap.writeToParcel(dest, flags);
         }
-        dest.writeString(mPrimaryLabel);
-        dest.writeInt(mPrimaryIntent != null ? 1 : 0);
-        if (mPrimaryIntent != null) {
-            mPrimaryIntent.writeToParcel(dest, flags);
+        dest.writeString(mLegacyLabel);
+        dest.writeInt(mLegacyIntent != null ? 1 : 0);
+        if (mLegacyIntent != null) {
+            mLegacyIntent.writeToParcel(dest, flags);
         }
-        // mPrimaryOnClickListener is not parcelable.
-        dest.writeTypedList(drawablesToBitmaps(mSecondaryIcons, MAX_SECONDARY_ICON_SIZE));
-        dest.writeStringList(mSecondaryLabels);
-        dest.writeTypedList(mSecondaryIntents);
+        // mOnClickListener is not parcelable.
+        dest.writeTypedList(mActions);
         mEntityConfidence.writeToParcel(dest, flags);
         dest.writeString(mSignature);
     }
@@ -754,15 +633,19 @@
 
     private TextClassification(Parcel in) {
         mText = in.readString();
-        mPrimaryIcon = in.readInt() == 0
+        mLegacyIcon = in.readInt() == 0
                 ? null
                 : new BitmapDrawable(Resources.getSystem(), Bitmap.CREATOR.createFromParcel(in));
-        mPrimaryLabel = in.readString();
-        mPrimaryIntent = in.readInt() == 0 ? null : Intent.CREATOR.createFromParcel(in);
-        mPrimaryOnClickListener = null;  // not parcelable
-        mSecondaryIcons = bitmapsToDrawables(in.createTypedArrayList(Bitmap.CREATOR));
-        mSecondaryLabels = in.createStringArrayList();
-        mSecondaryIntents = in.createTypedArrayList(Intent.CREATOR);
+        mLegacyLabel = in.readString();
+        if (in.readInt() == 0) {
+            mLegacyIntent = null;
+        } else {
+            mLegacyIntent = Intent.CREATOR.createFromParcel(in);
+            mLegacyIntent.removeFlags(
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        }
+        mLegacyOnClickListener = null;  // not parcelable
+        mActions = in.createTypedArrayList(RemoteAction.CREATOR);
         mEntityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
         mSignature = in.readString();
     }
diff --git a/core/java/android/view/textclassifier/TextClassificationContext.java b/core/java/android/view/textclassifier/TextClassificationContext.java
new file mode 100644
index 0000000..a88f2f6
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationContext.java
@@ -0,0 +1,123 @@
+/*
+ * 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.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.textclassifier.TextClassifier.WidgetType;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Locale;
+
+/**
+ * A representation of the context in which text classification would be performed.
+ * @see TextClassificationManager#createTextClassificationSession(TextClassificationContext)
+ */
+public final class TextClassificationContext {
+
+    private final String mPackageName;
+    private final String mWidgetType;
+    @Nullable private final String mWidgetVersion;
+
+    private TextClassificationContext(
+            String packageName,
+            String widgetType,
+            String widgetVersion) {
+        mPackageName = Preconditions.checkNotNull(packageName);
+        mWidgetType = Preconditions.checkNotNull(widgetType);
+        mWidgetVersion = widgetVersion;
+    }
+
+    /**
+     * Returns the package name for the calling package.
+     */
+    @NonNull
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Returns the widget type for this classification context.
+     */
+    @NonNull
+    @WidgetType
+    public String getWidgetType() {
+        return mWidgetType;
+    }
+
+    /**
+     * Returns a custom version string for the widget type.
+     *
+     * @see #getWidgetType()
+     */
+    @Nullable
+    public String getWidgetVersion() {
+        return mWidgetVersion;
+    }
+
+    @Override
+    public String toString() {
+        return String.format(Locale.US, "TextClassificationContext{"
+                + "packageName=%s, widgetType=%s, widgetVersion=%s}",
+                mPackageName, mWidgetType, mWidgetVersion);
+    }
+
+    /**
+     * A builder for building a TextClassification context.
+     */
+    public static final class Builder {
+
+        private final String mPackageName;
+        private final String mWidgetType;
+
+        @Nullable private String mWidgetVersion;
+
+        /**
+         * Initializes a new builder for text classification context objects.
+         *
+         * @param packageName the name of the calling package
+         * @param widgetType the type of widget e.g. {@link TextClassifier#WIDGET_TYPE_TEXTVIEW}
+         *
+         * @return this builder
+         */
+        public Builder(@NonNull String packageName, @NonNull @WidgetType String widgetType) {
+            mPackageName = Preconditions.checkNotNull(packageName);
+            mWidgetType = Preconditions.checkNotNull(widgetType);
+        }
+
+        /**
+         * Sets an optional custom version string for the widget type.
+         *
+         * @return this builder
+         */
+        public Builder setWidgetVersion(@Nullable String widgetVersion) {
+            mWidgetVersion = widgetVersion;
+            return this;
+        }
+
+        /**
+         * Builds the text classification context object.
+         *
+         * @return the built TextClassificationContext object
+         */
+        @NonNull
+        public TextClassificationContext build() {
+            return new TextClassificationContext(mPackageName, mWidgetType, mWidgetVersion);
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index a7f1ca1..262d9b8 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -16,6 +16,7 @@
 
 package android.view.textclassifier;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
@@ -36,6 +37,9 @@
     private static final String LOG_TAG = "TextClassificationManager";
 
     private final Object mLock = new Object();
+    private final TextClassificationSessionFactory mDefaultSessionFactory =
+            classificationContext -> new TextClassificationSession(
+                    classificationContext, getTextClassifier());
 
     private final Context mContext;
     private final TextClassificationConstants mSettings;
@@ -46,12 +50,15 @@
     private TextClassifier mLocalTextClassifier;
     @GuardedBy("mLock")
     private TextClassifier mSystemTextClassifier;
+    @GuardedBy("mLock")
+    private TextClassificationSessionFactory mSessionFactory;
 
     /** @hide */
     public TextClassificationManager(Context context) {
         mContext = Preconditions.checkNotNull(context);
         mSettings = TextClassificationConstants.loadFromString(Settings.Global.getString(
                 context.getContentResolver(), Settings.Global.TEXT_CLASSIFIER_CONSTANTS));
+        mSessionFactory = mDefaultSessionFactory;
     }
 
     /**
@@ -61,6 +68,7 @@
      *
      * @see #setTextClassifier(TextClassifier)
      */
+    @NonNull
     public TextClassifier getTextClassifier() {
         synchronized (mLock) {
             if (mTextClassifier == null) {
@@ -93,7 +101,6 @@
      * @see TextClassifier#SYSTEM
      * @hide
      */
-    // TODO: Expose as system API.
     public TextClassifier getTextClassifier(@TextClassifierType int type) {
         switch (type) {
             case TextClassifier.LOCAL:
@@ -108,6 +115,61 @@
         return mSettings;
     }
 
+    /**
+     * Call this method to start a text classification session with the given context.
+     * A session is created with a context helping the classifier better understand
+     * what the user needs and consists of queries and feedback events. The queries
+     * are directly related to providing useful functionality to the user and the events
+     * are a feedback loop back to the classifier helping it learn and better serve
+     * future queries.
+     *
+     * <p> All interactions with the returned classifier are considered part of a single
+     * session and are logically grouped. For example, when a text widget is focused
+     * all user interactions around text editing (selection, editing, etc) can be
+     * grouped together to allow the classifier get better.
+     *
+     * @param classificationContext The context in which classification would occur
+     *
+     * @return An instance to perform classification in the given context
+     */
+    @NonNull
+    public TextClassifier createTextClassificationSession(
+            @NonNull TextClassificationContext classificationContext) {
+        Preconditions.checkNotNull(classificationContext);
+        final TextClassifier textClassifier =
+                mSessionFactory.createTextClassificationSession(classificationContext);
+        Preconditions.checkNotNull(textClassifier, "Session Factory should never return null");
+        return textClassifier;
+    }
+
+    /**
+     * @see #createTextClassificationSession(TextClassificationContext, TextClassifier)
+     * @hide
+     */
+    public TextClassifier createTextClassificationSession(
+            TextClassificationContext classificationContext, TextClassifier textClassifier) {
+        Preconditions.checkNotNull(classificationContext);
+        Preconditions.checkNotNull(textClassifier);
+        return new TextClassificationSession(classificationContext, textClassifier);
+    }
+
+    /**
+     * Sets a TextClassificationSessionFactory to be used to create session-aware TextClassifiers.
+     *
+     * @param factory the textClassification session factory. If this is null, the default factory
+     *      will be used.
+     */
+    public void setTextClassificationSessionFactory(
+            @Nullable TextClassificationSessionFactory factory) {
+        synchronized (mLock) {
+            if (factory != null) {
+                mSessionFactory = factory;
+            } else {
+                mSessionFactory = mDefaultSessionFactory;
+            }
+        }
+    }
+
     private TextClassifier getSystemTextClassifier() {
         synchronized (mLock) {
             if (mSystemTextClassifier == null && isSystemTextClassifierEnabled()) {
diff --git a/core/java/android/view/textclassifier/TextClassificationSession.java b/core/java/android/view/textclassifier/TextClassificationSession.java
new file mode 100644
index 0000000..6938e1a
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationSession.java
@@ -0,0 +1,217 @@
+/*
+ * 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.view.textclassifier;
+
+import android.annotation.WorkerThread;
+import android.view.textclassifier.DefaultLogger.SignatureParser;
+import android.view.textclassifier.SelectionEvent.InvocationMethod;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Session-aware TextClassifier.
+ */
+@WorkerThread
+final class TextClassificationSession implements TextClassifier {
+
+    /* package */ static final boolean DEBUG_LOG_ENABLED = true;
+    private static final String LOG_TAG = "TextClassificationSession";
+
+    private final TextClassifier mDelegate;
+    private final SelectionEventHelper mEventHelper;
+
+    private boolean mDestroyed;
+
+    TextClassificationSession(TextClassificationContext context, TextClassifier delegate) {
+        mDelegate = Preconditions.checkNotNull(delegate);
+        mEventHelper = new SelectionEventHelper(new TextClassificationSessionId(), context);
+    }
+
+    @Override
+    public TextSelection suggestSelection(CharSequence text, int selectionStartIndex,
+            int selectionEndIndex, TextSelection.Options options) {
+        checkDestroyed();
+        return mDelegate.suggestSelection(text, selectionStartIndex, selectionEndIndex, options);
+    }
+
+    @Override
+    public TextClassification classifyText(CharSequence text, int startIndex, int endIndex,
+            TextClassification.Options options) {
+        checkDestroyed();
+        return mDelegate.classifyText(text, startIndex, endIndex, options);
+    }
+
+    @Override
+    public TextLinks generateLinks(CharSequence text, TextLinks.Options options) {
+        checkDestroyed();
+        return mDelegate.generateLinks(text, options);
+    }
+
+    @Override
+    public void onSelectionEvent(SelectionEvent event) {
+        checkDestroyed();
+        Preconditions.checkNotNull(event);
+        if (mEventHelper.sanitizeEvent(event)) {
+            mDelegate.onSelectionEvent(event);
+        }
+    }
+
+    @Override
+    public void destroy() {
+        mEventHelper.endSession();
+        mDestroyed = true;
+    }
+
+    @Override
+    public boolean isDestroyed() {
+        return mDestroyed;
+    }
+
+    /**
+     * @throws IllegalStateException if this TextClassification session has been destroyed.
+     * @see #isDestroyed()
+     * @see #destroy()
+     */
+    private void checkDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("This TextClassification session has been destroyed");
+        }
+    }
+
+    /**
+     * Helper class for updating SelectionEvent fields.
+     */
+    private static final class SelectionEventHelper {
+
+        private final TextClassificationSessionId mSessionId;
+        private final TextClassificationContext mContext;
+
+        @InvocationMethod
+        private int mInvocationMethod = SelectionEvent.INVOCATION_UNKNOWN;
+        private SelectionEvent mPrevEvent;
+        private SelectionEvent mSmartEvent;
+        private SelectionEvent mStartEvent;
+
+        SelectionEventHelper(
+                TextClassificationSessionId sessionId, TextClassificationContext context) {
+            mSessionId = Preconditions.checkNotNull(sessionId);
+            mContext = Preconditions.checkNotNull(context);
+        }
+
+        /**
+         * Updates the necessary fields in the event for the current session.
+         *
+         * @return true if the event should be reported. false if the event should be ignored
+         */
+        boolean sanitizeEvent(SelectionEvent event) {
+            updateInvocationMethod(event);
+            modifyAutoSelectionEventType(event);
+
+            if (event.getEventType() != SelectionEvent.EVENT_SELECTION_STARTED
+                    && mStartEvent == null) {
+                if (DEBUG_LOG_ENABLED) {
+                    Log.d(LOG_TAG, "Selection session not yet started. Ignoring event");
+                }
+                return false;
+            }
+
+            final long now = System.currentTimeMillis();
+            switch (event.getEventType()) {
+                case SelectionEvent.EVENT_SELECTION_STARTED:
+                    Preconditions.checkArgument(
+                            event.getAbsoluteEnd() == event.getAbsoluteStart() + 1);
+                    event.setSessionId(mSessionId);
+                    mStartEvent = event;
+                    break;
+                case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:
+                    mSmartEvent = event;
+                    break;
+                case SelectionEvent.EVENT_SELECTION_MODIFIED:  // fall through
+                case SelectionEvent.EVENT_AUTO_SELECTION:
+                    if (mPrevEvent != null
+                            && mPrevEvent.getAbsoluteStart() == event.getAbsoluteStart()
+                            && mPrevEvent.getAbsoluteEnd() == event.getAbsoluteEnd()) {
+                        // Selection did not change. Ignore event.
+                        return false;
+                    }
+                    break;
+                default:
+                    // do nothing.
+            }
+
+            event.setEventTime(now);
+            if (mStartEvent != null) {
+                event.setSessionId(mStartEvent.getSessionId())
+                        .setDurationSinceSessionStart(now - mStartEvent.getEventTime())
+                        .setStart(event.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                        .setEnd(event.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+            }
+            if (mSmartEvent != null) {
+                event.setSignature(mSmartEvent.getSignature())
+                        .setSmartStart(
+                                mSmartEvent.getAbsoluteStart() - mStartEvent.getAbsoluteStart())
+                        .setSmartEnd(mSmartEvent.getAbsoluteEnd() - mStartEvent.getAbsoluteStart());
+            }
+            if (mPrevEvent != null) {
+                event.setDurationSincePreviousEvent(now - mPrevEvent.getEventTime())
+                        .setEventIndex(mPrevEvent.getEventIndex() + 1);
+            }
+            mPrevEvent = event;
+            return true;
+        }
+
+        void endSession() {
+            mPrevEvent = null;
+            mSmartEvent = null;
+            mStartEvent = null;
+        }
+
+        private void updateInvocationMethod(SelectionEvent event) {
+            event.setTextClassificationSessionContext(mContext);
+            if (event.getInvocationMethod() == SelectionEvent.INVOCATION_UNKNOWN) {
+                event.setInvocationMethod(mInvocationMethod);
+            } else {
+                mInvocationMethod = event.getInvocationMethod();
+            }
+        }
+
+        private void modifyAutoSelectionEventType(SelectionEvent event) {
+            switch (event.getEventType()) {
+                case SelectionEvent.EVENT_SMART_SELECTION_SINGLE:  // fall through
+                case SelectionEvent.EVENT_SMART_SELECTION_MULTI:  // fall through
+                case SelectionEvent.EVENT_AUTO_SELECTION:
+                    if (isPlatformLocalTextClassifierSmartSelection(event.getSignature())) {
+                        if (event.getAbsoluteEnd() - event.getAbsoluteStart() > 1) {
+                            event.setEventType(SelectionEvent.EVENT_SMART_SELECTION_MULTI);
+                        } else {
+                            event.setEventType(SelectionEvent.EVENT_SMART_SELECTION_SINGLE);
+                        }
+                    } else {
+                        event.setEventType(SelectionEvent.EVENT_AUTO_SELECTION);
+                    }
+                    return;
+                default:
+                    return;
+            }
+        }
+
+        private static boolean isPlatformLocalTextClassifierSmartSelection(String signature) {
+            return DefaultLogger.CLASSIFIER_ID.equals(SignatureParser.getClassifierId(signature));
+        }
+    }
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionFactory.java b/core/java/android/view/textclassifier/TextClassificationSessionFactory.java
new file mode 100644
index 0000000..c0914b6
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationSessionFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.textclassifier;
+
+import android.annotation.NonNull;
+
+/**
+ * An interface for creating a session-aware TextClassifier.
+ *
+ * @see TextClassificationManager#createTextClassificationSession(TextClassificationContext)
+ */
+public interface TextClassificationSessionFactory {
+
+    /**
+     * Creates and returns a session-aware TextClassifier.
+     *
+     * @param classificationContext the classification context
+     */
+    @NonNull
+    TextClassifier createTextClassificationSession(
+            @NonNull TextClassificationContext classificationContext);
+}
diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java
new file mode 100644
index 0000000..3e4dc1c
--- /dev/null
+++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 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.view.textclassifier;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.UUID;
+
+/**
+ * This class represents the id of a text classification session.
+ */
+public final class TextClassificationSessionId implements Parcelable {
+    private final @NonNull String mValue;
+
+    /**
+     * Creates a new instance.
+     *
+     * @hide
+     */
+    public TextClassificationSessionId() {
+        this(UUID.randomUUID().toString());
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param value The internal value.
+     *
+     * @hide
+     */
+    public TextClassificationSessionId(@NonNull String value) {
+        mValue = value;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + mValue.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        TextClassificationSessionId other = (TextClassificationSessionId) obj;
+        if (!mValue.equals(other.mValue)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeString(mValue);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flattens this id to a string.
+     *
+     * @return The flattened id.
+     *
+     * @hide
+     */
+    public @NonNull String flattenToString() {
+        return mValue;
+    }
+
+    /**
+     * Unflattens a print job id from a string.
+     *
+     * @param string The string.
+     * @return The unflattened id, or null if the string is malformed.
+     *
+     * @hide
+     */
+    public static @NonNull TextClassificationSessionId unflattenFromString(@NonNull String string) {
+        return new TextClassificationSessionId(string);
+    }
+
+    public static final Parcelable.Creator<TextClassificationSessionId> CREATOR =
+            new Parcelable.Creator<TextClassificationSessionId>() {
+                @Override
+                public TextClassificationSessionId createFromParcel(Parcel parcel) {
+                    return new TextClassificationSessionId(
+                            Preconditions.checkNotNull(parcel.readString()));
+                }
+
+                @Override
+                public TextClassificationSessionId[] newArray(int size) {
+                    return new TextClassificationSessionId[size];
+                }
+            };
+}
diff --git a/core/java/android/view/textclassifier/TextClassifier.java b/core/java/android/view/textclassifier/TextClassifier.java
index 98fa574..2048f2b 100644
--- a/core/java/android/view/textclassifier/TextClassifier.java
+++ b/core/java/android/view/textclassifier/TextClassifier.java
@@ -112,6 +112,38 @@
     @StringDef(prefix = { "HINT_" }, value = {HINT_TEXT_IS_EDITABLE, HINT_TEXT_IS_NOT_EDITABLE})
     @interface Hints {}
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef({WIDGET_TYPE_TEXTVIEW, WIDGET_TYPE_WEBVIEW, WIDGET_TYPE_EDITTEXT,
+            WIDGET_TYPE_EDIT_WEBVIEW, WIDGET_TYPE_CUSTOM_TEXTVIEW, WIDGET_TYPE_CUSTOM_EDITTEXT,
+            WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW, WIDGET_TYPE_UNKNOWN})
+    @interface WidgetType {}
+
+    /** The widget involved in the text classification session is a standard
+     * {@link android.widget.TextView}. */
+    String WIDGET_TYPE_TEXTVIEW = "textview";
+    /** The widget involved in the text classification session is a standard
+     * {@link android.widget.EditText}. */
+    String WIDGET_TYPE_EDITTEXT = "edittext";
+    /** The widget involved in the text classification session is a standard non-selectable
+     * {@link android.widget.TextView}. */
+    String WIDGET_TYPE_UNSELECTABLE_TEXTVIEW = "nosel-textview";
+    /** The widget involved in the text classification session is a standard
+     * {@link android.webkit.WebView}. */
+    String WIDGET_TYPE_WEBVIEW = "webview";
+    /** The widget involved in the text classification session is a standard editable
+     * {@link android.webkit.WebView}. */
+    String WIDGET_TYPE_EDIT_WEBVIEW = "edit-webview";
+    /** The widget involved in the text classification session is a custom text widget. */
+    String WIDGET_TYPE_CUSTOM_TEXTVIEW = "customview";
+    /** The widget involved in the text classification session is a custom editable text widget. */
+    String WIDGET_TYPE_CUSTOM_EDITTEXT = "customedit";
+    /** The widget involved in the text classification session is a custom non-selectable text
+     * widget. */
+    String WIDGET_TYPE_CUSTOM_UNSELECTABLE_TEXTVIEW = "nosel-customview";
+    /** The widget involved in the text classification session is of an unknown/unspecified type. */
+    String WIDGET_TYPE_UNKNOWN = "unknown";
+
     /**
      * No-op TextClassifier.
      * This may be used to turn off TextClassifier features.
@@ -124,6 +156,9 @@
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text text providing context for the selected text (which is specified
      *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
      * @param selectionStartIndex start index of the selected part of text
@@ -156,6 +191,9 @@
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text text providing context for the selected text (which is specified
      *      by the sub sequence starting at selectionStartIndex and ending at selectionEndIndex)
      * @param selectionStartIndex start index of the selected part of text
@@ -185,6 +223,9 @@
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #suggestSelection(CharSequence, int, int, TextSelection.Options)}. If that method
      * calls this method, a stack overflow error will happen.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      */
     @WorkerThread
     @NonNull
@@ -205,6 +246,9 @@
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text text providing context for the text to classify (which is specified
      *      by the sub sequence starting at startIndex and ending at endIndex)
      * @param startIndex start index of the text to classify
@@ -237,6 +281,9 @@
      * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
      * calls this method, a stack overflow error will happen.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text text providing context for the text to classify (which is specified
      *      by the sub sequence starting at startIndex and ending at endIndex)
      * @param startIndex start index of the text to classify
@@ -265,6 +312,9 @@
      * <p><b>NOTE:</b> Do not implement. The default implementation of this method calls
      * {@link #classifyText(CharSequence, int, int, TextClassification.Options)}. If that method
      * calls this method, a stack overflow error will happen.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
      */
     @WorkerThread
     @NonNull
@@ -285,6 +335,9 @@
      *
      * <p><strong>NOTE: </strong>Call on a worker thread.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text the text to generate annotations for
      * @param options configuration for link generation
      *
@@ -295,6 +348,7 @@
      * @see #getMaxGenerateLinksTextLength()
      */
     @WorkerThread
+    @NonNull
     default TextLinks generateLinks(
             @NonNull CharSequence text, @Nullable TextLinks.Options options) {
         Utils.validate(text, false);
@@ -311,6 +365,9 @@
      * {@link #generateLinks(CharSequence, TextLinks.Options)}. If that method calls this method,
      * a stack overflow error will happen.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @param text the text to generate annotations for
      *
      * @throws IllegalArgumentException if text is null or the text is too long for the
@@ -320,6 +377,7 @@
      * @see #getMaxGenerateLinksTextLength()
      */
     @WorkerThread
+    @NonNull
     default TextLinks generateLinks(@NonNull CharSequence text) {
         return generateLinks(text, null);
     }
@@ -327,6 +385,9 @@
     /**
      * Returns the maximal length of text that can be processed by generateLinks.
      *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
      * @see #generateLinks(CharSequence)
      * @see #generateLinks(CharSequence, TextLinks.Options)
      */
@@ -339,6 +400,7 @@
      * Returns a helper for logging TextClassifier related events.
      *
      * @param config logger configuration
+     * @hide
      */
     @WorkerThread
     default Logger getLogger(@NonNull Logger.Config config) {
@@ -347,6 +409,37 @@
     }
 
     /**
+     * Reports a selection event.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to this method should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     */
+    default void onSelectionEvent(@NonNull SelectionEvent event) {}
+
+    /**
+     * Destroys this TextClassifier.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, calls to its methods should
+     * throw an {@link IllegalStateException}. See {@link #isDestroyed()}.
+     *
+     * <p>Subsequent calls to this method are no-ops.
+     */
+    default void destroy() {}
+
+    /**
+     * Returns whether or not this TextClassifier has been destroyed.
+     *
+     * <strong>NOTE: </strong>If a TextClassifier has been destroyed, caller should not interact
+     * with the classifier and an attempt to do so would throw an {@link IllegalStateException}.
+     * However, this method should never throw an {@link IllegalStateException}.
+     *
+     * @see #destroy()
+     */
+    default boolean isDestroyed() {
+        return false;
+    }
+
+    /**
      * Configuration object for specifying what entities to identify.
      *
      * Configs are initially based on a predefined preset, and can be modified from there.
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index c2fb032..8d1ed0e 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -16,9 +16,12 @@
 
 package android.view.textclassifier;
 
+import static java.time.temporal.ChronoUnit.MILLIS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.WorkerThread;
+import android.app.RemoteAction;
 import android.app.SearchManager;
 import android.content.ComponentName;
 import android.content.ContentUris;
@@ -26,7 +29,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.LocaleList;
@@ -44,9 +47,10 @@
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
+import java.time.Instant;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Calendar;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -93,6 +97,8 @@
     private Logger.Config mLoggerConfig;
     @GuardedBy("mLoggerLock") // Do not access outside this lock.
     private Logger mLogger;
+    @GuardedBy("mLoggerLock") // Do not access outside this lock.
+    private Logger mLogger2;  // This is the new logger. Will replace mLogger.
 
     private final TextClassificationConstants mSettings;
 
@@ -116,7 +122,7 @@
                     && rangeLength <= mSettings.getSuggestSelectionMaxRangeLength()) {
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                 final String localesString = concatenateLocales(locales);
-                final Calendar refTime = Calendar.getInstance();
+                final ZonedDateTime refTime = ZonedDateTime.now();
                 final boolean darkLaunchAllowed = options != null && options.isDarkLaunchAllowed();
                 final TextClassifierImplNative nativeImpl = getNative(locales);
                 final String string = text.toString();
@@ -140,8 +146,8 @@
                             nativeImpl.classifyText(
                                     string, start, end,
                                     new TextClassifierImplNative.ClassificationOptions(
-                                            refTime.getTimeInMillis(),
-                                            refTime.getTimeZone().getID(),
+                                            refTime.toInstant().toEpochMilli(),
+                                            refTime.getZone().getId(),
                                             localesString));
                     final int size = results.length;
                     for (int i = 0; i < size; i++) {
@@ -180,19 +186,20 @@
                 final String string = text.toString();
                 final LocaleList locales = (options == null) ? null : options.getDefaultLocales();
                 final String localesString = concatenateLocales(locales);
-                final Calendar refTime = (options != null && options.getReferenceTime() != null)
-                        ? options.getReferenceTime() : Calendar.getInstance();
+                final ZonedDateTime refTime =
+                        (options != null && options.getReferenceTime() != null)
+                                ? options.getReferenceTime() : ZonedDateTime.now();
 
                 final TextClassifierImplNative.ClassificationResult[] results =
                         getNative(locales)
                                 .classifyText(string, startIndex, endIndex,
                                         new TextClassifierImplNative.ClassificationOptions(
-                                                refTime.getTimeInMillis(),
-                                                refTime.getTimeZone().getID(),
+                                                refTime.toInstant().toEpochMilli(),
+                                                refTime.getZone().getId(),
                                                 localesString));
                 if (results.length > 0) {
                     return createClassificationResult(
-                            results, string, startIndex, endIndex, refTime);
+                            results, string, startIndex, endIndex, refTime.toInstant());
                 }
             }
         } catch (Throwable t) {
@@ -221,7 +228,7 @@
         try {
             final long startTimeMs = System.currentTimeMillis();
             final LocaleList defaultLocales = options != null ? options.getDefaultLocales() : null;
-            final Calendar refTime = Calendar.getInstance();
+            final ZonedDateTime refTime = ZonedDateTime.now();
             final Collection<String> entitiesToIdentify =
                     options != null && options.getEntityConfig() != null
                             ? options.getEntityConfig().resolveEntityListModifications(
@@ -233,8 +240,8 @@
                     nativeImpl.annotate(
                         textString,
                         new TextClassifierImplNative.AnnotationOptions(
-                                refTime.getTimeInMillis(),
-                                refTime.getTimeZone().getID(),
+                                refTime.toInstant().toEpochMilli(),
+                                refTime.getZone().getId(),
                                 concatenateLocales(defaultLocales)));
             for (TextClassifierImplNative.AnnotatedSpan span : annotations) {
                 final TextClassifierImplNative.ClassificationResult[] results =
@@ -299,6 +306,18 @@
         return mLogger;
     }
 
+    @Override
+    public void onSelectionEvent(SelectionEvent event) {
+        Preconditions.checkNotNull(event);
+        synchronized (mLoggerLock) {
+            if (mLogger2 == null) {
+                mLogger2 = new DefaultLogger(
+                        new Logger.Config(mContext, WIDGET_TYPE_UNKNOWN, null));
+            }
+            mLogger2.writeEvent(event);
+        }
+    }
+
     private TextClassifierImplNative getNative(LocaleList localeList)
             throws FileNotFoundException {
         synchronized (mLock) {
@@ -401,7 +420,7 @@
 
     private TextClassification createClassificationResult(
             TextClassifierImplNative.ClassificationResult[] classifications,
-            String text, int start, int end, @Nullable Calendar referenceTime) {
+            String text, int start, int end, @Nullable Instant referenceTime) {
         final String classifiedText = text.substring(start, end);
         final TextClassification.Builder builder = new TextClassification.Builder()
                 .setText(classifiedText);
@@ -418,50 +437,27 @@
             }
         }
 
-        addActions(builder, IntentFactory.create(
-                mContext, referenceTime, highestScoringResult, classifiedText));
+        boolean isPrimaryAction = true;
+        for (LabeledIntent labeledIntent : IntentFactory.create(
+                mContext, referenceTime, highestScoringResult, classifiedText)) {
+            RemoteAction action = labeledIntent.asRemoteAction(mContext);
+            if (isPrimaryAction) {
+                // For O backwards compatibility, the first RemoteAction is also written to the
+                // legacy API fields.
+                builder.setIcon(action.getIcon().loadDrawable(mContext));
+                builder.setLabel(action.getTitle().toString());
+                builder.setIntent(labeledIntent.getIntent());
+                builder.setOnClickListener(TextClassification.createIntentOnClickListener(
+                        TextClassification.createPendingIntent(mContext,
+                                labeledIntent.getIntent())));
+                isPrimaryAction = false;
+            }
+            builder.addAction(action);
+        }
 
         return builder.setSignature(getSignature(text, start, end)).build();
     }
 
-    /** Extends the classification with the intents that can be resolved. */
-    private void addActions(
-            TextClassification.Builder builder, List<Intent> intents) {
-        final PackageManager pm = mContext.getPackageManager();
-        final int size = intents.size();
-        for (int i = 0; i < size; i++) {
-            final Intent intent = intents.get(i);
-            final ResolveInfo resolveInfo;
-            if (intent != null) {
-                resolveInfo = pm.resolveActivity(intent, 0);
-            } else {
-                resolveInfo = null;
-            }
-            if (resolveInfo != null && resolveInfo.activityInfo != null) {
-                final String packageName = resolveInfo.activityInfo.packageName;
-                final String label = IntentFactory.getLabel(mContext, intent);
-                Drawable icon;
-                if ("android".equals(packageName)) {
-                    // Requires the chooser to find an activity to handle the intent.
-                    icon = null;
-                } else {
-                    // A default activity will handle the intent.
-                    intent.setComponent(
-                            new ComponentName(packageName, resolveInfo.activityInfo.name));
-                    icon = resolveInfo.activityInfo.loadIcon(pm);
-                    if (icon == null) {
-                        icon = resolveInfo.loadIcon(pm);
-                    }
-                }
-                if (i == 0) {
-                    builder.setPrimaryAction(intent, label, icon);
-                } else {
-                    builder.addSecondaryAction(intent, label, icon);
-                }
-            }
-        }
-    }
-
     /**
      * Closes the ParcelFileDescriptor and logs any errors that occur.
      */
@@ -588,6 +584,60 @@
     }
 
     /**
+     * Helper class to store the information from which RemoteActions are built.
+     */
+    private static final class LabeledIntent {
+        private String mTitle;
+        private String mDescription;
+        private Intent mIntent;
+
+        LabeledIntent(String title, String description, Intent intent) {
+            mTitle = title;
+            mDescription = description;
+            mIntent = intent;
+        }
+
+        String getTitle() {
+            return mTitle;
+        }
+
+        String getDescription() {
+            return mDescription;
+        }
+
+        Intent getIntent() {
+            return mIntent;
+        }
+
+        RemoteAction asRemoteAction(Context context) {
+            final PackageManager pm = context.getPackageManager();
+            final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0);
+            final String packageName = resolveInfo != null && resolveInfo.activityInfo != null
+                    ? resolveInfo.activityInfo.packageName : null;
+            Icon icon = null;
+            boolean shouldShowIcon = false;
+            if (packageName != null && !"android".equals(packageName)) {
+                // There is a default activity handling the intent.
+                mIntent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name));
+                if (resolveInfo.activityInfo.getIconResource() != 0) {
+                    icon = Icon.createWithResource(
+                            packageName, resolveInfo.activityInfo.getIconResource());
+                    shouldShowIcon = true;
+                }
+            }
+            if (icon == null) {
+                // RemoteAction requires that there be an icon.
+                icon = Icon.createWithResource("android",
+                        com.android.internal.R.drawable.ic_more_items);
+            }
+            RemoteAction action = new RemoteAction(icon, mTitle, mDescription,
+                    TextClassification.createPendingIntent(context, mIntent));
+            action.setShouldShowIcon(shouldShowIcon);
+            return action;
+        }
+    }
+
+    /**
      * Creates intents based on the classification type.
      */
     static final class IntentFactory {
@@ -598,84 +648,101 @@
         private IntentFactory() {}
 
         @NonNull
-        public static List<Intent> create(
+        public static List<LabeledIntent> create(
                 Context context,
-                @Nullable Calendar referenceTime,
+                @Nullable Instant referenceTime,
                 TextClassifierImplNative.ClassificationResult classification,
                 String text) {
             final String type = classification.getCollection().trim().toLowerCase(Locale.ENGLISH);
             text = text.trim();
             switch (type) {
                 case TextClassifier.TYPE_EMAIL:
-                    return createForEmail(text);
+                    return createForEmail(context, text);
                 case TextClassifier.TYPE_PHONE:
                     return createForPhone(context, text);
                 case TextClassifier.TYPE_ADDRESS:
-                    return createForAddress(text);
+                    return createForAddress(context, text);
                 case TextClassifier.TYPE_URL:
                     return createForUrl(context, text);
                 case TextClassifier.TYPE_DATE:
                 case TextClassifier.TYPE_DATE_TIME:
                     if (classification.getDatetimeResult() != null) {
-                        Calendar eventTime = Calendar.getInstance();
-                        eventTime.setTimeInMillis(
+                        final Instant parsedTime = Instant.ofEpochMilli(
                                 classification.getDatetimeResult().getTimeMsUtc());
-                        return createForDatetime(type, referenceTime, eventTime);
+                        return createForDatetime(context, type, referenceTime, parsedTime);
                     } else {
                         return new ArrayList<>();
                     }
                 case TextClassifier.TYPE_FLIGHT_NUMBER:
-                    return createForFlight(text);
+                    return createForFlight(context, text);
                 default:
                     return new ArrayList<>();
             }
         }
 
         @NonNull
-        private static List<Intent> createForEmail(String text) {
+        private static List<LabeledIntent> createForEmail(Context context, String text) {
             return Arrays.asList(
-                    new Intent(Intent.ACTION_SENDTO)
-                            .setData(Uri.parse(String.format("mailto:%s", text))),
-                    new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                            .putExtra(ContactsContract.Intents.Insert.EMAIL, text));
+                    new LabeledIntent(
+                            context.getString(com.android.internal.R.string.email),
+                            context.getString(com.android.internal.R.string.email_desc),
+                            new Intent(Intent.ACTION_SENDTO)
+                                    .setData(Uri.parse(String.format("mailto:%s", text)))),
+                    new LabeledIntent(
+                            context.getString(com.android.internal.R.string.add_contact),
+                            context.getString(com.android.internal.R.string.add_contact_desc),
+                            new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                                    .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                                    .putExtra(ContactsContract.Intents.Insert.EMAIL, text)));
         }
 
         @NonNull
-        private static List<Intent> createForPhone(Context context, String text) {
-            final List<Intent> intents = new ArrayList<>();
+        private static List<LabeledIntent> createForPhone(Context context, String text) {
+            final List<LabeledIntent> actions = new ArrayList<>();
             final UserManager userManager = context.getSystemService(UserManager.class);
             final Bundle userRestrictions = userManager != null
                     ? userManager.getUserRestrictions() : new Bundle();
             if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) {
-                intents.add(new Intent(Intent.ACTION_DIAL)
-                        .setData(Uri.parse(String.format("tel:%s", text))));
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.dial),
+                        context.getString(com.android.internal.R.string.dial_desc),
+                        new Intent(Intent.ACTION_DIAL).setData(
+                                Uri.parse(String.format("tel:%s", text)))));
             }
-            intents.add(new Intent(Intent.ACTION_INSERT_OR_EDIT)
-                    .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
-                    .putExtra(ContactsContract.Intents.Insert.PHONE, text));
+            actions.add(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_contact),
+                    context.getString(com.android.internal.R.string.add_contact_desc),
+                    new Intent(Intent.ACTION_INSERT_OR_EDIT)
+                            .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
+                            .putExtra(ContactsContract.Intents.Insert.PHONE, text)));
             if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) {
-                intents.add(new Intent(Intent.ACTION_SENDTO)
-                        .setData(Uri.parse(String.format("smsto:%s", text))));
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.sms),
+                        context.getString(com.android.internal.R.string.sms_desc),
+                        new Intent(Intent.ACTION_SENDTO)
+                                .setData(Uri.parse(String.format("smsto:%s", text)))));
             }
-            return intents;
+            return actions;
         }
 
         @NonNull
-        private static List<Intent> createForAddress(String text) {
-            final List<Intent> intents = new ArrayList<>();
+        private static List<LabeledIntent> createForAddress(Context context, String text) {
+            final List<LabeledIntent> actions = new ArrayList<>();
             try {
                 final String encText = URLEncoder.encode(text, "UTF-8");
-                intents.add(new Intent(Intent.ACTION_VIEW)
-                        .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))));
+                actions.add(new LabeledIntent(
+                        context.getString(com.android.internal.R.string.map),
+                        context.getString(com.android.internal.R.string.map_desc),
+                        new Intent(Intent.ACTION_VIEW)
+                                .setData(Uri.parse(String.format("geo:0,0?q=%s", encText)))));
             } catch (UnsupportedEncodingException e) {
                 Log.e(LOG_TAG, "Could not encode address", e);
             }
-            return intents;
+            return actions;
         }
 
         @NonNull
-        private static List<Intent> createForUrl(Context context, String text) {
+        private static List<LabeledIntent> createForUrl(Context context, String text) {
             final String httpPrefix = "http://";
             final String httpsPrefix = "https://";
             if (text.toLowerCase().startsWith(httpPrefix)) {
@@ -685,99 +752,64 @@
             } else {
                 text = httpPrefix + text;
             }
-            return Arrays.asList(new Intent(Intent.ACTION_VIEW, Uri.parse(text))
-                    .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()));
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.browse),
+                    context.getString(com.android.internal.R.string.browse_desc),
+                    new Intent(Intent.ACTION_VIEW, Uri.parse(text))
+                            .putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName())));
         }
 
         @NonNull
-        private static List<Intent> createForDatetime(
-                String type, @Nullable Calendar referenceTime, Calendar eventTime) {
+        private static List<LabeledIntent> createForDatetime(
+                Context context, String type, @Nullable Instant referenceTime,
+                Instant parsedTime) {
             if (referenceTime == null) {
                 // If no reference time was given, use now.
-                referenceTime = Calendar.getInstance();
+                referenceTime = Instant.now();
             }
-            List<Intent> intents = new ArrayList<>();
-            intents.add(createCalendarViewIntent(eventTime));
-            final long millisSinceReference =
-                    eventTime.getTimeInMillis() - referenceTime.getTimeInMillis();
-            if (millisSinceReference > MIN_EVENT_FUTURE_MILLIS) {
-                intents.add(createCalendarCreateEventIntent(eventTime, type));
+            List<LabeledIntent> actions = new ArrayList<>();
+            actions.add(createCalendarViewIntent(context, parsedTime));
+            final long millisUntilEvent = referenceTime.until(parsedTime, MILLIS);
+            if (millisUntilEvent > MIN_EVENT_FUTURE_MILLIS) {
+                actions.add(createCalendarCreateEventIntent(context, parsedTime, type));
             }
-            return intents;
+            return actions;
         }
 
         @NonNull
-        private static List<Intent> createForFlight(String text) {
-            return Arrays.asList(new Intent(Intent.ACTION_WEB_SEARCH)
-                    .putExtra(SearchManager.QUERY, text));
+        private static List<LabeledIntent> createForFlight(Context context, String text) {
+            return Arrays.asList(new LabeledIntent(
+                    context.getString(com.android.internal.R.string.view_flight),
+                    context.getString(com.android.internal.R.string.view_flight_desc),
+                    new Intent(Intent.ACTION_WEB_SEARCH)
+                            .putExtra(SearchManager.QUERY, text)));
         }
 
         @NonNull
-        private static Intent createCalendarViewIntent(Calendar eventTime) {
+        private static LabeledIntent createCalendarViewIntent(Context context, Instant parsedTime) {
             Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
             builder.appendPath("time");
-            ContentUris.appendId(builder, eventTime.getTimeInMillis());
-            return new Intent(Intent.ACTION_VIEW).setData(builder.build());
+            ContentUris.appendId(builder, parsedTime.toEpochMilli());
+            return new LabeledIntent(
+                    context.getString(com.android.internal.R.string.view_calendar),
+                    context.getString(com.android.internal.R.string.view_calendar_desc),
+                    new Intent(Intent.ACTION_VIEW).setData(builder.build()));
         }
 
         @NonNull
-        private static Intent createCalendarCreateEventIntent(
-                Calendar eventTime, @EntityType String type) {
+        private static LabeledIntent createCalendarCreateEventIntent(
+                Context context, Instant parsedTime, @EntityType String type) {
             final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type);
-            return new Intent(Intent.ACTION_INSERT)
-                    .setData(CalendarContract.Events.CONTENT_URI)
-                    .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
-                    .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, eventTime.getTimeInMillis())
-                    .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
-                            eventTime.getTimeInMillis() + DEFAULT_EVENT_DURATION);
-        }
-
-        @Nullable
-        public static String getLabel(Context context, @Nullable Intent intent) {
-            if (intent == null || intent.getAction() == null) {
-                return null;
-            }
-            final String authority =
-                    intent.getData() == null ? null : intent.getData().getAuthority();
-            switch (intent.getAction()) {
-                case Intent.ACTION_DIAL:
-                    return context.getString(com.android.internal.R.string.dial);
-                case Intent.ACTION_SENDTO:
-                    if ("mailto".equals(intent.getScheme())) {
-                        return context.getString(com.android.internal.R.string.email);
-                    } else if ("smsto".equals(intent.getScheme())) {
-                        return context.getString(com.android.internal.R.string.sms);
-                    } else {
-                        return null;
-                    }
-                case Intent.ACTION_INSERT:
-                    if (CalendarContract.AUTHORITY.equals(authority)) {
-                        return context.getString(com.android.internal.R.string.add_calendar_event);
-                    }
-                    return null;
-                case Intent.ACTION_INSERT_OR_EDIT:
-                    if (ContactsContract.Contacts.CONTENT_ITEM_TYPE.equals(
-                            intent.getType())) {
-                        return context.getString(com.android.internal.R.string.add_contact);
-                    } else {
-                        return null;
-                    }
-                case Intent.ACTION_VIEW:
-                    if (CalendarContract.AUTHORITY.equals(authority)) {
-                        return context.getString(com.android.internal.R.string.view_calendar);
-                    } else if ("geo".equals(intent.getScheme())) {
-                        return context.getString(com.android.internal.R.string.map);
-                    } else if ("http".equals(intent.getScheme())
-                            || "https".equals(intent.getScheme())) {
-                        return context.getString(com.android.internal.R.string.browse);
-                    } else {
-                        return null;
-                    }
-                case Intent.ACTION_WEB_SEARCH:
-                    return context.getString(com.android.internal.R.string.view_flight);
-                default:
-                    return null;
-            }
+            return new LabeledIntent(
+                    context.getString(com.android.internal.R.string.add_calendar_event),
+                    context.getString(com.android.internal.R.string.add_calendar_event_desc),
+                    new Intent(Intent.ACTION_INSERT)
+                            .setData(CalendarContract.Events.CONTENT_URI)
+                            .putExtra(CalendarContract.EXTRA_EVENT_ALL_DAY, isAllDay)
+                            .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
+                                    parsedTime.toEpochMilli())
+                            .putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
+                                    parsedTime.toEpochMilli() + DEFAULT_EVENT_DURATION));
         }
     }
 }
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index d0f9eee..6df1655 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -152,6 +152,14 @@
      * will continue to load the resource as usual.  Otherwise, the return
      * response and data will be used.
      *
+     * <p>This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code
+     * data:}, {@code file:}, etc.), not only those schemes which send requests over the network.
+     * This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed
+     * via {@code file:///android_asset/} or {@code file:///android_res/} URLs.
+     *
+     * <p>In the case of redirects, this is only called for the initial resource URL, not any
+     * subsequent redirect URLs.
+     *
      * <p class="note"><b>Note:</b> This method is called on a thread
      * other than the UI thread so clients should exercise caution
      * when accessing private data or the view system.
@@ -182,6 +190,14 @@
      * will continue to load the resource as usual.  Otherwise, the return
      * response and data will be used.
      *
+     * <p>This callback is invoked for a variety of URL schemes (e.g., {@code http(s):}, {@code
+     * data:}, {@code file:}, etc.), not only those schemes which send requests over the network.
+     * This is not called for {@code javascript:} URLs, {@code blob:} URLs, or for assets accessed
+     * via {@code file:///android_asset/} or {@code file:///android_res/} URLs.
+     *
+     * <p>In the case of redirects, this is only called for the initial resource URL, not any
+     * subsequent redirect URLs.
+     *
      * <p class="note"><b>Note:</b> This method is called on a thread
      * other than the UI thread so clients should exercise caution
      * when accessing private data or the view system.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 3775835..9946726 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
 import android.content.ClipData;
 import android.content.ClipData.Item;
 import android.content.Context;
@@ -4045,43 +4046,46 @@
             if (textClassification == null) {
                 return;
             }
-            final OnClickListener onClick = getSupportedOnClickListener(
-                    textClassification.getIcon(),
-                    textClassification.getLabel(),
-                    textClassification.getIntent());
-            if (onClick != null) {
+            if (!textClassification.getActions().isEmpty()) {
+                // Primary assist action (Always shown).
+                final MenuItem item = addAssistMenuItem(menu,
+                        textClassification.getActions().get(0), TextView.ID_ASSIST,
+                        MENU_ITEM_ORDER_ASSIST, MenuItem.SHOW_AS_ACTION_ALWAYS);
+                item.setIntent(textClassification.getIntent());
+            } else if (hasLegacyAssistItem(textClassification)) {
+                // Legacy primary assist action (Always shown).
                 final MenuItem item = menu.add(
                         TextView.ID_ASSIST, TextView.ID_ASSIST, MENU_ITEM_ORDER_ASSIST,
                         textClassification.getLabel())
                         .setIcon(textClassification.getIcon())
                         .setIntent(textClassification.getIntent());
                 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
-                mAssistClickHandlers.put(
-                        item, TextClassification.createIntentOnClickListener(
-                                mTextView.getContext(), textClassification.getIntent()));
+                mAssistClickHandlers.put(item, TextClassification.createIntentOnClickListener(
+                        TextClassification.createPendingIntent(mTextView.getContext(),
+                                textClassification.getIntent())));
             }
-            final int count = textClassification.getSecondaryActionsCount();
-            for (int i = 0; i < count; i++) {
-                final OnClickListener onClick1 = getSupportedOnClickListener(
-                        textClassification.getSecondaryIcon(i),
-                        textClassification.getSecondaryLabel(i),
-                        textClassification.getSecondaryIntent(i));
-                if (onClick1 == null) {
-                    continue;
-                }
-                final int order = MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i;
-                final MenuItem item = menu.add(
-                        TextView.ID_ASSIST, Menu.NONE, order,
-                        textClassification.getSecondaryLabel(i))
-                        .setIcon(textClassification.getSecondaryIcon(i))
-                        .setIntent(textClassification.getSecondaryIntent(i));
-                item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
-                mAssistClickHandlers.put(item,
-                        TextClassification.createIntentOnClickListener(
-                                mTextView.getContext(), textClassification.getSecondaryIntent(i)));
+            final int count = textClassification.getActions().size();
+            for (int i = 1; i < count; i++) {
+                // Secondary assist action (Never shown).
+                addAssistMenuItem(menu, textClassification.getActions().get(i), Menu.NONE,
+                        MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i - 1,
+                        MenuItem.SHOW_AS_ACTION_NEVER);
             }
         }
 
+        private MenuItem addAssistMenuItem(Menu menu, RemoteAction action, int intemId, int order,
+                int showAsAction) {
+            final MenuItem item = menu.add(TextView.ID_ASSIST, intemId, order, action.getTitle())
+                    .setContentDescription(action.getContentDescription());
+            if (action.shouldShowIcon()) {
+                item.setIcon(action.getIcon().loadDrawable(mTextView.getContext()));
+            }
+            item.setShowAsAction(showAsAction);
+            mAssistClickHandlers.put(item,
+                    TextClassification.createIntentOnClickListener(action.getActionIntent()));
+            return item;
+        }
+
         private void clearAssistMenuItems(Menu menu) {
             int i = 0;
             while (i < menu.size()) {
@@ -4094,15 +4098,11 @@
             }
         }
 
-        @Nullable
-        private OnClickListener getSupportedOnClickListener(
-                Drawable icon, CharSequence label, Intent intent) {
-            final boolean hasUi = icon != null || !TextUtils.isEmpty(label);
-            if (hasUi) {
-                return TextClassification.createIntentOnClickListener(
-                        mTextView.getContext(), intent);
-            }
-            return null;
+        private boolean hasLegacyAssistItem(TextClassification classification) {
+            // Check whether we have the UI data and and action.
+            return (classification.getIcon() != null || !TextUtils.isEmpty(
+                    classification.getLabel())) && (classification.getIntent() != null
+                    || classification.getOnClickListener() != null);
         }
 
         private boolean onAssistMenuItemClicked(MenuItem assistMenuItem) {
@@ -4120,7 +4120,7 @@
                 final Intent intent = assistMenuItem.getIntent();
                 if (intent != null) {
                     onClickListener = TextClassification.createIntentOnClickListener(
-                            mTextView.getContext(), intent);
+                            TextClassification.createPendingIntent(mTextView.getContext(), intent));
                 }
             }
             if (onClickListener != null) {
@@ -4585,8 +4585,8 @@
             return mContainer.isShowing();
         }
 
-        private boolean isVisible() {
-            // Always show a dragging handle.
+        private boolean shouldShow() {
+            // A dragging handle should always be shown.
             if (mIsDragging) {
                 return true;
             }
@@ -4599,6 +4599,10 @@
                     mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
         }
 
+        private void setVisible(final boolean visible) {
+            mContainer.getContentView().setVisibility(visible ? VISIBLE : INVISIBLE);
+        }
+
         public abstract int getCurrentCursorOffset();
 
         protected abstract void updateSelection(int offset);
@@ -4692,7 +4696,7 @@
                     onHandleMoved();
                 }
 
-                if (isVisible()) {
+                if (shouldShow()) {
                     // Transform to the window coordinates to follow the view tranformation.
                     final int[] pts = { mPositionX + mHotspotX + getHorizontalOffset(), mPositionY};
                     mTextView.transformFromViewToWindowSpace(pts);
@@ -4745,6 +4749,15 @@
             return 0;
         }
 
+        private boolean tooLargeTextForMagnifier() {
+            final float magnifierContentHeight = Math.round(
+                    mMagnifierAnimator.mMagnifier.getHeight()
+                            / mMagnifierAnimator.mMagnifier.getZoom());
+            final Paint.FontMetrics fontMetrics = mTextView.getPaint().getFontMetrics();
+            final float glyphHeight = fontMetrics.descent - fontMetrics.ascent;
+            return glyphHeight > magnifierContentHeight;
+        }
+
         /**
          * Computes the position where the magnifier should be shown, relative to
          * {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4824,18 +4837,34 @@
             return true;
         }
 
+        private boolean handleOverlapsMagnifier() {
+            final int handleY = mContainer.getDecorViewLayoutParams().y;
+            final int magnifierBottomWhenAtWindowTop =
+                    mTextView.getRootWindowInsets().getSystemWindowInsetTop()
+                        + mMagnifierAnimator.mMagnifier.getHeight();
+            return handleY <= magnifierBottomWhenAtWindowTop;
+        }
+
         protected final void updateMagnifier(@NonNull final MotionEvent event) {
             if (mMagnifierAnimator == null) {
                 return;
             }
 
             final PointF showPosInView = new PointF();
-            final boolean shouldShow = obtainMagnifierShowCoordinates(event, showPosInView);
+            final boolean shouldShow = !tooLargeTextForMagnifier()
+                    && obtainMagnifierShowCoordinates(event, showPosInView);
             if (shouldShow) {
                 // Make the cursor visible and stop blinking.
                 mRenderCursorRegardlessTiming = true;
                 mTextView.invalidateCursorPath();
                 suspendBlink();
+                // Hide handle if it overlaps the magnifier.
+                if (handleOverlapsMagnifier()) {
+                    setVisible(false);
+                } else {
+                    setVisible(true);
+                }
+
                 mMagnifierAnimator.show(showPosInView.x, showPosInView.y);
             } else {
                 dismissMagnifier();
@@ -4847,6 +4876,7 @@
                 mMagnifierAnimator.dismiss();
                 mRenderCursorRegardlessTiming = false;
                 resumeBlink();
+                setVisible(true);
             }
         }
 
diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java
index 05204d0..8b49ccb 100644
--- a/core/java/android/widget/SelectionActionModeHelper.java
+++ b/core/java/android/widget/SelectionActionModeHelper.java
@@ -35,6 +35,7 @@
 import android.view.ActionMode;
 import android.view.textclassifier.Logger;
 import android.view.textclassifier.SelectionEvent;
+import android.view.textclassifier.SelectionEvent.InvocationMethod;
 import android.view.textclassifier.TextClassification;
 import android.view.textclassifier.TextClassificationConstants;
 import android.view.textclassifier.TextClassificationManager;
@@ -86,7 +87,7 @@
         mTextClassificationSettings = TextClassificationManager.getSettings(mTextView.getContext());
         mTextClassificationHelper = new TextClassificationHelper(
                 mTextView.getContext(),
-                mTextView.getTextClassifier(),
+                mTextView::getTextClassifier,
                 getText(mTextView),
                 0, 1, mTextView.getTextLocales());
         mSelectionTracker = new SelectionTracker(mTextView);
@@ -221,7 +222,7 @@
 
     private boolean skipTextClassification() {
         // No need to make an async call for a no-op TextClassifier.
-        final boolean noOpTextClassifier = mTextView.getTextClassifier() == TextClassifier.NO_OP;
+        final boolean noOpTextClassifier = mTextView.usesNoOpTextClassifier();
         // Do not call the TextClassifier if there is no selection.
         final boolean noSelection = mTextView.getSelectionEnd() == mTextView.getSelectionStart();
         // Do not call the TextClassifier if this is a password field.
@@ -447,7 +448,7 @@
             selectionEnd = mTextView.getSelectionEnd();
         }
         mTextClassificationHelper.init(
-                mTextView.getTextClassifier(),
+                mTextView::getTextClassifier,
                 getText(mTextView),
                 selectionStart, selectionEnd,
                 mTextView.getTextLocales());
@@ -660,6 +661,7 @@
         private static final String LOG_TAG = "SelectionMetricsLogger";
         private static final Pattern PATTERN_WHITESPACE = Pattern.compile("\\s+");
 
+        private final Supplier<TextClassifier> mTextClassificationSession;
         private final Logger mLogger;
         private final boolean mEditTextLogger;
         private final BreakIterator mTokenIterator;
@@ -668,26 +670,27 @@
 
         SelectionMetricsLogger(TextView textView) {
             Preconditions.checkNotNull(textView);
+            mTextClassificationSession = textView::getTextClassificationSession;
             mLogger = textView.getTextClassifier().getLogger(
                     new Logger.Config(textView.getContext(), getWidetType(textView), null));
             mEditTextLogger = textView.isTextEditable();
             mTokenIterator = mLogger.getTokenIterator(textView.getTextLocale());
         }
 
-        @Logger.WidgetType
+        @TextClassifier.WidgetType
         private static String getWidetType(TextView textView) {
             if (textView.isTextEditable()) {
-                return Logger.WIDGET_EDITTEXT;
+                return TextClassifier.WIDGET_TYPE_EDITTEXT;
             }
             if (textView.isTextSelectable()) {
-                return Logger.WIDGET_TEXTVIEW;
+                return TextClassifier.WIDGET_TYPE_TEXTVIEW;
             }
-            return Logger.WIDGET_UNSELECTABLE_TEXTVIEW;
+            return TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
         }
 
         public void logSelectionStarted(
                 CharSequence text, int index,
-                @SelectionEvent.InvocationMethod int invocationMethod) {
+                @InvocationMethod int invocationMethod) {
             try {
                 Preconditions.checkNotNull(text);
                 Preconditions.checkArgumentInRange(index, 0, text.length(), "index");
@@ -697,9 +700,12 @@
                 mTokenIterator.setText(mText);
                 mStartIndex = index;
                 mLogger.logSelectionStartedEvent(invocationMethod, 0);
+                // TODO: Remove the above legacy logging.
+                mTextClassificationSession.get().onSelectionEvent(
+                        SelectionEvent.createSelectionStartedEvent(invocationMethod, 0));
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
             }
         }
 
@@ -712,16 +718,28 @@
                 if (selection != null) {
                     mLogger.logSelectionModifiedEvent(
                             wordIndices[0], wordIndices[1], selection);
+                    // TODO: Remove the above legacy logging.
+                    mTextClassificationSession.get().onSelectionEvent(
+                            SelectionEvent.createSelectionModifiedEvent(
+                                    wordIndices[0], wordIndices[1], selection));
                 } else if (classification != null) {
                     mLogger.logSelectionModifiedEvent(
                             wordIndices[0], wordIndices[1], classification);
+                    // TODO: Remove the above legacy logging.
+                    mTextClassificationSession.get().onSelectionEvent(
+                            SelectionEvent.createSelectionModifiedEvent(
+                                    wordIndices[0], wordIndices[1], classification));
                 } else {
                     mLogger.logSelectionModifiedEvent(
                             wordIndices[0], wordIndices[1]);
+                    // TODO: Remove the above legacy logging.
+                    mTextClassificationSession.get().onSelectionEvent(
+                            SelectionEvent.createSelectionModifiedEvent(
+                                    wordIndices[0], wordIndices[1]));
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
             }
         }
 
@@ -736,13 +754,25 @@
                 if (classification != null) {
                     mLogger.logSelectionActionEvent(
                             wordIndices[0], wordIndices[1], action, classification);
+                    // TODO: Remove the above legacy logging.
+                    mTextClassificationSession.get().onSelectionEvent(
+                            SelectionEvent.createSelectionActionEvent(
+                                    wordIndices[0], wordIndices[1], action, classification));
                 } else {
                     mLogger.logSelectionActionEvent(
                             wordIndices[0], wordIndices[1], action);
+                    // TODO: Remove the above legacy logging.
+                    mTextClassificationSession.get().onSelectionEvent(
+                            SelectionEvent.createSelectionActionEvent(
+                                    wordIndices[0], wordIndices[1], action));
                 }
             } catch (Exception e) {
                 // Avoid crashes due to logging.
-                Log.d(LOG_TAG, e.getMessage());
+                Log.e(LOG_TAG, "" + e.getMessage(), e);
+            } finally {
+                if (SelectionEvent.isTerminal(action)) {
+                    mTextClassificationSession.get().destroy();
+                }
             }
         }
 
@@ -887,7 +917,7 @@
 
         private final Context mContext;
         private final boolean mDarkLaunchEnabled;
-        private TextClassifier mTextClassifier;
+        private Supplier<TextClassifier> mTextClassifier;
 
         /** The original TextView text. **/
         private String mText;
@@ -919,7 +949,7 @@
         /** Whether the TextClassifier has been initialized. */
         private boolean mHot;
 
-        TextClassificationHelper(Context context, TextClassifier textClassifier,
+        TextClassificationHelper(Context context, Supplier<TextClassifier> textClassifier,
                 CharSequence text, int selectionStart, int selectionEnd, LocaleList locales) {
             init(textClassifier, text, selectionStart, selectionEnd, locales);
             mContext = Preconditions.checkNotNull(context);
@@ -928,7 +958,7 @@
         }
 
         @UiThread
-        public void init(TextClassifier textClassifier, CharSequence text,
+        public void init(Supplier<TextClassifier> textClassifier, CharSequence text,
                 int selectionStart, int selectionEnd, LocaleList locales) {
             mTextClassifier = Preconditions.checkNotNull(textClassifier);
             mText = Preconditions.checkNotNull(text).toString();
@@ -953,11 +983,11 @@
             trimText();
             final TextSelection selection;
             if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
-                selection = mTextClassifier.suggestSelection(
+                selection = mTextClassifier.get().suggestSelection(
                         mTrimmedText, mRelativeStart, mRelativeEnd, mSelectionOptions);
             } else {
                 // Use old APIs.
-                selection = mTextClassifier.suggestSelection(
+                selection = mTextClassifier.get().suggestSelection(
                         mTrimmedText, mRelativeStart, mRelativeEnd,
                         mSelectionOptions.getDefaultLocales());
             }
@@ -1006,11 +1036,11 @@
                 trimText();
                 final TextClassification classification;
                 if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.O_MR1) {
-                    classification = mTextClassifier.classifyText(
+                    classification = mTextClassifier.get().classifyText(
                             mTrimmedText, mRelativeStart, mRelativeEnd, mClassificationOptions);
                 } else {
                     // Use old APIs.
-                    classification = mTextClassifier.classifyText(
+                    classification = mTextClassifier.get().classifyText(
                             mTrimmedText, mRelativeStart, mRelativeEnd,
                             mClassificationOptions.getDefaultLocales());
                 }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 7b1acb1..8bf497e 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -163,6 +163,7 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.view.textclassifier.TextClassification;
+import android.view.textclassifier.TextClassificationContext;
 import android.view.textclassifier.TextClassificationManager;
 import android.view.textclassifier.TextClassifier;
 import android.view.textclassifier.TextLinks;
@@ -427,6 +428,7 @@
     private boolean mPreDrawListenerDetached;
 
     private TextClassifier mTextClassifier;
+    private TextClassifier mTextClassificationSession;
 
     // A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
     // that if a user is holding down a movement key to traverse text, we shouldn't also traverse
@@ -11527,18 +11529,63 @@
     @NonNull
     public TextClassifier getTextClassifier() {
         if (mTextClassifier == null) {
-            TextClassificationManager tcm =
+            final TextClassificationManager tcm =
                     mContext.getSystemService(TextClassificationManager.class);
             if (tcm != null) {
-                mTextClassifier = tcm.getTextClassifier();
-            } else {
-                mTextClassifier = TextClassifier.NO_OP;
+                return tcm.getTextClassifier();
             }
+            return TextClassifier.NO_OP;
         }
         return mTextClassifier;
     }
 
     /**
+     * Returns a session-aware text classifier.
+     */
+    @NonNull
+    TextClassifier getTextClassificationSession() {
+        if (mTextClassificationSession == null || mTextClassificationSession.isDestroyed()) {
+            final TextClassificationManager tcm =
+                    mContext.getSystemService(TextClassificationManager.class);
+            if (tcm != null) {
+                final String widgetType;
+                if (isTextEditable()) {
+                    widgetType = TextClassifier.WIDGET_TYPE_EDITTEXT;
+                } else if (isTextSelectable()) {
+                    widgetType = TextClassifier.WIDGET_TYPE_TEXTVIEW;
+                } else {
+                    widgetType = TextClassifier.WIDGET_TYPE_UNSELECTABLE_TEXTVIEW;
+                }
+                // TODO: Tagged this widgetType with a * so it we can monitor if it reports
+                // SelectionEvents exactly as the older Logger does. Remove once investigations
+                // are complete.
+                final TextClassificationContext textClassificationContext =
+                        new TextClassificationContext.Builder(
+                                mContext.getPackageName(), "*" + widgetType)
+                                .build();
+                if (mTextClassifier != null) {
+                    mTextClassificationSession = tcm.createTextClassificationSession(
+                            textClassificationContext, mTextClassifier);
+                } else {
+                    mTextClassificationSession = tcm.createTextClassificationSession(
+                            textClassificationContext);
+                }
+            } else {
+                mTextClassificationSession = TextClassifier.NO_OP;
+            }
+        }
+        return mTextClassificationSession;
+    }
+
+    /**
+     * Returns true if this TextView uses a no-op TextClassifier.
+     */
+    boolean usesNoOpTextClassifier() {
+        return getTextClassifier() == TextClassifier.NO_OP;
+    }
+
+
+    /**
      * Starts an ActionMode for the specified TextLinkSpan.
      *
      * @return Whether or not we're attempting to start the action mode.
@@ -12474,9 +12521,8 @@
                         + " before=" + before + " after=" + after + ": " + buffer);
             }
 
-            if (AccessibilityManager.getInstance(mContext).isEnabled()
-                    && !isPasswordInputType(getInputType()) && !hasPasswordTransformationMethod()) {
-                mBeforeText = buffer.toString();
+            if (AccessibilityManager.getInstance(mContext).isEnabled() && (mTransformed != null)) {
+                mBeforeText = mTransformed.toString();
             }
 
             TextView.this.sendBeforeTextChanged(buffer, start, before, after);
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 514ff76..03dd77f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -93,6 +93,7 @@
     void noteVibratorOff(int uid);
     void noteGpsChanged(in WorkSource oldSource, in WorkSource newSource);
     void noteGpsSignalQuality(int signalLevel);
+    void noteUsbConnectionState(boolean connected);
     void noteScreenState(int state);
     void noteScreenBrightness(int brightness);
     void noteUserActivity(int uid, int event);
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index b049db3..f4b7032 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -187,11 +187,27 @@
 
     @Override
     public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
+        return performBackup(packageInfo, data, /*flags=*/ 0);
+    }
+
+    @Override
+    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+        boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
+        boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
+
+        if (isIncremental) {
+            Log.i(TAG, "Performing incremental backup for " + packageInfo.packageName);
+        } else if (isNonIncremental) {
+            Log.i(TAG, "Performing non-incremental backup for " + packageInfo.packageName);
+        } else {
+            Log.i(TAG, "Performing backup for " + packageInfo.packageName);
+        }
+
         if (DEBUG) {
             try {
-            StructStat ss = Os.fstat(data.getFileDescriptor());
-            Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
-                    + " size=" + ss.st_size);
+                StructStat ss = Os.fstat(data.getFileDescriptor());
+                Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName
+                        + " size=" + ss.st_size + " flags=" + flags);
             } catch (ErrnoException e) {
                 Log.w(TAG, "Unable to stat input file in performBackup() on "
                         + packageInfo.packageName);
@@ -199,7 +215,26 @@
         }
 
         File packageDir = new File(mCurrentSetIncrementalDir, packageInfo.packageName);
-        packageDir.mkdirs();
+        boolean hasDataForPackage = !packageDir.mkdirs();
+
+        if (isIncremental) {
+            if (mParameters.isNonIncrementalOnly() || !hasDataForPackage) {
+                if (mParameters.isNonIncrementalOnly()) {
+                    Log.w(TAG, "Transport is in non-incremental only mode.");
+
+                } else {
+                    Log.w(TAG,
+                            "Requested incremental, but transport currently stores no data for the "
+                                    + "package, requesting non-incremental retry.");
+                }
+                return TRANSPORT_NON_INCREMENTAL_BACKUP_REQUIRED;
+            }
+        }
+        if (isNonIncremental && hasDataForPackage) {
+            Log.w(TAG, "Requested non-incremental, deleting existing data.");
+            clearBackupData(packageInfo);
+            packageDir.mkdirs();
+        }
 
         // Each 'record' in the restore set is kept in its own file, named by
         // the record key.  Wind through the data file, extracting individual
diff --git a/core/java/com/android/internal/backup/LocalTransportParameters.java b/core/java/com/android/internal/backup/LocalTransportParameters.java
index 154e79d..2427d39 100644
--- a/core/java/com/android/internal/backup/LocalTransportParameters.java
+++ b/core/java/com/android/internal/backup/LocalTransportParameters.java
@@ -26,8 +26,10 @@
     private static final String TAG = "LocalTransportParams";
     private static final String SETTING = Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS;
     private static final String KEY_FAKE_ENCRYPTION_FLAG = "fake_encryption_flag";
+    private static final String KEY_NON_INCREMENTAL_ONLY = "non_incremental_only";
 
     private boolean mFakeEncryptionFlag;
+    private boolean mIsNonIncrementalOnly;
 
     LocalTransportParameters(Handler handler, ContentResolver resolver) {
         super(handler, resolver, Settings.Secure.getUriFor(SETTING));
@@ -37,11 +39,16 @@
         return mFakeEncryptionFlag;
     }
 
+    boolean isNonIncrementalOnly() {
+        return mIsNonIncrementalOnly;
+    }
+
     public String getSettingValue(ContentResolver resolver) {
         return Settings.Secure.getString(resolver, SETTING);
     }
 
     public void update(KeyValueListParser parser) {
         mFakeEncryptionFlag = parser.getBoolean(KEY_FAKE_ENCRYPTION_FLAG, false);
+        mIsNonIncrementalOnly = parser.getBoolean(KEY_NON_INCREMENTAL_ONLY, false);
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3c150c1..89f6156 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.UidTraffic;
 import android.content.ContentResolver;
@@ -767,6 +766,8 @@
     int mCameraOnNesting;
     StopwatchTimer mCameraOnTimer;
 
+    int mUsbDataState; // 0: unknown, 1: disconnected, 2: connected
+
     int mGpsSignalQualityBin = -1;
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     protected final StopwatchTimer[] mGpsSignalQualityTimer =
@@ -5240,6 +5241,19 @@
         }
     }
 
+    public void noteUsbConnectionStateLocked(boolean connected) {
+        int newState = connected ? 2 : 1;
+        if (mUsbDataState != newState) {
+            mUsbDataState = newState;
+            if (connected) {
+                mHistoryCur.states2 |= HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+            } else {
+                mHistoryCur.states2 &= ~HistoryItem.STATE2_USB_DATA_LINK_FLAG;
+            }
+            addHistoryRecordLocked(mClocks.elapsedRealtime(), mClocks.uptimeMillis());
+        }
+    }
+
     void stopAllPhoneSignalStrengthTimersLocked(int except) {
         final long elapsedRealtime = mClocks.elapsedRealtime();
         for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
@@ -5652,29 +5666,7 @@
             mBluetoothScanTimer.startRunningLocked(elapsedRealtime);
         }
         mBluetoothScanNesting++;
-
-        if (workChain != null) {
-            StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
-                    workChain.getUids(), workChain.getTags(),
-                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
-            if (isUnoptimized) {
-                StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(),
-                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
-            }
-        } else {
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
-                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__ON);
-            if (isUnoptimized) {
-                StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
-                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__ON);
-            }
-        }
-
         getUidStatsLocked(uid).noteBluetoothScanStartedLocked(elapsedRealtime, isUnoptimized);
-        if (workChain != null) {
-            getUidStatsLocked(uid).addBluetoothWorkChain(workChain, isUnoptimized);
-        }
     }
 
     public void noteBluetoothScanStartedFromSourceLocked(WorkSource ws, boolean isUnoptimized) {
@@ -5704,29 +5696,7 @@
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothScanTimer.stopRunningLocked(elapsedRealtime);
         }
-
-        if (workChain != null) {
-            StatsLog.write(
-                    StatsLog.BLE_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(),
-                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
-            if (isUnoptimized) {
-                StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
-                        workChain.getUids(), workChain.getTags(),
-                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
-            }
-        } else {
-            StatsLog.write_non_chained(StatsLog.BLE_SCAN_STATE_CHANGED, uid, null,
-                    StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
-            if (isUnoptimized) {
-                StatsLog.write_non_chained(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED, uid, null,
-                        StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
-            }
-        }
-
         getUidStatsLocked(uid).noteBluetoothScanStoppedLocked(elapsedRealtime, isUnoptimized);
-        if (workChain != null) {
-            getUidStatsLocked(uid).removeBluetoothWorkChain(workChain, isUnoptimized);
-        }
     }
 
     private int getAttributionUid(int uid, WorkChain workChain) {
@@ -5761,33 +5731,9 @@
                     + Integer.toHexString(mHistoryCur.states2));
             addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtime);
-
-
             for (int i=0; i<mUidStats.size(); i++) {
                 BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
                 uid.noteResetBluetoothScanLocked(elapsedRealtime);
-
-                List<WorkChain> allWorkChains = uid.getAllBluetoothWorkChains();
-                if (allWorkChains != null) {
-                    for (int j = 0; j < allWorkChains.size(); ++j) {
-                        StatsLog.write(StatsLog.BLE_SCAN_STATE_CHANGED,
-                                allWorkChains.get(j).getUids(),
-                                allWorkChains.get(j).getTags(),
-                                StatsLog.BLE_SCAN_STATE_CHANGED__STATE__OFF);
-                    }
-                    allWorkChains.clear();
-                }
-
-                List<WorkChain> unoptimizedWorkChains = uid.getUnoptimizedBluetoothWorkChains();
-                if (unoptimizedWorkChains != null) {
-                    for (int j = 0; j < unoptimizedWorkChains.size(); ++j) {
-                        StatsLog.write(StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED,
-                                unoptimizedWorkChains.get(j).getUids(),
-                                unoptimizedWorkChains.get(j).getTags(),
-                                StatsLog.BLE_UNOPTIMIZED_SCAN_STATE_CHANGED__STATE__OFF);
-                    }
-                    unoptimizedWorkChains.clear();
-                }
             }
         }
     }
@@ -5999,7 +5945,6 @@
             if (strengthBin >= 0) {
                 if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
                     mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
-                    StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
                 }
                 mHistoryCur.states2 =
                         (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
@@ -6010,6 +5955,7 @@
             } else {
                 stopAllWifiSignalStrengthTimersLocked(-1);
             }
+            StatsLog.write(StatsLog.WIFI_SIGNAL_STRENGTH_CHANGED, strengthBin);
             mWifiSignalStrengthBin = strengthBin;
         }
     }
@@ -6859,15 +6805,6 @@
          */
         final SparseArray<Pid> mPids = new SparseArray<>();
 
-        /**
-         * The list of WorkChains associated with active bluetooth scans.
-         *
-         * NOTE: This is a hack and it only needs to exist because there's a "reset" API that is
-         * supposed to stop and log all WorkChains that were currently active.
-         */
-        ArrayList<WorkChain> mAllBluetoothChains = null;
-        ArrayList<WorkChain> mUnoptimizedBluetoothChains = null;
-
         public Uid(BatteryStatsImpl bsi, int uid) {
             mBsi = bsi;
             mUid = uid;
@@ -7250,25 +7187,17 @@
 
         public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
             createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                    StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
         }
 
         public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mAudioTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                            StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetAudioLocked(long elapsedRealtimeMs) {
             if (mAudioTurnedOnTimer != null) {
                 mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, getUid(), null,
-                        StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7282,25 +7211,17 @@
 
         public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
             createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
-                    StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__ON);
         }
 
         public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
             if (mVideoTurnedOnTimer != null) {
                 mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mVideoTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(),
-                            null, StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetVideoLocked(long elapsedRealtimeMs) {
             if (mVideoTurnedOnTimer != null) {
                 mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED, getUid(), null,
-                        StatsLog.MEDIA_CODEC_ACTIVITY_CHANGED__STATE__OFF);
             }
         }
 
@@ -7314,25 +7235,17 @@
 
         public void noteFlashlightTurnedOnLocked(long elapsedRealtimeMs) {
             createFlashlightTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
         }
 
         public void noteFlashlightTurnedOffLocked(long elapsedRealtimeMs) {
             if (mFlashlightTurnedOnTimer != null) {
                 mFlashlightTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mFlashlightTurnedOnTimer.isRunningLocked()) {
-                    StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                            StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetFlashlightLocked(long elapsedRealtimeMs) {
             if (mFlashlightTurnedOnTimer != null) {
                 mFlashlightTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, getUid(), null,
-                        StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7346,25 +7259,17 @@
 
         public void noteCameraTurnedOnLocked(long elapsedRealtimeMs) {
             createCameraTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
-            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                    StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
         }
 
         public void noteCameraTurnedOffLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
-                if (!mCameraTurnedOnTimer.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                            StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
         public void noteResetCameraLocked(long elapsedRealtimeMs) {
             if (mCameraTurnedOnTimer != null) {
                 mCameraTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, getUid(), null,
-                        StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
             }
         }
 
@@ -7428,40 +7333,6 @@
             }
         }
 
-        public void addBluetoothWorkChain(WorkChain workChain, boolean isUnoptimized) {
-            if (mAllBluetoothChains == null) {
-                mAllBluetoothChains = new ArrayList<WorkChain>(4);
-            }
-
-            if (isUnoptimized && mUnoptimizedBluetoothChains == null) {
-                mUnoptimizedBluetoothChains = new ArrayList<WorkChain>(4);
-            }
-
-            mAllBluetoothChains.add(workChain);
-            if (isUnoptimized) {
-                mUnoptimizedBluetoothChains.add(workChain);
-            }
-        }
-
-        public void removeBluetoothWorkChain(WorkChain workChain, boolean isUnoptimized) {
-            if (mAllBluetoothChains != null) {
-                mAllBluetoothChains.remove(workChain);
-            }
-
-            if (isUnoptimized && mUnoptimizedBluetoothChains != null) {
-                mUnoptimizedBluetoothChains.remove(workChain);
-            }
-        }
-
-        public List<WorkChain> getAllBluetoothWorkChains() {
-            return mAllBluetoothChains;
-        }
-
-        public List<WorkChain> getUnoptimizedBluetoothWorkChains() {
-            return mUnoptimizedBluetoothChains;
-        }
-
-
         public void noteResetBluetoothScanLocked(long elapsedRealtimeMs) {
             if (mBluetoothScanTimer != null) {
                 mBluetoothScanTimer.stopAllRunningLocked(elapsedRealtimeMs);
@@ -10040,8 +9911,6 @@
             DualTimer t = mSyncStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
-                        StatsLog.SYNC_STATE_CHANGED__STATE__ON);
             }
         }
 
@@ -10049,10 +9918,6 @@
             DualTimer t = mSyncStats.stopObject(name);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (!t.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, getUid(), null, name,
-                            StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
@@ -10060,9 +9925,6 @@
             DualTimer t = mJobStats.startObject(name);
             if (t != null) {
                 t.startRunningLocked(elapsedRealtimeMs);
-                StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                        name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
-                        JobProtoEnums.STOP_REASON_CANCELLED);
             }
         }
 
@@ -10070,11 +9932,6 @@
             DualTimer t = mJobStats.stopObject(name);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (!t.isRunningLocked()) { // only tell statsd if truly stopped
-                    StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, getUid(), null,
-                            name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
-                            stopReason);
-                }
             }
             if (mBsi.mOnBatteryTimeBase.isRunning()) {
                 SparseIntArray types = mJobCompletions.get(name);
@@ -10182,10 +10039,6 @@
         public void noteStartSensor(int sensor, long elapsedRealtimeMs) {
             DualTimer t = getSensorTimerLocked(sensor, /* create= */ true);
             t.startRunningLocked(elapsedRealtimeMs);
-            if (sensor != Sensor.GPS) {
-                StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null, sensor,
-                        StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
-            }
         }
 
         public void noteStopSensor(int sensor, long elapsedRealtimeMs) {
@@ -10193,10 +10046,6 @@
             DualTimer t = getSensorTimerLocked(sensor, false);
             if (t != null) {
                 t.stopRunningLocked(elapsedRealtimeMs);
-                if (sensor != Sensor.GPS) {
-                    StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, getUid(), null,
-                             sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
-                }
             }
         }
 
diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java
index 89a4e17..dc660a4 100644
--- a/core/java/com/android/internal/os/RoSystemProperties.java
+++ b/core/java/com/android/internal/os/RoSystemProperties.java
@@ -31,6 +31,8 @@
             SystemProperties.get("ro.control_privapp_permissions");
 
     // ------ ro.config.* -------- //
+    public static final boolean CONFIG_AVOID_GFX_ACCEL =
+            SystemProperties.getBoolean("ro.config.avoid_gfx_accel", false);
     public static final boolean CONFIG_LOW_RAM =
             SystemProperties.getBoolean("ro.config.low_ram", false);
     public static final boolean CONFIG_SMALL_BATTERY =
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index cd83c57..5d40a73 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -47,6 +47,8 @@
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
+
 import libcore.io.IoUtils;
 
 /**
@@ -159,6 +161,11 @@
             return null;
         }
 
+        if (parsedArgs.apiBlacklistExemptions != null) {
+            handleApiBlacklistExemptions(parsedArgs.apiBlacklistExemptions);
+            return null;
+        }
+
         if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
             throw new ZygoteSecurityException("Client may not specify capabilities: " +
                     "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
@@ -278,6 +285,15 @@
         }
     }
 
+    private void handleApiBlacklistExemptions(String[] exemptions) {
+        try {
+            ZygoteInit.setApiBlacklistExemptions(exemptions);
+            mSocketOutStream.writeInt(0);
+        } catch (IOException ioe) {
+            throw new IllegalStateException("Error writing to command socket", ioe);
+        }
+    }
+
     protected void preload() {
         ZygoteInit.lazyPreload();
     }
@@ -439,6 +455,12 @@
         boolean startChildZygote;
 
         /**
+         * Exemptions from API blacklisting. These are sent to the pre-forked zygote at boot time,
+         * or when they change, via --set-api-blacklist-exemptions.
+         */
+        String[] apiBlacklistExemptions;
+
+        /**
          * Constructs instance and parses args
          * @param args zygote command-line args
          * @throws IllegalArgumentException
@@ -592,6 +614,11 @@
                     preloadDefault = true;
                 } else if (arg.equals("--start-child-zygote")) {
                     startChildZygote = true;
+                } else if (arg.equals("--set-api-blacklist-exemptions")) {
+                    // consume all remaining args; this is a stand-alone command, never included
+                    // with the regular fork command.
+                    apiBlacklistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
+                    curArg = args.length;
                 } else {
                     break;
                 }
@@ -606,7 +633,7 @@
                     throw new IllegalArgumentException(
                             "Unexpected arguments after --preload-package.");
                 }
-            } else if (!preloadDefault) {
+            } else if (!preloadDefault && apiBlacklistExemptions == null) {
                 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 9467ecc..c5d41db 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -514,6 +514,10 @@
         /* should never reach here */
     }
 
+    public static void setApiBlacklistExemptions(String[] exemptions) {
+        VMRuntime.getRuntime().setHiddenApiExemptions(exemptions);
+    }
+
     /**
      * 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/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 221bf88..ad5743d 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
 
@@ -141,7 +141,7 @@
     void showShutdownUi(boolean isReboot, String reason);
 
     // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
+    void showFingerprintDialog(in Bundle bundle, IBiometricDialogReceiver receiver);
     // Used to hide the dialog when a finger is authenticated
     void onFingerprintAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index adf4287..0c5efe2 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -20,7 +20,7 @@
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.service.notification.StatusBarNotification;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.StatusBarIcon;
@@ -88,7 +88,7 @@
     void showPinningEscapeToast();
 
     // Used to show the dialog when FingerprintService starts authentication
-    void showFingerprintDialog(in Bundle bundle, IFingerprintDialogReceiver receiver);
+    void showFingerprintDialog(in Bundle bundle, IBiometricDialogReceiver receiver);
     // Used to hide the dialog when a finger is authenticated
     void onFingerprintAuthenticated();
     // Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 59b14f1..ae7ba19 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -61,7 +61,6 @@
     void initRecoveryServiceWithSigFile(in String rootCertificateAlias,
             in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile);
     KeyChainSnapshot getKeyChainSnapshot();
-    byte[] generateAndStoreKey(String alias);
     String generateKey(String alias);
     String importKey(String alias, in byte[] keyBytes);
     String getKey(String alias);
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 51dd929..e8fc598 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1000,6 +1000,11 @@
             setPatternInProgress(false);
             cancelLineAnimations();
             notifyPatternDetected();
+            // Also clear pattern if fading is enabled
+            if (mFadePattern) {
+                clearPatternDrawLookup();
+                mPatternDisplayMode = DisplayMode.Correct;
+            }
             invalidate();
         }
         if (PROFILE_DRAWING) {
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c71e505..c5be8e4 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -142,12 +142,24 @@
     // Package names that are exempted from private API blacklisting
     final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
 
+    // The list of carrier applications which should be disabled until used.
+    // This function suppresses update notifications for these pre-installed apps.
+    // In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the
+    // following conditions are met.
+    // 1. Not currently carrier-privileged according to the inserted SIM
+    // 2. Pre-installed
+    // 3. In the default state (enabled but not explicitly)
+    // And SubscriptionInfoUpdater undoes this and marks the app enabled when a SIM is inserted
+    // that marks the app as carrier privileged. It also grants the app default permissions
+    // for Phone and Location. As such, apps MUST only ever be added to this list if they
+    // obtain user consent to access their location through other means.
+    final ArraySet<String> mDisabledUntilUsedPreinstalledCarrierApps = new ArraySet<>();
+
     // These are the packages of carrier-associated apps which should be disabled until used until
     // a SIM is inserted which grants carrier privileges to that carrier app.
     final ArrayMap<String, List<String>> mDisabledUntilUsedPreinstalledCarrierAssociatedApps =
             new ArrayMap<>();
 
-
     final ArrayMap<String, ArraySet<String>> mPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mPrivAppDenyPermissions = new ArrayMap<>();
 
@@ -232,6 +244,10 @@
         return mBackupTransportWhitelist;
     }
 
+    public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
+        return mDisabledUntilUsedPreinstalledCarrierApps;
+    }
+
     public ArrayMap<String, List<String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() {
         return mDisabledUntilUsedPreinstalledCarrierAssociatedApps;
     }
@@ -630,6 +646,18 @@
                         associatedPkgs.add(pkgname);
                     }
                     XmlUtils.skipCurrentTag(parser);
+                } else if ("disabled-until-used-preinstalled-carrier-app".equals(name)
+                        && allowAppConfigs) {
+                    String pkgname = parser.getAttributeValue(null, "package");
+                    if (pkgname == null) {
+                        Slog.w(TAG,
+                                "<disabled-until-used-preinstalled-carrier-app> without "
+                                        + "package in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                    } else {
+                        mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
+                    }
+                    XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
                     // privapp permissions from system, vendor and product partitions are stored
                     // separately. This is to prevent xml files in the vendor partition from
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 726c450..825b7a0 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -210,7 +210,7 @@
                                           jint desiredWidth, jint desiredHeight, jobject jsubset,
                                           jboolean requireMutable, jint allocator,
                                           jboolean requireUnpremul, jboolean preferRamOverQuality,
-                                          jboolean asAlphaMask) {
+                                          jboolean asAlphaMask, jobject jcolorSpace) {
     auto* decoder = reinterpret_cast<ImageDecoder*>(nativePtr);
     SkAndroidCodec* codec = decoder->mCodec.get();
     const SkISize desiredSize = SkISize::Make(desiredWidth, desiredHeight);
@@ -264,7 +264,8 @@
         // This is currently the only way to know that we should decode to F16.
         colorType = codec->computeOutputColorType(colorType);
     }
-    sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+    sk_sp<SkColorSpace> colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
+    colorSpace = codec->computeOutputColorSpace(colorType, colorSpace);
     decodeInfo = decodeInfo.makeColorType(colorType).makeColorSpace(colorSpace);
 
     SkBitmap bm;
@@ -507,18 +508,26 @@
     return encodedFormatToString(env, decoder->mCodec->getEncodedFormat());
 }
 
+static jobject ImageDecoder_nGetColorSpace(JNIEnv* env, jobject /*clazz*/, jlong nativePtr) {
+    auto* codec = reinterpret_cast<ImageDecoder*>(nativePtr)->mCodec.get();
+    auto colorType = codec->computeOutputColorType(codec->getInfo().colorType());
+    sk_sp<SkColorSpace> colorSpace = codec->computeOutputColorSpace(colorType);
+    return GraphicsJNI::getColorSpace(env, colorSpace, colorType);
+}
+
 static const JNINativeMethod gImageDecoderMethods[] = {
     { "nCreate",        "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;",    (void*) ImageDecoder_nCreateAsset },
     { "nCreate",        "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
     { "nCreate",        "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
     { "nCreate",        "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
     { "nCreate",        "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
-    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZ)Landroid/graphics/Bitmap;",
+    { "nDecodeBitmap",  "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZLandroid/graphics/ColorSpace;)Landroid/graphics/Bitmap;",
                                                                  (void*) ImageDecoder_nDecodeBitmap },
     { "nGetSampledSize","(JI)Landroid/util/Size;",               (void*) ImageDecoder_nGetSampledSize },
     { "nGetPadding",    "(JLandroid/graphics/Rect;)V",           (void*) ImageDecoder_nGetPadding },
     { "nClose",         "(J)V",                                  (void*) ImageDecoder_nClose},
     { "nGetMimeType",   "(J)Ljava/lang/String;",                 (void*) ImageDecoder_nGetMimeType },
+    { "nGetColorSpace", "(J)Landroid/graphics/ColorSpace;",      (void*) ImageDecoder_nGetColorSpace },
 };
 
 int register_android_graphics_ImageDecoder(JNIEnv* env) {
diff --git a/core/jni/android/graphics/Path.cpp b/core/jni/android/graphics/Path.cpp
index d3d6882..97abd82 100644
--- a/core/jni/android/graphics/Path.cpp
+++ b/core/jni/android/graphics/Path.cpp
@@ -37,6 +37,14 @@
 class SkPathGlue {
 public:
 
+    static void finalizer(SkPath* obj) {
+        // Purge entries from the HWUI path cache if this path's data is unique
+        if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
+            android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
+        }
+        delete obj;
+    }
+
     // ---------------- Regular JNI -----------------------------
 
     static jlong init(JNIEnv* env, jclass clazz) {
@@ -48,13 +56,8 @@
         return reinterpret_cast<jlong>(new SkPath(*val));
     }
 
-    static void finalize(JNIEnv* env, jclass clazz, jlong objHandle) {
-        SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
-        // Purge entries from the HWUI path cache if this path's data is unique
-        if (obj->unique() && android::uirenderer::Caches::hasInstance()) {
-            android::uirenderer::Caches::getInstance().pathCache.removeDeferred(obj);
-        }
-        delete obj;
+    static jlong getFinalizer(JNIEnv* env, jclass clazz) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
     }
 
     static void set(JNIEnv* env, jclass clazz, jlong dstHandle, jlong srcHandle) {
@@ -469,7 +472,9 @@
         SkRect rect;
         SkPath* obj = reinterpret_cast<SkPath*>(objHandle);
         jboolean result = obj->isRect(&rect);
-        GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+        if (jrect) {
+            GraphicsJNI::rect_to_jrectf(rect, env, jrect);
+        }
         return result;
     }
 
@@ -510,7 +515,7 @@
 static const JNINativeMethod methods[] = {
     {"nInit","()J", (void*) SkPathGlue::init},
     {"nInit","(J)J", (void*) SkPathGlue::init_Path},
-    {"nFinalize", "(J)V", (void*) SkPathGlue::finalize},
+    {"nGetFinalizer", "()J", (void*) SkPathGlue::getFinalizer},
     {"nSet","(JJ)V", (void*) SkPathGlue::set},
     {"nComputeBounds","(JLandroid/graphics/RectF;)V", (void*) SkPathGlue::computeBounds},
     {"nIncReserve","(JI)V", (void*) SkPathGlue::incReserve},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 04cb08f..b0f68cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,7 +37,7 @@
 #include <system/graphics.h>
 #include <ui/DisplayInfo.h>
 #include <ui/FrameStats.h>
-#include <ui/GraphicsTypes.h>
+#include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
 #include <ui/Rect.h>
 #include <ui/Region.h>
@@ -598,7 +598,7 @@
 static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) {
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return NULL;
-    Vector<ColorMode> colorModes;
+    Vector<ui::ColorMode> colorModes;
     if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR ||
             colorModes.isEmpty()) {
         return NULL;
@@ -628,7 +628,7 @@
     sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
     if (token == NULL) return JNI_FALSE;
     status_t err = SurfaceComposerClient::setActiveColorMode(token,
-            static_cast<ColorMode>(colorMode));
+            static_cast<ui::ColorMode>(colorMode));
     return err == NO_ERROR ? JNI_TRUE : JNI_FALSE;
 }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index b5fd792..b2853c9 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -260,7 +260,7 @@
   }
 
   // Apply system or app filter based on uid.
-  if (getuid() >= AID_APP_START) {
+  if (uid >= AID_APP_START) {
     set_app_seccomp_filter();
   } else {
     set_system_seccomp_filter();
@@ -619,11 +619,6 @@
       fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
     }
 
-    // Must be called when the new process still has CAP_SYS_ADMIN.  The other alternative is to
-    // call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
-    // b/71859146).
-    SetUpSeccompFilter(uid);
-
     // Keep capabilities across UID change, unless we're staying root.
     if (uid != 0) {
       if (!EnableKeepCapabilities(&error_msg)) {
@@ -699,6 +694,13 @@
       fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
     }
 
+    // Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
+    // uid from 0, which clears capabilities.  The other alternative is to call
+    // prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
+    // b/71859146).  As the result, privileged syscalls used below still need to be accessible in
+    // app process.
+    SetUpSeccompFilter(uid);
+
     rc = setresuid(uid, uid, uid);
     if (rc == -1) {
       fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
diff --git a/core/proto/android/app/job/enums.proto b/core/proto/android/app/job/enums.proto
index 0f14f20..17bf4fb 100644
--- a/core/proto/android/app/job/enums.proto
+++ b/core/proto/android/app/job/enums.proto
@@ -24,6 +24,7 @@
 // Reasons a job is stopped.
 // Primarily used in android.app.job.JobParameters.java.
 enum StopReasonEnum {
+  STOP_REASON_UNKNOWN = -1;
   STOP_REASON_CANCELLED = 0;
   STOP_REASON_CONSTRAINTS_NOT_SATISFIED = 1;
   STOP_REASON_PREEMPT = 2;
diff --git a/core/proto/android/content/clipdata.proto b/core/proto/android/content/clipdata.proto
index aeeef97..cbc00a7 100644
--- a/core/proto/android/content/clipdata.proto
+++ b/core/proto/android/content/clipdata.proto
@@ -25,7 +25,7 @@
 
 // An android.content.ClipData object.
 message ClipDataProto {
-    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+    option (.android.msg_privacy).dest = DEST_LOCAL;
 
     optional android.content.ClipDescriptionProto description = 1;
 
@@ -40,7 +40,7 @@
 
     // An android.content.ClipData.Item object.
     message Item {
-        option (.android.msg_privacy).dest = DEST_EXPLICIT;
+        option (.android.msg_privacy).dest = DEST_LOCAL;
 
         oneof data {
             string html_text = 1;
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 476d5fe..ea0b825 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -134,8 +134,14 @@
 
     // Linux services
     optional ProcrankProto procrank = 2000 [
-        (section).type = SECTION_NONE, // disable procrank until figure out permission
-        (section).args = "/system/xbin/procrank"
+        // Disable procrank for reasons below:
+        // 1. incidentd can't execute `procrank` because it don't have DAC perms
+        //    since it is running as its own uid, no root access.
+        // 2. the same information is able to be accessed by meminfo dumpsys.
+        // 3. leave this one here to show case of how to disable a section
+        //    (no removal allowed if you are familiar with PROTOBUF).
+        (section).type = SECTION_NONE,
+        (section).args = "procrank"
     ];
 
     optional PageTypeInfoProto page_type_info = 2001 [
@@ -169,9 +175,9 @@
     ];
 
     optional GZippedFileProto last_kmsg = 2007 [
-        (section).type = SECTION_NONE, // disable until selinux permission is gained
+        (section).type = SECTION_GZIP,
         (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg",
-        (privacy).dest = DEST_AUTOMATIC
+        (privacy).dest = DEST_EXPLICIT
     ];
 
     // System Services
diff --git a/core/proto/android/os/system_properties.proto b/core/proto/android/os/system_properties.proto
index 694b94b..8bf3772 100644
--- a/core/proto/android/os/system_properties.proto
+++ b/core/proto/android/os/system_properties.proto
@@ -192,6 +192,8 @@
     optional string libc_debug_malloc_program = 15;
 
     message Log {
+        option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
         optional string tag_WifiHAL = 1;
         optional string tag_stats_log = 2;
 
diff --git a/core/proto/android/providers/settings.proto b/core/proto/android/providers/settings.proto
index 7f8bed5..76a3b5d 100644
--- a/core/proto/android/providers/settings.proto
+++ b/core/proto/android/providers/settings.proto
@@ -20,8 +20,9 @@
 option java_multiple_files = true;
 option java_outer_classname = "SettingsServiceProto";
 
-import "frameworks/base/core/proto/android/providers/settings/common.proto";
 import "frameworks/base/core/proto/android/providers/settings/global.proto";
+import "frameworks/base/core/proto/android/providers/settings/secure.proto";
+import "frameworks/base/core/proto/android/providers/settings/system.proto";
 import "frameworks/base/libs/incident/proto/android/privacy.proto";
 
 message SettingsServiceDumpProto {
@@ -47,351 +48,6 @@
     optional SystemSettingsProto system_settings = 3;
 }
 
-// Note: it's a conscious decision to add each setting as a separate field. This
-// allows annotating each setting with its own privacy tag.
-message SecureSettingsProto {
-    option (android.msg_privacy).dest = DEST_EXPLICIT;
-
-    repeated SettingsOperationProto historical_operations = 1;
-
-    optional SettingProto android_id = 2;
-    optional SettingProto default_input_method = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto selected_input_method_subtype = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto input_methods_subtype_history = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto input_method_selector_visibility = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The currently selected voice interaction service flattened ComponentName.
-    optional SettingProto voice_interaction_service = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The currently selected autofill service flattened ComponentName.
-    optional SettingProto autofill_service = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Boolean indicating if Autofill supports field classification.
-    optional SettingProto autofill_feature_field_classification = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_user_data_max_user_data_size = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_user_data_max_field_classification_ids_size = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_user_data_max_category_count = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_user_data_max_value_length = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto autofill_user_data_min_value_length = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto user_setup_complete = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Whether the current user has been set up via setup wizard (0 = false,
-    // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be
-    // reset back to 0 in case SetupWizard has been re-enabled on TV devices.
-    optional SettingProto tv_user_setup_complete = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    repeated SettingProto completed_categories = 17;
-    optional SettingProto enabled_input_methods = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto disabled_system_input_methods = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_ime_with_hard_keyboard = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto always_on_vpn_app = 21;
-    optional SettingProto always_on_vpn_lockdown = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto install_non_market_apps = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto unknown_sources_default_reversed = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The degree of location access enabled by the user.
-    optional SettingProto location_mode = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The App or module that changes the location mode.
-    optional SettingProto location_changer = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Whether lock-to-app will lock the keyguard when exiting.
-    optional SettingProto lock_to_app_exit_locked = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_screen_lock_after_timeout = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_screen_allow_private_notifications = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_screen_allow_remote_input = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_note_about_notification_hiding = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto trust_agents_initialized = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto parental_control_enabled = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto parental_control_last_update = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto parental_control_redirect_url = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto settings_classname = 36 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_enabled = 37 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_shortcut_enabled = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_shortcut_on_lock_screen = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_shortcut_dialog_shown = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_shortcut_target_service = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Setting specifying the accessibility service or feature to be toggled via
-    // the accessibility button in the navigation bar. This is either a
-    // flattened ComponentName or the class name of a system class implementing
-    // a supported accessibility feature.
-    optional SettingProto accessibility_button_target_component = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto touch_exploration_enabled = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // List of the enabled accessibility providers.
-    optional SettingProto enabled_accessibility_services = 44;
-    // List of the accessibility services to which the user has granted
-    // permission to put the device into touch exploration mode.
-    optional SettingProto touch_exploration_granted_accessibility_services = 45;
-    // Uri of the slice that's presented on the keyguard. Defaults to a slice
-    // with the date and next alarm.
-    optional SettingProto keyguard_slice_uri = 46;
-    // Whether to speak passwords while in accessibility mode.
-    optional SettingProto accessibility_speak_password = 47 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_high_text_contrast_enabled = 48 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_display_magnification_enabled = 49 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_display_magnification_navbar_enabled = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_display_magnification_scale = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_soft_keyboard_mode = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_enabled = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_locale = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_preset = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_background_color = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_foreground_color = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_edge_type = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_edge_color = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_window_color = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_typeface = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_captioning_font_scale = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_display_inversion_enabled = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_display_daltonizer_enabled = 64 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Integer property that specifies the type of color space adjustment to perform.
-    optional SettingProto accessibility_display_daltonizer = 65 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_autoclick_enabled = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_autoclick_delay = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accessibility_large_pointer_icon = 68 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto long_press_timeout = 69 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto multi_press_timeout = 70 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto enabled_print_services = 71;
-    optional SettingProto disabled_print_services = 72;
-    optional SettingProto display_density_forced = 73 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tts_default_rate = 74 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tts_default_pitch = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tts_default_synth = 76 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tts_default_locale = 77 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tts_enabled_plugins = 78;
-    optional SettingProto connectivity_release_pending_intent_delay_ms = 79 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto allowed_geolocation_origins = 80;
-    optional SettingProto preferred_tty_mode = 81 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto enhanced_voice_privacy_enabled = 82 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tty_mode_enabled = 83 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto backup_enabled = 84 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto backup_auto_restore = 85 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto backup_provisioned = 86 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto backup_transport = 87 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto last_setup_shown = 88 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_global_search_activity = 89 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_num_promoted_sources = 90 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_max_results_to_display = 91 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_max_results_per_source = 92 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_web_results_override_limit = 93 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_promoted_source_deadline_millis = 94 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_source_timeout_millis = 95 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_prefill_millis = 96 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_max_stat_age_millis = 97 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_max_source_event_age_millis = 98 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_min_impressions_for_source_ranking = 99 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_min_clicks_for_source_ranking = 100 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_max_shortcuts_returned = 101 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_query_thread_core_pool_size = 102 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_query_thread_max_pool_size = 103 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_shortcut_refresh_core_pool_size = 104 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_shortcut_refresh_max_pool_size = 105 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_thread_keepalive_seconds = 106 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto search_per_source_concurrent_query_limit = 107 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Whether or not alert sounds are played on StorageManagerService events.
-    optional SettingProto mount_play_notification_snd = 108 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto mount_ums_autostart = 109 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto mount_ums_prompt = 110 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto mount_ums_notify_enabled = 111 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto anr_show_background = 112 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_first_crash_dialog_dev_option = 113 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The ComponentName string of the service to be used as the voice
-    // recognition service.
-    optional SettingProto voice_recognition_service = 114 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto package_verifier_user_consent = 115 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto selected_spell_checker = 116 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto selected_spell_checker_subtype = 117 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto spell_checker_enabled = 118 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto incall_power_button_behavior = 119 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto incall_back_button_behavior = 120 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto wake_gesture_enabled = 121 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto doze_enabled = 122 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto doze_always_on = 123 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto doze_pulse_on_pick_up = 124 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto doze_pulse_on_long_press = 125 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto doze_pulse_on_double_tap = 126 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto ui_night_mode = 127 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screensaver_enabled = 128 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screensaver_components = 129 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screensaver_activate_on_dock = 130 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screensaver_activate_on_sleep = 131 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screensaver_default_component = 132 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto nfc_payment_default_component = 133 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto nfc_payment_foreground = 134 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sms_default_application = 135 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto dialer_default_application = 136 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto emergency_assistance_application = 137 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_structure_enabled = 138 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_screenshot_enabled = 139 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_disclosure_enabled = 140 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_rotation_suggestions = 141 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto num_rotation_suggestions_accepted = 142 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Read only list of the service components that the current user has
-    // explicitly allowed to see and assist with all of the user's
-    // notifications.
-    optional SettingProto enabled_notification_assistant = 143 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto enabled_notification_listeners = 144 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto enabled_notification_policy_access_packages = 145 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Defines whether managed profile ringtones should be synced from its
-    // parent profile.
-    optional SettingProto sync_parent_sounds = 146 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto immersive_mode_confirmations = 147 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The query URI to find a print service to install.
-    optional SettingProto print_service_search_uri = 148 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The query URI to find an NFC service to install.
-    optional SettingProto payment_service_search_uri = 149 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The query URI to find an auto fill service to install.
-    optional SettingProto autofill_service_search_uri = 150 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto skip_first_use_hints = 151 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto unsafe_volume_music_active_ms = 152 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_screen_show_notifications = 153 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tv_input_hidden_inputs = 154;
-    optional SettingProto tv_input_custom_labels = 155;
-    optional SettingProto usb_audio_automatic_routing_disabled = 156 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sleep_timeout = 157 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto double_tap_to_wake = 158 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // The current assistant component. It could be a voice interaction service,
-    // or an activity that handles ACTION_ASSIST, or empty, which means using
-    // the default handling.
-    optional SettingProto assistant = 159 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto camera_gesture_disabled = 160 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto camera_double_tap_power_gesture_disabled = 161 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto camera_double_twist_to_flip_enabled = 162 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto camera_lift_trigger_enabled = 163 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_gesture_enabled = 164 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_gesture_sensitivity = 165 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_gesture_silence_alerts_enabled = 166 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_gesture_wake_enabled = 167 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto assist_gesture_setup_complete = 168 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_activated = 169 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_auto_mode = 170 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_color_temperature = 171 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_custom_start_time = 172 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_custom_end_time = 173 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto night_display_last_activated_time = 174 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto enabled_vr_listeners = 175 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vr_display_mode = 176 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto carrier_apps_handled = 177 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto managed_profile_contact_remote_search = 178 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto automatic_storage_manager_enabled = 179 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto automatic_storage_manager_days_to_retain = 180 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto automatic_storage_manager_bytes_cleared = 181 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto automatic_storage_manager_last_run = 182 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto automatic_storage_manager_turned_off_by_policy = 183 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto system_navigation_keys_enabled = 184 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Holds comma-separated list of ordering of QuickSettings tiles.
-    optional SettingProto qs_tiles = 185 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto instant_apps_enabled = 186 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto device_paired = 187 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto package_verifier_state = 188 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto cmas_additional_broadcast_pkg = 189 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto notification_badging = 190 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto qs_auto_added_tiles = 191 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lockdown_in_power_menu = 192 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto backup_manager_constants = 193;
-    optional SettingProto backup_local_transport_parameters = 194;
-    optional SettingProto bluetooth_on_while_driving = 195 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingsProto volume_hush_gesture = 196 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Please insert fields in the same order as in
-    // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 197
-}
-
-message SystemSettingsProto {
-    option (android.msg_privacy).dest = DEST_EXPLICIT;
-
-    repeated SettingsOperationProto historical_operations = 1;
-
-    optional SettingProto end_button_behavior = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto advanced_settings = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto bluetooth_discoverability = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto bluetooth_discoverability_timeout = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto font_scale = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto system_locales = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto display_color_mode = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_off_timeout = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness_for_vr = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_brightness_mode = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto screen_auto_brightness_adj = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Determines which streams are affected by ringer mode changes. The stream
-    // type's bit will be set to 1 if it should be muted when going into an
-    // inaudible ringer mode.
-    optional SettingProto mode_ringer_streams_affected = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto mute_streams_affected = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_on = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_input_devices = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto notification_vibration_intensity = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto haptic_feedback_intensity = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_ring = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_system = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_voice = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_music = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_alarm = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_notification = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_bluetooth_sco = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_accessibility = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto volume_master = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto master_mono = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Whether silent mode should allow vibration feedback. This is used
-    // internally in AudioService and the Sound settings activity to coordinate
-    // decoupling of vibrate and silent modes. This setting will likely be
-    // removed in a future release with support for audio/vibe feedback
-    // profiles.
-    // Not used anymore. On devices with vibrator, the user explicitly selects
-    // silent or vibrate mode. Kept for use by legacy database upgrade code in
-    // DatabaseHelper.
-    optional SettingProto vibrate_in_silent = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Appended to various volume related settings to record the previous values
-    // before the settings were affected by a silent/vibrate ringer mode change.
-    optional SettingProto append_for_last_audible = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto ringtone = 32;
-    optional SettingProto ringtone_cache = 33;
-    optional SettingProto notification_sound = 34;
-    optional SettingProto notification_sound_cache = 35;
-    optional SettingProto alarm_alert = 36;
-    optional SettingProto alarm_alert_cache = 37;
-    optional SettingProto media_button_receiver = 38;
-    optional SettingProto text_auto_replace = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_auto_caps = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_auto_punctuate = 41 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto text_show_password = 42 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_gtalk_service_status = 43 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto time_12_24 = 44 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto date_format = 45 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto setup_wizard_has_run = 46 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto accelerometer_rotation = 47 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto user_rotation = 48 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto hide_rotation_lock_toggle_for_accessibility = 49 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto vibrate_when_ringing = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto dtmf_tone_when_dialing = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto dtmf_tone_type_when_dialing = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto hearing_aid = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto tty_mode = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // User-selected RTT mode. When on, outgoing and incoming calls will be
-    // answered as RTT calls when supported by the device and carrier. Boolean
-    // value.
-    optional SettingProto rtt_calling_mode = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sound_effects_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto haptic_feedback_enabled = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto notification_light_pulse = 58 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Show pointer location on screen? 0 = no, 1 = yes.
-    optional SettingProto pointer_location = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_touches = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    // Log raw orientation data from {@link
-    // com.android.server.policy.WindowOrientationListener} for use with the
-    // orientationplot.py tool.
-    // 0 = no, 1 = yes
-    optional SettingProto window_orientation_listener_log = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lockscreen_sounds_enabled = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lockscreen_disabled = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_receive_calls = 64 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_call_options = 65 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_always = 66 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto sip_address_only = 67 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto pointer_speed = 68 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto lock_to_app_enabled = 69 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto egg_mode = 70 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto show_battery_percent = 71 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    optional SettingProto when_to_make_wifi_calls = 72 [ (android.privacy).dest = DEST_AUTOMATIC ];
-
-    // Please insert fields in the same order as in
-    // frameworks/base/core/java/android/provider/Settings.java.
-    // Next tag = 73;
-}
-
 message SettingsProto {
     // Enum values gotten from Settings.java
     enum ScreenBrightnessMode {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index c7de947..b5303c8 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -672,8 +672,10 @@
         option (android.msg_privacy).dest = DEST_LOCAL;
 
         // The requested Private DNS mode and an accompanying specifier.
-        optional SettingProto dns_mode = 1;
-        optional SettingProto dns_specifier = 2;
+        // msg_privacy settings don't apply to sub messages, only to primitive
+        // fields, so these must also be explicitly set to LOCAL.
+        optional SettingProto dns_mode = 1 [ (android.privacy).dest = DEST_LOCAL ];
+        optional SettingProto dns_specifier = 2 [ (android.privacy).dest = DEST_LOCAL ];
     }
     optional Private private = 96;
 
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
new file mode 100644
index 0000000..593747d
--- /dev/null
+++ b/core/proto/android/providers/settings/secure.proto
@@ -0,0 +1,478 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.providers.settings;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/providers/settings/common.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
+message SecureSettingsProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    repeated SettingsOperationProto historical_operations = 1;
+
+    message Accessibility {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // List of the enabled accessibility providers.
+        optional SettingProto enabled_accessibility_services = 2;
+        optional SettingProto autoclick_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto autoclick_delay = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Setting specifying the accessibility service or feature to be toggled via
+        // the accessibility button in the navigation bar. This is either a
+        // flattened ComponentName or the class name of a system class implementing
+        // a supported accessibility feature.
+        optional SettingProto button_target_component = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_enabled = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_locale = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_preset = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_background_color = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_foreground_color = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_edge_type = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_edge_color = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_window_color = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_typeface = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto captioning_font_scale = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto display_daltonizer_enabled = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Integer property that specifies the type of color space adjustment to perform.
+        optional SettingProto display_daltonizer = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto display_inversion_enabled = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto display_magnification_enabled = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto display_magnification_navbar_enabled = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto display_magnification_scale = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto high_text_contrast_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto large_pointer_icon = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_enabled = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_on_lock_screen = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_dialog_shown = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_target_service = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto soft_keyboard_mode = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Whether to speak passwords while in accessibility mode.
+        optional SettingProto speak_password = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto touch_exploration_enabled = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // List of the accessibility services to which the user has granted
+        // permission to put the device into touch exploration mode.
+        optional SettingProto touch_exploration_granted_accessibility_services = 31;
+    }
+    optional Accessibility accessibility = 2;
+
+    // Origins for which browsers should allow geolocation by default.
+    // The value is a space-separated list of origins.
+    optional SettingProto allowed_geolocation_origins = 3;
+
+    message AlwaysOnVpn {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto app = 1;
+        optional SettingProto lockdown = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional AlwaysOnVpn always_on_vpn = 4;
+
+    optional SettingProto android_id = 5;
+    optional SettingProto anr_show_background = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Assist {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // The current assistant component. It could be a voice interaction service,
+        // or an activity that handles ACTION_ASSIST, or empty, which means using
+        // the default handling.
+        optional SettingProto assistant = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto structure_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto screenshot_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto disclosure_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gesture_enabled = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gesture_sensitivity = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gesture_silence_alerts_enabled = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gesture_wake_enabled = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto gesture_setup_complete = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Assist assist = 7;
+
+    message Autofill {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // The currently selected autofill service flattened ComponentName.
+        optional SettingProto service = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Boolean indicating if Autofill supports field classification.
+        optional SettingProto feature_field_classification = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto user_data_max_user_data_size = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto user_data_max_field_classification_ids_size = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto user_data_max_category_count = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto user_data_max_value_length = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto user_data_min_value_length = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // The query URI to find an auto fill service to install.
+        optional SettingProto service_search_uri = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Autofill autofill = 8;
+
+    message AutomaticStorageManager {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto days_to_retain = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto bytes_cleared = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto last_run = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto turned_off_by_policy = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional AutomaticStorageManager automatic_storage_manager = 9;
+
+    message Backup {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_restore = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto provisioned = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto transport = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto manager_constants = 5;
+        optional SettingProto local_transport_parameters = 6;
+        optional SettingProto packages_to_clear_data_before_full_restore = 7;
+    }
+    optional Backup backup = 10;
+
+    optional SettingProto bluetooth_on_while_driving = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Camera {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto gesture_disabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto double_tap_power_gesture_disabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto double_twist_to_flip_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto lift_trigger_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Camera camera = 12;
+
+    optional SettingProto carrier_apps_handled = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto cmas_additional_broadcast_pkg = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    repeated SettingProto completed_categories = 15;
+    optional SettingProto connectivity_release_pending_intent_delay_ms = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto device_paired = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto dialer_default_application = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto display_density_forced = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto double_tap_to_wake = 20 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Doze {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto always_on = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pulse_on_pick_up = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pulse_on_long_press = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pulse_on_double_tap = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Doze doze = 21;
+
+    optional SettingProto emergency_assistance_application = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto immersive_mode_confirmations = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Incall {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto power_button_behavior = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto back_button_behavior = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Incall incall = 25;
+
+    message InputMethods {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto default_input_method = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto disabled_system_input_methods = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto enabled_input_methods = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto method_selector_visibility = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto subtype_history = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto selected_input_method_subtype = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_ime_with_hard_keyboard = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional InputMethods input_methods = 26;
+
+    optional SettingProto install_non_market_apps = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto instant_apps_enabled = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    // Uri of the slice that's presented on the keyguard. Defaults to a slice
+    // with the date and next alarm.
+    optional SettingProto keyguard_slice_uri = 29;
+    optional SettingProto last_setup_shown = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Location {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // The degree of location access enabled by the user.
+        optional SettingProto mode = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // The App or module that changes the location mode.
+        optional SettingProto changer = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Location location = 31;
+
+    message LockScreen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto lock_after_timeout = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto allow_private_notifications = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto allow_remote_input = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_notifications = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional LockScreen lock_screen = 32;
+
+    // Whether lock-to-app will lock the keyguard when exiting.
+    optional SettingProto lock_to_app_exit_locked = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto lockdown_in_power_menu = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto long_press_timeout = 35 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message ManagedProfile {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto contact_remote_search = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional ManagedProfile managed_profile = 36;
+
+    message Mount {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Whether or not alert sounds are played on StorageManagerService events.
+        optional SettingProto play_notification_snd = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto ums_autostart = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto ums_prompt = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto ums_notify_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Mount mount = 37;
+
+    optional SettingProto multi_press_timeout = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message NfcPayment {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto default_component = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Whether NFC payment is handled by the foreground application or a default.
+        optional SettingProto foreground = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // The query URI to find an NFC service to install.
+        optional SettingProto payment_service_search_uri = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional NfcPayment nfc_payment = 39;
+
+    message NightDisplay {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto activated = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_mode = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto color_temperature = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto custom_start_time = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto custom_end_time = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto last_activated_time = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional NightDisplay night_display = 40;
+
+    message Notification {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Read only list of the service components that the current user has
+        // explicitly allowed to see and assist with all of the user's
+        // notifications.
+        // "enabled_notification_assistant" in code.
+        optional SettingProto enabled_assistant = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto enabled_listeners = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto enabled_policy_access_packages = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto badging = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_note_about_notification_hiding = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Notification notification = 41;
+
+    message PackageVerifier {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto user_consent = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto state = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional PackageVerifier package_verifier = 42;
+
+    message ParentalControl {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto last_update = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto redirect_url = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional ParentalControl parental_control = 43;
+
+    message PrintService {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // The query URI to find a print service to install.
+        optional SettingProto search_uri = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto disabled_print_services = 2;
+        optional SettingProto enabled_print_services = 3;
+    }
+    optional PrintService print_service = 44;
+
+    message QuickSettings {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Holds comma-separated list of ordering of QuickSettings tiles.
+        optional SettingProto tiles = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_added_tiles = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional QuickSettings qs = 45;
+
+    message Rotation {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto show_rotation_suggestions = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto num_rotation_suggestions_accepted = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Rotation rotation = 46;
+
+    message Screensaver {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto components = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto activate_on_dock = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto activate_on_sleep = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto default_component = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Screensaver screensaver = 47;
+
+    message Search {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto global_search_activity = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto num_promoted_sources = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto max_results_to_display = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto max_results_per_source = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto web_results_override_limit = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto promoted_source_deadline_millis = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto source_timeout_millis = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto prefill_millis = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto max_stat_age_millis = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto max_source_event_age_millis = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto min_impressions_for_source_ranking = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto min_clicks_for_source_ranking = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto max_shortcuts_returned = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto query_thread_core_pool_size = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto query_thread_max_pool_size = 15 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_refresh_core_pool_size = 16 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto shortcut_refresh_max_pool_size = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto thread_keepalive_seconds = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto per_source_concurrent_query_limit = 19 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Search search = 48;
+
+    message SpellChecker {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // "selected_spell_checker" in code.
+        optional SettingProto selected = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // "selected_spell_checker_subtype" in code.
+        optional SettingProto selected_subtype = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional SpellChecker spell_checker = 49;
+
+    optional SettingProto settings_classname = 50 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_first_crash_dialog_dev_option = 51 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto skip_first_use_hints = 52 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto sleep_timeout = 53 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto sms_default_application = 54 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    // Defines whether managed profile ringtones should be synced from its
+    // parent profile.
+    optional SettingProto sync_parent_sounds = 55 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto system_navigation_keys_enabled = 56 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Tts {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto default_rate = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto default_pitch = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto default_synth = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto default_locale = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto enabled_plugins = 5;
+    }
+    optional Tts tts = 58;
+
+    message Tty {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto tty_mode_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // The preferred TTY mode:
+        // 0 = TTy Off, CDMA default
+        // 1 = TTY Full
+        // 2 = TTY HCO
+        // 3 = TTY VCO
+        optional SettingProto preferred_tty_mode = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Tty tty = 59;
+
+    message Tv {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Whether the current user has been set up via setup wizard (0 = false,
+        // 1 = true). This value differs from USER_SETUP_COMPLETE in that it can be
+        // reset back to 0 in case SetupWizard has been re-enabled on TV devices.
+        optional SettingProto user_setup_complete = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto input_hidden_inputs = 2;
+        optional SettingProto input_custom_labels = 3;
+    }
+    optional Tv tv = 60;
+
+    optional SettingProto ui_night_mode = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto unknown_sources_default_reversed = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto usb_audio_automatic_routing_disabled = 63 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    // Whether the current user has been set up via setup wizard (0 = false, 1 = true)
+    optional SettingProto user_setup_complete = 64 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Voice {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // The currently selected voice interaction service flattened ComponentName.
+        optional SettingProto interaction_service = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // The ComponentName string of the service to be used as the voice
+        // recognition service.
+        optional SettingProto recognition_service = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Voice voice = 65;
+
+    message Volume {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // What behavior should be invoked when the volume hush gesture is triggered
+        // One of VOLUME_HUSH_OFF, VOLUME_HUSH_VIBRATE, VOLUME_HUSH_MUTE.
+        optional SettingProto hush_gesture = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Persisted playback time after a user confirmation of an unsafe volume level.
+        optional SettingProto unsafe_volume_music_active_ms = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Volume volume = 66;
+
+    message Vr {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto display_mode = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto enabled_listeners = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Vr vr = 67;
+
+    optional SettingProto wake_gesture_enabled = 68 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    // Please insert fields in alphabetical order and group them into messages
+    // if possible (to avoid reaching the method limit).
+    // Next tag = 69;
+}
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
new file mode 100644
index 0000000..6b6edd2
--- /dev/null
+++ b/core/proto/android/providers/settings/system.proto
@@ -0,0 +1,246 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.providers.settings;
+
+option java_multiple_files = true;
+
+import "frameworks/base/core/proto/android/providers/settings/common.proto";
+import "frameworks/base/libs/incident/proto/android/privacy.proto";
+
+// Note: it's a conscious decision to add each setting as a separate field. This
+// allows annotating each setting with its own privacy tag.
+message SystemSettingsProto {
+    option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+    repeated SettingsOperationProto historical_operations = 1;
+
+    optional SettingProto advanced_settings = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Alarm {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "alarm_alert" in code.
+        optional SettingProto default_uri = 1;
+        optional SettingProto alert_cache = 2;
+    }
+    optional Alarm alarm = 3;
+
+    message Bluetooth {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Whether remote devices may discover and/or connect to this device:
+        // 2 -- discoverable and connectable
+        // 1 -- connectable but not discoverable
+        // 0 -- neither connectable nor discoverable
+        optional SettingProto discoverability = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto discoverability_timeout_secs = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Bluetooth bluetooth = 4;
+
+    optional SettingProto date_format = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto display_color_mode = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message DevOptions {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Show pointer location on screen? 0 = no, 1 = yes. "pointer_location
+        // in code.
+        optional SettingProto pointer_location = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_touches = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Log raw orientation data from {@link
+        // com.android.server.policy.WindowOrientationListener} for use with the
+        // orientationplot.py tool.
+        // 0 = no, 1 = yes
+        optional SettingProto window_orientation_listener_log = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional DevOptions developer_options = 7;
+
+    message DtmfTone {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "dtmf_tone_when_dialing" in code.
+        optional SettingProto play_when_dialing = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // "dtmf_tone_type_when_dialing" in code.
+        optional SettingProto type_played_when_dialing = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional DtmfTone dtmf_tone = 8;
+
+    optional SettingProto egg_mode = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto end_button_behavior = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto font_scale = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message HapticFeedback {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto intensity = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional HapticFeedback haptic_feedback = 12;
+
+    // Whether the hearing aid is enabled. The value is boolean (1 or 0).
+    optional SettingProto hearing_aid = 13 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto lock_to_app_enabled = 14 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Lockscreen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto sounds_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto disabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Lockscreen lockscreen = 15;
+
+    // The system default media button event receiver.
+    optional SettingProto media_button_receiver = 16;
+
+    message Notification {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto sound = 1;
+        optional SettingProto sound_cache = 2;
+        optional SettingProto light_pulse = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto vibration_intensity = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Notification notification = 17;
+
+    optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Ringtone {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // "ringtone" in code. The system-wide default ringtone URI.
+        optional SettingProto default_uri = 1;
+        optional SettingProto cache = 2;
+    }
+    optional Ringtone ringtone = 19;
+
+    message Rotation {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        // Control whether the accelerometer will be used to change screen
+        // orientation.  If 0, it will not be used unless explicitly requested
+        // by the application; if 1, it will be used by default unless
+        // explicitly disabled by the application.
+        optional SettingProto accelerometer_rotation = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Default screen rotation when no other policy applies.
+        // When accelerometer_rotation is zero and no on-screen Activity expresses a
+        // preference, this rotation value will be used. Must be one of the
+        // {@link android.view.Surface#ROTATION_0 Surface rotation constants}.
+        optional SettingProto user_rotation = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Control whether the rotation lock toggle in the System UI should be hidden.
+        // Typically this is done for accessibility purposes to make it harder for
+        // the user to accidentally toggle the rotation lock while the display rotation
+        // has been locked for accessibility.
+        // If 0, then rotation lock toggle is not hidden for accessibility (although it may be
+        // unavailable for other reasons).  If 1, then the rotation lock toggle is hidden.
+        optional SettingProto hide_rotation_lock_toggle_for_accessibility = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Rotation rotation = 20;
+
+    // User-selected RTT mode. When on, outgoing and incoming calls will be
+    // answered as RTT calls when supported by the device and carrier. Boolean
+    // value.
+    optional SettingProto rtt_calling_mode = 21 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Screen {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto off_timeout = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_for_vr = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto brightness_mode = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_brightness_adj = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Screen screen = 22;
+
+    optional SettingProto setup_wizard_has_run = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_battery_percent = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto show_gtalk_service_status = 25 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Sip {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto receive_calls = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto call_options = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto always = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto address_only = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Sip sip = 26;
+
+    optional SettingProto sound_effects_enabled = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto system_locales = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Text {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto auto_replace = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_caps = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto auto_punctuate = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto show_password = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Text text = 29;
+
+    optional SettingProto time_12_24 = 30 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    optional SettingProto tty_mode = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    message Vibrate {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto on = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto input_devices = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Whether silent mode should allow vibration feedback. This is used
+        // internally in AudioService and the Sound settings activity to coordinate
+        // decoupling of vibrate and silent modes. This setting will likely be
+        // removed in a future release with support for audio/vibe feedback
+        // profiles.
+        // Not used anymore. On devices with vibrator, the user explicitly selects
+        // silent or vibrate mode. Kept for use by legacy database upgrade code in
+        // DatabaseHelper.
+        optional SettingProto in_silent = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto when_ringing = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Vibrate vibrate = 32;
+
+    message Volume {
+        option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+        optional SettingProto ring = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto system = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto voice = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto music = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto alarm = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto notification = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto bluetooth_sco = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto master = 9 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto master_mono = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Determines which streams are affected by ringer mode changes. The stream
+        // type's bit will be set to 1 if it should be muted when going into an
+        // inaudible ringer mode.
+        optional SettingProto mode_ringer_streams_affected = 11 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Which streams are affected by mute. The stream type's bit should be set
+        // to 1 if it should be muted when a mute request is received.
+        optional SettingProto mute_streams_affected = 12 [ (android.privacy).dest = DEST_AUTOMATIC ];
+    }
+    optional Volume volume = 33;
+
+    optional SettingProto when_to_make_wifi_calls = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+    // Please insert fields in alphabetical order and group them into messages
+    // if possible (to avoid reaching the method limit).
+    // Next tag = 35;
+}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 8d4b56c..4df3b63 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -527,9 +527,7 @@
     optional int32 calling_uid = 1;
     // Job IDs can technically be negative.
     optional int32 job_id = 2;
-    optional string battery_name = 3 [
-        (.android.privacy).dest = DEST_EXPLICIT
-    ];
+    optional string battery_name = 3;
 }
 
 // Dump from a com.android.server.job.controllers.JobStatus object.
@@ -578,6 +576,12 @@
 
         optional .android.os.PersistableBundleProto extras = 14;
         optional .android.os.BundleProto transient_extras = 15;
+        // ClipData of information that is returned to the application at
+        // execution time, but not persisted by the system. This is provided by
+        // the app and the main purpose of providing a ClipData is to allow
+        // granting of URI permissions for data associated with the clip.  The
+        // exact kind of permission grant to perform is specified in the flags
+        // field.
         optional .android.content.ClipDataProto clip_data = 16;
 
         optional GrantedUriPermissionsDumpProto granted_uri_permissions = 17;
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 063135d..a8d0825 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -156,6 +156,8 @@
     TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21;
     TRANSIT_KEYGUARD_OCCLUDE = 22;
     TRANSIT_KEYGUARD_UNOCCLUDE = 23;
+    TRANSIT_TRANSLUCENT_ACTIVITY_OPEN = 24;
+    TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE = 25;
   }
   optional TransitionType last_used_app_transition = 2;
 }
diff --git a/core/proto/android/service/usb.proto b/core/proto/android/service/usb.proto
index b60c569..8240d8a 100644
--- a/core/proto/android/service/usb.proto
+++ b/core/proto/android/service/usb.proto
@@ -80,7 +80,7 @@
     optional string model = 2;
     optional string description = 3;
     optional string version = 4;
-    optional string uri = 5;
+    optional string uri = 5 [ (android.privacy).dest = DEST_EXPLICIT ];
     optional string serial = 6 [ (android.privacy).dest = DEST_EXPLICIT ];
 }
 
@@ -155,6 +155,7 @@
 message UsbConnectionRecordProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
+    // usb device's address, e.g. 001/002, nothing about the phone
     optional string device_address = 1;
     optional android.service.UsbConnectionRecordMode mode = 2;
     optional int64 timestamp = 3;
@@ -251,6 +252,7 @@
     optional string name = 3;
     optional bool has_playback = 4;
     optional bool has_capture = 5;
+    // usb device's address, e.g. 001/002, nothing about the phone
     optional string address = 6;
 }
 
@@ -259,6 +261,7 @@
 
     optional int32 card = 1;
     optional int32 device = 2;
+    // usb device's address, e.g. 001/002, nothing about the phone
     optional string device_address = 3;
 }
 
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
index cbd06fd..2a03050 100644
--- a/core/proto/android/view/displayinfo.proto
+++ b/core/proto/android/view/displayinfo.proto
@@ -29,5 +29,5 @@
   optional int32 logical_height = 2;
   optional int32 app_width = 3;
   optional int32 app_height = 4;
-  optional string name = 5;
+  optional string name = 5 [ (.android.privacy).dest = DEST_EXPLICIT ];
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c4d3667..f4715fc 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -89,6 +89,8 @@
     <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
     <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
     <protected-broadcast android:name="android.intent.action.USER_ACTIVITY_NOTIFICATION" />
+    <protected-broadcast android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
+    <protected-broadcast android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
 
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
@@ -1102,6 +1104,8 @@
 
     <!-- Allows an app to use fingerprint hardware.
          <p>Protection level: normal
+         @deprecated Applications should request {@link
+         android.Manifest.permission#USE_BIOMETRIC} instead
     -->
     <permission android:name="android.permission.USE_FINGERPRINT"
         android:permissionGroup="android.permission-group.SENSORS"
@@ -1109,6 +1113,15 @@
         android:description="@string/permdesc_useFingerprint"
         android:protectionLevel="normal" />
 
+    <!-- Allows an app to use device supported biometric modalities.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.USE_BIOMETRIC"
+        android:permissionGroup="android.permission-group.SENSORS"
+        android:label="@string/permlab_useBiometric"
+        android:description="@string/permdesc_useBiometric"
+        android:protectionLevel="normal" />
+
     <!-- ====================================================================== -->
     <!-- REMOVED PERMISSIONS                                                    -->
     <!-- ====================================================================== -->
@@ -2003,11 +2016,11 @@
     <permission android:name="android.permission.REMOVE_TASKS"
         android:protectionLevel="signature" />
 
-    <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+    <!-- @SystemApi @TestApi @hide Allows an application to create/manage/remove stacks -->
     <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
         android:protectionLevel="signature|privileged|development" />
 
-    <!-- @SystemApi @hide Allows an application to embed other activities -->
+    <!-- @SystemApi @TestApi @hide Allows an application to embed other activities -->
     <permission android:name="android.permission.ACTIVITY_EMBEDDING"
                 android:protectionLevel="signature|privileged|development" />
 
@@ -3108,6 +3121,12 @@
     <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_MODE"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to control the color saturation of the display.
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.CONTROL_DISPLAY_SATURATION"
+        android:protectionLevel="signature|privileged" />
+
     <!-- Allows an application to collect usage infomation about brightness slider changes.
          <p>Not for use by third-party applications.</p>
          @hide
@@ -4224,11 +4243,21 @@
                   android:exported="false">
         </receiver>
 
-        <receiver android:name="com.android.server.stats.StatsCompanionService$PollingAlarmReceiver"
+        <receiver android:name="com.android.server.stats.StatsCompanionService$PullingAlarmReceiver"
                   android:permission="android.permission.STATSCOMPANION"
                   android:exported="false">
         </receiver>
 
+        <receiver android:name="com.android.server.am.BatteryStatsService$UsbConnectionReceiver"
+                  android:exported="false">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.hardware.usb.action.USB_STATE" />
+            </intent-filter>
+        </receiver>
+
         <service android:name="android.hardware.location.GeofenceHardwareService"
             android:permission="android.permission.LOCATION_HARDWARE"
             android:exported="false" />
diff --git a/core/res/res/anim/activity_translucent_close_exit.xml b/core/res/res/anim/activity_translucent_close_exit.xml
new file mode 100644
index 0000000..04c5dea
--- /dev/null
+++ b/core/res/res/anim/activity_translucent_close_exit.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false"
+    android:zAdjustment="top">
+    <translate
+        android:fromYDelta="0%"
+        android:toYDelta="100%"
+        android:interpolator="@interpolator/fast_out_linear_in"
+        android:duration="300"/>
+</set>
diff --git a/core/res/res/anim/activity_translucent_open_enter.xml b/core/res/res/anim/activity_translucent_open_enter.xml
new file mode 100644
index 0000000..da3dded
--- /dev/null
+++ b/core/res/res/anim/activity_translucent_open_enter.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shareInterpolator="false">
+    <translate
+        android:fromYDelta="100%"
+        android:toYDelta="0"
+        android:interpolator="@interpolator/linear_out_slow_in"
+        android:duration="300"/>
+</set>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
new file mode 100644
index 0000000..048494a
--- /dev/null
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<view  xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
+    android:id="@+id/autofill_dataset_picker"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    style="@style/AutofillDatasetPicker">
+
+    <LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+      <LinearLayout
+          android:id="@+id/autofill_dataset_header"
+          android:visibility="gone"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:orientation="vertical"/>
+
+      <ListView
+          android:id="@+id/autofill_dataset_list"
+          android:layout_weight="1"
+          android:layout_width="fill_parent"
+          android:layout_height="0dp"
+          android:drawSelectorOnTop="true"
+          android:clickable="true"
+          android:divider="@null"
+          android:visibility="gone">
+      </ListView>
+
+      <LinearLayout
+          android:id="@+id/autofill_dataset_footer"
+          android:visibility="gone"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:orientation="vertical"/>
+
+    </LinearLayout>
+
+</view>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
new file mode 100644
index 0000000..24b14a0
--- /dev/null
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/autofill_dataset_picker"
+    style="@style/AutofillDatasetPicker"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/autofill_window_title"
+        android:layout_above="@+id/autofill_dataset_container"
+        android:layout_alignStart="@+id/autofill_dataset_container"
+        android:textSize="16sp"/>
+
+    <!-- autofill_container is the common parent for inserting authentication item or
+         autofill_dataset_list-->
+    <FrameLayout
+        android:id="@+id/autofill_dataset_container"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true">
+
+        <LinearLayout
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:orientation="vertical">
+
+            <LinearLayout
+                android:id="@+id/autofill_dataset_header"
+                android:visibility="gone"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"/>
+
+            <ListView
+                android:id="@+id/autofill_dataset_list"
+                android:layout_weight="1"
+                android:layout_width="fill_parent"
+                android:layout_height="0dp"
+                android:clickable="true"
+                android:divider="@null"
+                android:drawSelectorOnTop="true"
+                android:visibility="gone"/>
+
+            <LinearLayout
+                android:id="@+id/autofill_dataset_footer"
+                android:visibility="gone"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"/>
+
+        </LinearLayout>
+
+    </FrameLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3135455..aceba08 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2123,44 +2123,48 @@
         <!-- Controls how the window is laid out if there is a {@code DisplayCutout}.
         <p>
         Defaults to {@code default}.
-
-        @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
-        @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
-        @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
-        @see android.view.DisplayCutout
-        @see android.R.attr#layoutInDisplayCutoutMode -->
+        <p>
+        See also
+        {@link android.view.WindowManager.LayoutParams#layoutInDisplayCutoutMode
+                WindowManager.LayoutParams.layoutInDisplayCutoutMode},
+        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT},
+        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES},
+        {@link android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER},
+        and {@link android.view.DisplayCutout DisplayCutout}
+        -->
         <attr name="windowLayoutInDisplayCutoutMode">
-            <!-- The window is allowed to extend into the {@code DisplayCutout} area, only if the
-            {@code DisplayCutout} is fully contained within a system bar. Otherwise, the window is
-            laid out such that it does not overlap with the {@code DisplayCutout} area.
-
-            @see android.view.DisplayCutout
-            @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
+            <!-- <p>
+            The window is allowed to extend into the <code>DisplayCutout</code> area, only if
+            the <code>DisplayCutout</code> is fully contained within a system bar. Otherwise, the
+            window is laid out such that it does not overlap with the <code>DisplayCutout</code>
+            area.
+            <p>
+            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT</code>.
             -->
             <enum name="default" value="0" />
-            <!--
-            The window is always allowed to extend into the {@code DisplayCutout} areas on the short
-            edges of the screen even if fullscreen or in landscape.
-            The window will never extend into a {@link DisplayCutout} area on the long edges of the
-            screen.
+            <!-- <p>
+            The window is always allowed to extend into the <code>DisplayCutout</code> areas on the
+            short edges of the screen even if fullscreen or in landscape.
+            The window will never extend into a <code>DisplayCutout</code> area on the long edges of
+            the screen.
             <p>
             The window must make sure that no important content overlaps with the
-            {@link DisplayCutout}.
-
-            @see android.view.DisplayCutout
-            @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+            <code>DisplayCutout</code>.
+            <p>
+            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES</code>.
             -->
             <enum name="shortEdges" value="1" />
-            <!-- Use {@code shortEdges} instead. This is temporarily here to unblock pushing the SDK
-                 until all usages have been migrated to {@code shortEdges} -->
+            <!-- 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" />
-            <!-- The window is never allowed to overlap with the DisplayCutout area.
+            <!-- <p>
+            The window is never allowed to overlap with the <code>DisplayCutout</code> area.
             <p>
-            This should be used with windows that transiently set {@code SYSTEM_UI_FLAG_FULLSCREEN}
-            to avoid a relayout of the window when the flag is set or cleared.
-
-            @see android.view.DisplayCutout
-            @see android.view.WindowManager.LayoutParams#LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
+            This should be used with windows that transiently set
+            <code>SYSTEM_UI_FLAG_FULLSCREEN</code> to avoid a relayout of the window when the
+            flag is set or cleared.
+            <p>
+            Corresponds to <code>LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER</code>.
             -->
             <enum name="never" value="2" />
         </attr>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b5e223c..2a6b331 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -30,33 +30,32 @@
         <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item>
-        <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_location</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
     </string-array>
 
     <string translatable="false" name="status_bar_rotate">rotate</string>
@@ -1095,6 +1094,9 @@
     <!-- Display low battery warning when battery level dips to this value -->
     <integer name="config_lowBatteryWarningLevel">15</integer>
 
+    <!-- The default suggested battery % at which we enable battery saver automatically.  -->
+    <integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
+
     <!-- Close low battery warning when battery level reaches the lowBatteryWarningLevel
          plus this -->
     <integer name="config_lowBatteryCloseWarningBump">5</integer>
@@ -1340,30 +1342,6 @@
     <integer-array name="config_autoBrightnessKeyboardBacklightValues">
     </integer-array>
 
-    <!-- Array of light sensor lux values to define the minimum brightness curve, which guarantees
-         that any 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
-         disabling auto-brightness or reseting the brightness curve via ADB.
-
-         The control points must be strictly increasing. Each control point corresponds to an entry
-         in the minimum brightness nits array. -->
-    <integer-array name="config_autoBrightnessMinimumBrightnessCurveLux">
-        <item>2000</item>
-        <item>4000</item>
-    </integer-array>
-
-    <!-- Array of desired screen brightness in nits corresponding to the lux values
-         in the config_autoBrightnessMinimumBrightnessCurveLux array.
-
-         This array should have size one greater than the size of the
-         config_autoBrightnessMinimumBrightnessCurveLux array. The values must be non-negative and
-         non-decreasing. -->
-    <array name="config_autoBrightnessMinimumBrightnessCurveNits">
-        <item>1.0</item>
-        <item>50.0</item>
-        <item>90.0</item>
-    </array>
-
     <!-- Array of hysteresis constraint values for brightening, represented as tenths of a
          percent. The length of this array is assumed to be one greater than
          config_dynamicHysteresisLuxLevels. The brightening threshold is calculated as
@@ -2389,19 +2367,6 @@
         <item>com.android.inputmethod.latin</item>
     </string-array>
 
-    <!-- The list of carrier applications which should be disabled until used.
-         This function suppresses update notifications for these pre-installed apps.
-         In SubscriptionInfoUpdater, the listed applications are disabled until used when all of the
-         following conditions are met.
-         1. Not currently carrier-privileged according to the inserted SIM
-         2. Pre-installed
-         3. In the default state (enabled but not explicitly)
-         And SubscriptionInfoUpdater undoes this and marks the app enabled when a SIM is inserted
-         that marks the app as carrier privileged. It also grants the app default permissions
-         for Phone and Location. As such, apps MUST only ever be added to this list if they
-         obtain user consent to access their location through other means. -->
-    <string-array name="config_disabledUntilUsedPreinstalledCarrierApps" translatable="false" />
-
     <!-- The list of classes that should be added to the notification ranking pipeline.
      See {@link com.android.server.notification.NotificationSignalExtractor}
       If you add a new extractor to this list make sure to update
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 7ff96fa..0581856 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -643,6 +643,9 @@
     -->
     <dimen name="autofill_save_icon_max_size">300dp</dimen>
 
+    <!-- Maximum number of datasets that are visible in the UX picker without scrolling -->
+    <integer name="autofill_max_visible_datasets">3</integer>
+
     <!-- Size of a slice shortcut view -->
     <dimen name="slice_shortcut_size">56dp</dimen>
     <!-- Size of action icons in a slice -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2e8f663..49cd272 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1325,6 +1325,11 @@
       disables the keylock when receiving an incoming phone call, then
       re-enables the keylock when the call is finished.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=NONE] -->
+    <string name="permlab_useBiometric">use biometric hardware</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this.[CHAR_LIMIT=NONE] -->
+    <string name="permdesc_useBiometric">Allows the app to use biometric hardware for authentication</string>
+
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_manageFingerprint">manage fingerprint hardware</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2752,30 +2757,57 @@
     <!-- Label for item in the text selection menu to trigger an Email app [CHAR LIMIT=20] -->
     <string name="email">Email</string>
 
+    <!-- Accessibility description for an item in the text selection menu to trigger an Email app [CHAR LIMIT=NONE] -->
+    <string name="email_desc">Email selected address</string>
+
     <!-- Label for item in the text selection menu to trigger a Dialer app [CHAR LIMIT=20] -->
     <string name="dial">Call</string>
 
+    <!-- Accessibility description for an item in the text selection menu to call a phone number [CHAR LIMIT=NONE] -->
+    <string name="dial_desc">Call selected phone number</string>
+
     <!-- Label for item in the text selection menu to trigger a Map app [CHAR LIMIT=20] -->
     <string name="map">Locate</string>
 
+    <!-- Accessibility description for an item in the text selection menu to open maps for an address [CHAR LIMIT=NONE] -->
+    <string name="map_desc">Locale selected address</string>
+
     <!-- Label for item in the text selection menu to trigger a Browser app [CHAR LIMIT=20] -->
     <string name="browse">Open</string>
 
+    <!-- Accessibility description for an item in the text selection menu to open a URL in a browser [CHAR LIMIT=NONE] -->
+    <string name="browse_desc">Open selected URL</string>
+
     <!-- Label for item in the text selection menu to trigger an SMS app [CHAR LIMIT=20] -->
     <string name="sms">Message</string>
 
+    <!-- Accessibility description for an item in the text selection menu to send an SMS to a phone number [CHAR LIMIT=NONE] -->
+    <string name="sms_desc">Message selected phone number</string>
+
     <!-- Label for item in the text selection menu to trigger adding a contact [CHAR LIMIT=20] -->
     <string name="add_contact">Add</string>
 
+    <!-- Accessibility description for an item in the text selection menu to add the selected detail to contacts [CHAR LIMIT=NONE] -->
+    <string name="add_contact_desc">Add to contacts</string>
+
     <!-- Label for item in the text selection menu to view the calendar for the selected time/date [CHAR LIMIT=20] -->
     <string name="view_calendar">View</string>
 
+    <!-- Accessibility description for an item in the text selection menu to view the calendar for a date [CHAR LIMIT=NONE]-->
+    <string name="view_calendar_desc">View selected time in calendar</string>
+
     <!-- Label for item in the text selection menu to create a calendar event at the selected time/date [CHAR LIMIT=20] -->
     <string name="add_calendar_event">Schedule</string>
 
+    <!-- Accessibility description for an item in the text selection menu to schedule an event for a date [CHAR LIMIT=NONE] -->
+    <string name="add_calendar_event_desc">Schedule event for selected time</string>
+
     <!-- Label for item in the text selection menu to track a selected flight number [CHAR LIMIT=20] -->
     <string name="view_flight">Track</string>
 
+    <!-- Accessibility description for an item in the text selection menu to track a flight [CHAR LIMIT=NONE] -->
+    <string name="view_flight_desc">Track selected flight</string>
+
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the title of that notification. -->
     <string name="low_internal_storage_view_title">Storage space running out</string>
     <!-- If the device is getting low on internal storage, a notification is shown to the user.  This is the message of that notification. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5eeb418..cbd7b4d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -545,14 +545,23 @@
   <java-symbol type="string" name="undo" />
   <java-symbol type="string" name="redo" />
   <java-symbol type="string" name="email" />
+  <java-symbol type="string" name="email_desc" />
   <java-symbol type="string" name="dial" />
+  <java-symbol type="string" name="dial_desc" />
   <java-symbol type="string" name="map" />
+  <java-symbol type="string" name="map_desc" />
   <java-symbol type="string" name="browse" />
+  <java-symbol type="string" name="browse_desc" />
   <java-symbol type="string" name="sms" />
+  <java-symbol type="string" name="sms_desc" />
   <java-symbol type="string" name="add_contact" />
+  <java-symbol type="string" name="add_contact_desc" />
   <java-symbol type="string" name="view_calendar" />
+  <java-symbol type="string" name="view_calendar_desc" />
   <java-symbol type="string" name="add_calendar_event" />
+  <java-symbol type="string" name="add_calendar_event_desc" />
   <java-symbol type="string" name="view_flight" />
+  <java-symbol type="string" name="view_flight_desc" />
   <java-symbol type="string" name="textSelectionCABTitle" />
   <java-symbol type="string" name="BaMmi" />
   <java-symbol type="string" name="CLIRDefaultOffNextCallOff" />
@@ -1233,7 +1242,6 @@
   <java-symbol type="array" name="supported_locales" />
   <java-symbol type="array" name="config_cdma_dun_supported_types" />
   <java-symbol type="array" name="config_disabledUntilUsedPreinstalledImes" />
-  <java-symbol type="array" name="config_disabledUntilUsedPreinstalledCarrierApps" />
   <java-symbol type="array" name="config_callBarringMMI" />
   <java-symbol type="array" name="config_globalActionsList" />
   <java-symbol type="array" name="config_telephonyEuiccDeviceCapabilities" />
@@ -1611,6 +1619,8 @@
   <java-symbol type="anim" name="task_open_enter" />
   <java-symbol type="anim" name="cross_profile_apps_thumbnail_enter" />
   <java-symbol type="anim" name="task_open_enter_cross_profile_apps" />
+  <java-symbol type="anim" name="activity_translucent_open_enter" />
+  <java-symbol type="anim" name="activity_translucent_close_exit" />
 
   <java-symbol type="array" name="config_autoRotationTiltTolerance" />
   <java-symbol type="array" name="config_keyboardTapVibePattern" />
@@ -1794,8 +1804,6 @@
   <java-symbol type="array" name="config_autoBrightnessKeyboardBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLcdBacklightValues" />
   <java-symbol type="array" name="config_autoBrightnessLevels" />
-  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveLux" />
-  <java-symbol type="array" name="config_autoBrightnessMinimumBrightnessCurveNits" />
   <java-symbol type="array" name="config_dynamicHysteresisBrightLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisDarkLevels" />
   <java-symbol type="array" name="config_dynamicHysteresisLuxLevels" />
@@ -2724,6 +2732,10 @@
   <java-symbol type="string" name="status_bar_alarm_clock" />
   <java-symbol type="string" name="status_bar_secure" />
   <java-symbol type="string" name="status_bar_clock" />
+  <java-symbol type="string" name="status_bar_airplane" />
+  <java-symbol type="string" name="status_bar_mobile" />
+  <java-symbol type="string" name="status_bar_ethernet" />
+  <java-symbol type="string" name="status_bar_vpn" />
 
   <!-- Locale picker -->
   <java-symbol type="id" name="locale_search_menu" />
@@ -3021,10 +3033,14 @@
   <java-symbol type="layout" name="autofill_save"/>
   <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
+  <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
+  <java-symbol type="layout" name="autofill_dataset_picker_header_footer_fullscreen"/>
+  <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_dataset_container"/>
+  <java-symbol type="id" name="autofill_dataset_footer"/>
+  <java-symbol type="id" name="autofill_dataset_header"/>
   <java-symbol type="id" name="autofill_dataset_list"/>
   <java-symbol type="id" name="autofill_dataset_picker"/>
-  <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_save_custom_subtitle" />
   <java-symbol type="id" name="autofill_save_icon" />
   <java-symbol type="id" name="autofill_save_no" />
@@ -3054,6 +3070,7 @@
   <java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
   <java-symbol type="dimen" name="autofill_save_custom_subtitle_max_height"/>
   <java-symbol type="dimen" name="autofill_save_icon_max_size"/>
+  <java-symbol type="integer" name="autofill_max_visible_datasets" />
 
   <java-symbol type="dimen" name="notification_big_picture_max_height"/>
   <java-symbol type="dimen" name="notification_big_picture_max_width"/>
@@ -3314,4 +3331,6 @@
   <java-symbol type="string" name="notification_app_name_system" />
   <java-symbol type="string" name="notification_app_name_settings" />
 
+  <java-symbol type="integer" name="config_lowBatteryAutoTriggerDefaultLevel" />
+
 </resources>
diff --git a/core/tests/coretests/src/android/app/backup/FullBackupTest.java b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
index 58ee7a7..5db416b 100644
--- a/core/tests/coretests/src/android/app/backup/FullBackupTest.java
+++ b/core/tests/coretests/src/android/app/backup/FullBackupTest.java
@@ -16,9 +16,6 @@
 
 package android.app.backup;
 
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import android.app.backup.FullBackup.BackupScheme.PathWithRequiredFlags;
 import android.content.Context;
 import android.support.test.filters.LargeTest;
@@ -102,6 +99,28 @@
                 include.getRequiredFlags());
     }
 
+    public void testParseBackupSchemeFromXml_onlyIncludeRequireFakeEncryptionFlag()
+            throws Exception {
+        mXpp.setInput(new StringReader(
+                "<full-backup-content>"
+                        + "<include path=\"onlyInclude.txt\" domain=\"file\""
+                        + " requireFlags=\"fakeClientSideEncryption\"/>"
+                        + "</full-backup-content>"));
+
+        FullBackup.BackupScheme bs = FullBackup.getBackupSchemeForTest(mContext);
+        bs.parseBackupSchemeFromXmlLocked(mXpp, excludesSet, includeMap);
+
+        Set<PathWithRequiredFlags> fileDomainIncludes = includeMap.get(FullBackup.FILES_TREE_TOKEN);
+        assertEquals("Didn't find expected file domain include.", 1, fileDomainIncludes.size());
+        PathWithRequiredFlags include = fileDomainIncludes.iterator().next();
+        assertEquals("Invalid path parsed for <include/>",
+                new File(mContext.getFilesDir(), "onlyInclude.txt").getCanonicalPath(),
+                include.getPath());
+        assertEquals("Invalid requireFlags parsed for <include/>",
+                BackupAgent.FLAG_FAKE_CLIENT_SIDE_ENCRYPTION_ENABLED,
+                include.getRequiredFlags());
+    }
+
     public void testparseBackupSchemeFromXml_onlyIncludeRequireD2DFlag() throws Exception {
         mXpp.setInput(new StringReader(
                 "<full-backup-content>" +
diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java
index 82af662..20b298d 100644
--- a/core/tests/coretests/src/android/os/MemoryFileTest.java
+++ b/core/tests/coretests/src/android/os/MemoryFileTest.java
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.BufferOverflowException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -110,7 +111,7 @@
         try {
             os.write(new byte[] { -1, -1 });
             fail();
-        } catch (IndexOutOfBoundsException expected) {
+        } catch (IndexOutOfBoundsException | BufferOverflowException expected) {
         }
 
         byte[] copy = new byte[file.length()];
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 6ef773a..dfc99f6 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -267,6 +267,7 @@
                     Settings.Global.LOW_POWER_MODE,
                     Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX,
                     Settings.Global.LOW_POWER_MODE_STICKY,
+                    Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS,
                     Settings.Global.LTE_SERVICE_FORCED,
                     Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE,
                     Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY,
@@ -586,7 +587,9 @@
                  Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
                  Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
                  Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
-                 Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED);
+                 Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED,
+                 Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION,
+                 Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
 
     @Test
     public void systemSettingsBackedUpOrBlacklisted() {
diff --git a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
index b77982b..861a43a 100644
--- a/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/SelectionEventTest.java
@@ -33,8 +33,11 @@
     @Test
     public void testParcel() {
         final SelectionEvent[] captured = new SelectionEvent[1];
-        final Logger logger = new Logger(new Logger.Config(
-                InstrumentationRegistry.getTargetContext(), Logger.WIDGET_TEXTVIEW, null)) {
+        final Logger logger = new Logger(
+                new Logger.Config(
+                        InstrumentationRegistry.getTargetContext(),
+                        TextClassifier.WIDGET_TYPE_TEXTVIEW,
+                        null)) {
             @Override
             public void writeEvent(SelectionEvent event) {
                 captured[0] = event;
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 57db153..f96027d 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -126,11 +126,7 @@
 
         TextClassification classification = mClassifier.classifyText(
                 text, startIndex, endIndex, mClassificationOptions);
-        assertThat(classification,
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_EMAIL,
-                        "mailto:" + classifiedText));
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_EMAIL));
     }
 
     @Test
@@ -144,11 +140,7 @@
 
         TextClassification classification = mClassifier.classifyText(
                 text, startIndex, endIndex, mClassificationOptions);
-        assertThat(classification,
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_URL,
-                        "http://" + classifiedText));
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
     }
 
     @Test
@@ -158,11 +150,7 @@
         String text = "Brandschenkestrasse 110, Zürich, Switzerland";
         TextClassification classification = mClassifier.classifyText(
                 text, 0, text.length(), mClassificationOptions);
-        assertThat(classification,
-                isTextClassification(
-                        text,
-                        TextClassifier.TYPE_ADDRESS,
-                        "geo:0,0?q=Brandschenkestrasse+110%2C+Z%C3%BCrich%2C+Switzerland"));
+        assertThat(classification, isTextClassification(text, TextClassifier.TYPE_ADDRESS));
     }
 
     @Test
@@ -176,11 +164,7 @@
 
         TextClassification classification = mClassifier.classifyText(
                 text, startIndex, endIndex, mClassificationOptions);
-        assertThat(classification,
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_URL,
-                        "http://ANDROID.COM"));
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_URL));
     }
 
     @Test
@@ -194,11 +178,7 @@
 
         TextClassification classification = mClassifier.classifyText(
                 text, startIndex, endIndex, mClassificationOptions);
-        assertThat(classification,
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_DATE,
-                        null));
+        assertThat(classification, isTextClassification(classifiedText, TextClassifier.TYPE_DATE));
     }
 
     @Test
@@ -213,10 +193,7 @@
         TextClassification classification = mClassifier.classifyText(
                 text, startIndex, endIndex, mClassificationOptions);
         assertThat(classification,
-                isTextClassification(
-                        classifiedText,
-                        TextClassifier.TYPE_DATE_TIME,
-                        null));
+                isTextClassification(classifiedText, TextClassifier.TYPE_DATE_TIME));
     }
 
     @Test
@@ -355,39 +332,15 @@
     }
 
     private static Matcher<TextClassification> isTextClassification(
-            final String text, final String type, final String intentUri) {
+            final String text, final String type) {
         return new BaseMatcher<TextClassification>() {
             @Override
             public boolean matches(Object o) {
                 if (o instanceof TextClassification) {
                     TextClassification result = (TextClassification) o;
-                    final boolean typeRequirementSatisfied;
-                    String scheme;
-                    switch (type) {
-                        case TextClassifier.TYPE_EMAIL:
-                            scheme = result.getIntent().getData().getScheme();
-                            typeRequirementSatisfied = "mailto".equals(scheme);
-                            break;
-                        case TextClassifier.TYPE_URL:
-                            scheme = result.getIntent().getData().getScheme();
-                            typeRequirementSatisfied = "http".equals(scheme)
-                                    || "https".equals(scheme);
-                            break;
-                        case TextClassifier.TYPE_ADDRESS:
-                            scheme = result.getIntent().getData().getScheme();
-                            typeRequirementSatisfied = "geo".equals(scheme);
-                            break;
-                        default:
-                            typeRequirementSatisfied = true;
-                    }
-
-                    return typeRequirementSatisfied
-                            && text.equals(result.getText())
+                    return text.equals(result.getText())
                             && result.getEntityCount() > 0
-                            && type.equals(result.getEntity(0))
-                            && (intentUri == null
-                                || intentUri.equals(result.getIntent().getDataString()));
-                    // TODO: Include other properties.
+                            && type.equals(result.getEntity(0));
                 }
                 return false;
             }
@@ -395,8 +348,7 @@
             @Override
             public void describeTo(Description description) {
                 description.appendText("text=").appendValue(text)
-                        .appendText(", type=").appendValue(type)
-                        .appendText(", intent.data=").appendValue(intentUri);
+                        .appendText(", type=").appendValue(type);
             }
         };
     }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index ada19fc..5d58f55 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -17,15 +17,19 @@
 package android.view.textclassifier;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Context;
 import android.content.Intent;
-import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Icon;
 import android.os.LocaleList;
 import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
@@ -33,55 +37,53 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Calendar;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.util.Locale;
-import java.util.TimeZone;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationTest {
 
-    public BitmapDrawable generateTestDrawable(int width, int height, int colorValue) {
+    public Icon generateTestIcon(int width, int height, int colorValue) {
         final int numPixels = width * height;
         final int[] colors = new int[numPixels];
         for (int i = 0; i < numPixels; ++i) {
             colors[i] = colorValue;
         }
         final Bitmap bitmap = Bitmap.createBitmap(colors, width, height, Bitmap.Config.ARGB_8888);
-        final BitmapDrawable drawable = new BitmapDrawable(Resources.getSystem(), bitmap);
-        drawable.setTargetDensity(bitmap.getDensity());
-        return drawable;
+        return Icon.createWithBitmap(bitmap);
     }
 
     @Test
     public void testParcel() {
+        final Context context = InstrumentationRegistry.getTargetContext();
         final String text = "text";
-        final BitmapDrawable primaryIcon = generateTestDrawable(16, 16, Color.RED);
-        final String primaryLabel = "primarylabel";
-        final Intent primaryIntent = new Intent("primaryintentaction");
-        final View.OnClickListener primaryOnClick = v -> { };
-        final BitmapDrawable secondaryIcon0 = generateTestDrawable(32, 288, Color.GREEN);
-        final String secondaryLabel0 = "secondarylabel0";
-        final Intent secondaryIntent0 = new Intent("secondaryintentaction0");
-        final BitmapDrawable secondaryIcon1 = generateTestDrawable(576, 288, Color.BLUE);
-        final String secondaryLabel1 = "secondaryLabel1";
-        final Intent secondaryIntent1 = null;
-        final BitmapDrawable secondaryIcon2 = null;
-        final String secondaryLabel2 = null;
-        final Intent secondaryIntent2 = new Intent("secondaryintentaction2");
-        final ColorDrawable secondaryIcon3 = new ColorDrawable(Color.CYAN);
-        final String secondaryLabel3 = null;
-        final Intent secondaryIntent3 = null;
+
+        final Icon primaryIcon = generateTestIcon(576, 288, Color.BLUE);
+        final String primaryLabel = "primaryLabel";
+        final String primaryDescription = "primaryDescription";
+        final Intent primaryIntent = new Intent("primaryIntentAction");
+        final PendingIntent primaryPendingIntent = PendingIntent.getActivity(context, 0,
+                primaryIntent, 0);
+        final RemoteAction remoteAction0 = new RemoteAction(primaryIcon, primaryLabel,
+                primaryDescription, primaryPendingIntent);
+
+        final Icon secondaryIcon = generateTestIcon(32, 288, Color.GREEN);
+        final String secondaryLabel = "secondaryLabel";
+        final String secondaryDescription = "secondaryDescription";
+        final Intent secondaryIntent = new Intent("secondaryIntentAction");
+        final PendingIntent secondaryPendingIntent = PendingIntent.getActivity(context, 0,
+                secondaryIntent, 0);
+        final RemoteAction remoteAction1 = new RemoteAction(secondaryIcon, secondaryLabel,
+                secondaryDescription, secondaryPendingIntent);
+
         final String signature = "signature";
         final TextClassification reference = new TextClassification.Builder()
                 .setText(text)
-                .setPrimaryAction(primaryIntent, primaryLabel, primaryIcon)
-                .setOnClickListener(primaryOnClick)
-                .addSecondaryAction(null, null, null)  // ignored
-                .addSecondaryAction(secondaryIntent0, secondaryLabel0, secondaryIcon0)
-                .addSecondaryAction(secondaryIntent1, secondaryLabel1, secondaryIcon1)
-                .addSecondaryAction(secondaryIntent2, secondaryLabel2, secondaryIcon2)
-                .addSecondaryAction(secondaryIntent3, secondaryLabel3, secondaryIcon3)
+                .addAction(remoteAction0)
+                .addAction(remoteAction1)
                 .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
                 .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
                 .setSignature(signature)
@@ -95,45 +97,25 @@
 
         assertEquals(text, result.getText());
         assertEquals(signature, result.getSignature());
-        assertEquals(4, result.getSecondaryActionsCount());
+        assertEquals(2, result.getActions().size());
 
-        // Primary action (re-use existing icon).
-        final Bitmap resPrimaryIcon = ((BitmapDrawable) result.getIcon()).getBitmap();
-        assertEquals(primaryIcon.getBitmap().getPixel(0, 0), resPrimaryIcon.getPixel(0, 0));
-        assertEquals(16, resPrimaryIcon.getWidth());
-        assertEquals(16, resPrimaryIcon.getHeight());
-        assertEquals(primaryLabel, result.getLabel());
-        assertEquals(primaryIntent.getAction(), result.getIntent().getAction());
-        assertEquals(null, result.getOnClickListener());  // Non-parcelable.
+        // Legacy API.
+        assertNull(result.getIcon());
+        assertNull(result.getLabel());
+        assertNull(result.getIntent());
+        assertNull(result.getOnClickListener());
 
-        // Secondary action 0 (scale with  height limit).
-        final Bitmap resSecondaryIcon0 = ((BitmapDrawable) result.getSecondaryIcon(0)).getBitmap();
-        assertEquals(secondaryIcon0.getBitmap().getPixel(0, 0), resSecondaryIcon0.getPixel(0, 0));
-        assertEquals(16, resSecondaryIcon0.getWidth());
-        assertEquals(144, resSecondaryIcon0.getHeight());
-        assertEquals(secondaryLabel0, result.getSecondaryLabel(0));
-        assertEquals(secondaryIntent0.getAction(), result.getSecondaryIntent(0).getAction());
+        // Primary action.
+        final RemoteAction primaryAction = result.getActions().get(0);
+        assertEquals(primaryLabel, primaryAction.getTitle());
+        assertEquals(primaryDescription, primaryAction.getContentDescription());
+        assertEquals(primaryPendingIntent, primaryAction.getActionIntent());
 
-        // Secondary action 1 (scale with width limit).
-        final Bitmap resSecondaryIcon1 = ((BitmapDrawable) result.getSecondaryIcon(1)).getBitmap();
-        assertEquals(secondaryIcon1.getBitmap().getPixel(0, 0), resSecondaryIcon1.getPixel(0, 0));
-        assertEquals(144, resSecondaryIcon1.getWidth());
-        assertEquals(72, resSecondaryIcon1.getHeight());
-        assertEquals(secondaryLabel1, result.getSecondaryLabel(1));
-        assertEquals(null, result.getSecondaryIntent(1));
-
-        // Secondary action 2 (no icon).
-        assertEquals(null, result.getSecondaryIcon(2));
-        assertEquals(null, result.getSecondaryLabel(2));
-        assertEquals(secondaryIntent2.getAction(), result.getSecondaryIntent(2).getAction());
-
-        // Secondary action 3 (convert non-bitmap drawable with negative size).
-        final Bitmap resSecondaryIcon3 = ((BitmapDrawable) result.getSecondaryIcon(3)).getBitmap();
-        assertEquals(secondaryIcon3.getColor(), resSecondaryIcon3.getPixel(0, 0));
-        assertEquals(1, resSecondaryIcon3.getWidth());
-        assertEquals(1, resSecondaryIcon3.getHeight());
-        assertEquals(null, result.getSecondaryLabel(3));
-        assertEquals(null, result.getSecondaryIntent(3));
+        // Secondary action.
+        final RemoteAction secondaryAction = result.getActions().get(1);
+        assertEquals(secondaryLabel, secondaryAction.getTitle());
+        assertEquals(secondaryDescription, secondaryAction.getContentDescription());
+        assertEquals(secondaryPendingIntent, secondaryAction.getActionIntent());
 
         // Entities.
         assertEquals(2, result.getEntityCount());
@@ -144,9 +126,47 @@
     }
 
     @Test
+    public void testParcelLegacy() {
+        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        final String text = "text";
+
+        final Icon icon = generateTestIcon(384, 192, Color.BLUE);
+        final String label = "label";
+        final Intent intent = new Intent("intent");
+        final View.OnClickListener onClickListener = v -> { };
+
+        final String signature = "signature";
+        final TextClassification reference = new TextClassification.Builder()
+                .setText(text)
+                .setIcon(icon.loadDrawable(context))
+                .setLabel(label)
+                .setIntent(intent)
+                .setOnClickListener(onClickListener)
+                .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f)
+                .setEntityType(TextClassifier.TYPE_PHONE, 0.7f)
+                .setSignature(signature)
+                .build();
+
+        // Parcel and unparcel
+        final Parcel parcel = Parcel.obtain();
+        reference.writeToParcel(parcel, reference.describeContents());
+        parcel.setDataPosition(0);
+        final TextClassification result = TextClassification.CREATOR.createFromParcel(parcel);
+
+        final Bitmap resultIcon = ((BitmapDrawable) result.getIcon()).getBitmap();
+        assertEquals(icon.getBitmap().getPixel(0, 0), resultIcon.getPixel(0, 0));
+        assertEquals(192, resultIcon.getWidth());
+        assertEquals(96, resultIcon.getHeight());
+        assertEquals(label, result.getLabel());
+        assertEquals(intent.getAction(), result.getIntent().getAction());
+        assertNull(result.getOnClickListener());
+    }
+
+    @Test
     public void testParcelOptions() {
-        Calendar referenceTime = Calendar.getInstance(TimeZone.getTimeZone("UTC"), Locale.US);
-        referenceTime.setTimeInMillis(946771200000L);  // 2000-01-02
+        ZonedDateTime referenceTime = ZonedDateTime.ofInstant(
+                Instant.ofEpochMilli(946771200000L),  // 2000-01-02
+                ZoneId.of("UTC"));
 
         TextClassification.Options reference = new TextClassification.Options();
         reference.setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
index acf3022..992b46f 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBackgroundStatsTest.java
@@ -273,53 +273,6 @@
     }
 
     @SmallTest
-    public void testAppBluetoothScan_workChainAccounting() throws Exception {
-        final MockClocks clocks = new MockClocks();MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
-        long curr = 0; // realtime in us
-
-        // On battery
-        curr = 1000 * (clocks.realtime = clocks.uptime = 100);
-        bi.updateTimeBasesLocked(true, Display.STATE_ON, curr, curr); // on battery
-
-        // App in foreground
-        bi.noteUidProcessStateLocked(UID, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
-
-        WorkSource ws = new WorkSource();
-        ws.createWorkChain().addNode(500, "foo");
-        ws.createWorkChain().addNode(500, "bar");
-
-        // Test start / stop and reset with isUnoptimized == false.
-        bi.noteBluetoothScanStartedFromSourceLocked(ws, false);
-        BatteryStatsImpl.Uid stats = (BatteryStatsImpl.Uid) bi.getUidStats().get(500);
-        assertEquals(ws.getWorkChains(), stats.getAllBluetoothWorkChains());
-        assertNull(stats.getUnoptimizedBluetoothWorkChains());
-
-        bi.noteBluetoothScanStoppedFromSourceLocked(ws, false);
-        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
-        assertNull(stats.getUnoptimizedBluetoothWorkChains());
-
-        bi.noteBluetoothScanStartedFromSourceLocked(ws, false);
-        bi.noteResetBluetoothScanLocked();
-        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
-        assertNull(stats.getUnoptimizedBluetoothWorkChains());
-
-        // Test start / stop  and reset with isUnoptimized == true.
-        bi.noteBluetoothScanStartedFromSourceLocked(ws, true);
-        stats = (BatteryStatsImpl.Uid) bi.getUidStats().get(500);
-        assertEquals(ws.getWorkChains(), stats.getAllBluetoothWorkChains());
-        assertEquals(ws.getWorkChains(), stats.getUnoptimizedBluetoothWorkChains());
-
-        bi.noteBluetoothScanStoppedFromSourceLocked(ws, true);
-        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
-        assertTrue(stats.getUnoptimizedBluetoothWorkChains().isEmpty());
-
-        bi.noteBluetoothScanStartedFromSourceLocked(ws, true);
-        bi.noteResetBluetoothScanLocked();
-        assertTrue(stats.getAllBluetoothWorkChains().isEmpty());
-        assertTrue(stats.getUnoptimizedBluetoothWorkChains().isEmpty());
-    }
-
-    @SmallTest
     public void testJob() throws Exception {
         final MockClocks clocks = new MockClocks();
         MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
diff --git a/data/etc/hiddenapi-package-whitelist.xml b/data/etc/hiddenapi-package-whitelist.xml
index bacddf14..a4a1b94 100644
--- a/data/etc/hiddenapi-package-whitelist.xml
+++ b/data/etc/hiddenapi-package-whitelist.xml
@@ -64,6 +64,7 @@
   <hidden-api-whitelisted-app package="com.android.gallery" />
   <hidden-api-whitelisted-app package="com.android.hotspot2" />
   <hidden-api-whitelisted-app package="com.android.keychain" />
+  <hidden-api-whitelisted-app package="com.android.launcher3" />
   <hidden-api-whitelisted-app package="com.android.location.fused" />
   <hidden-api-whitelisted-app package="com.android.managedprovisioning" />
   <hidden-api-whitelisted-app package="com.android.mms.service" />
diff --git a/docs/html/reference/images/display_cutout/default_mode/fullscreen_letterbox.png b/docs/html/reference/images/display_cutout/default_mode/fullscreen_letterbox.png
new file mode 100644
index 0000000..8602290
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/default_mode/fullscreen_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/default_mode/landscape.png b/docs/html/reference/images/display_cutout/default_mode/landscape.png
new file mode 100644
index 0000000..be112f4
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/default_mode/landscape.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/default_mode/landscape_corner.png b/docs/html/reference/images/display_cutout/default_mode/landscape_corner.png
new file mode 100644
index 0000000..7aa92b4
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/default_mode/landscape_corner.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/default_mode/portrait.png b/docs/html/reference/images/display_cutout/default_mode/portrait.png
new file mode 100644
index 0000000..6392ad5
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/default_mode/portrait.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/default_mode/portrait_corner.png b/docs/html/reference/images/display_cutout/default_mode/portrait_corner.png
new file mode 100644
index 0000000..dc8e6d0
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/default_mode/portrait_corner.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/never_mode/landscape.png b/docs/html/reference/images/display_cutout/never_mode/landscape.png
new file mode 100644
index 0000000..be112f4
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/never_mode/landscape.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/never_mode/portrait_top_letterbox.png b/docs/html/reference/images/display_cutout/never_mode/portrait_top_letterbox.png
new file mode 100644
index 0000000..fc7dfcf
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/never_mode/portrait_top_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png b/docs/html/reference/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png
new file mode 100644
index 0000000..09f942d
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/fullscreen_corner_no_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/fullscreen_side_letterbox.png b/docs/html/reference/images/display_cutout/short_edge/fullscreen_side_letterbox.png
new file mode 100644
index 0000000..9aafc58
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/fullscreen_side_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/fullscreen_top_no_letterbox.png b/docs/html/reference/images/display_cutout/short_edge/fullscreen_top_no_letterbox.png
new file mode 100644
index 0000000..75e913b
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/fullscreen_top_no_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/landscape_corner.png b/docs/html/reference/images/display_cutout/short_edge/landscape_corner.png
new file mode 100644
index 0000000..d9e0f99
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/landscape_corner.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/landscape_sideboxed.png b/docs/html/reference/images/display_cutout/short_edge/landscape_sideboxed.png
new file mode 100644
index 0000000..9d44493
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/landscape_sideboxed.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/landscape_top_no_letterbox.png b/docs/html/reference/images/display_cutout/short_edge/landscape_top_no_letterbox.png
new file mode 100644
index 0000000..ac023c5
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/landscape_top_no_letterbox.png
Binary files differ
diff --git a/docs/html/reference/images/display_cutout/short_edge/portrait_side_letterbox.png b/docs/html/reference/images/display_cutout/short_edge/portrait_side_letterbox.png
new file mode 100644
index 0000000..50228db
--- /dev/null
+++ b/docs/html/reference/images/display_cutout/short_edge/portrait_side_letterbox.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 5dc4463..71ee6c2 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -41,13 +41,19 @@
     /**
      * Should only be assigned in constructors (or setBitmap if software canvas),
      * freed by NativeAllocation.
+     * @hide
      */
     protected long mNativeCanvasWrapper;
 
     /**
      * Used to determine when compatibility scaling is in effect.
+     * @hide
      */
     protected int mScreenDensity = Bitmap.DENSITY_NONE;
+
+    /**
+     * @hide
+     */
     protected int mDensity = Bitmap.DENSITY_NONE;
     private boolean mAllowHwBitmapsInSwMode = false;
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index b0bc102..7080657 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -47,6 +47,7 @@
  * Canvas and Drawables</a> developer guide.</p></div>
  */
 public class Canvas extends BaseCanvas {
+    private static int sCompatiblityVersion = 0;
     /** @hide */
     public static boolean sCompatibilityRestore = false;
     /** @hide */
@@ -306,7 +307,7 @@
 
     /**
      * Restore the current matrix when restore() is called.
-     *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the matrix
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -318,6 +319,7 @@
     /**
      * Restore the current clip when restore() is called.
      *
+     * @removed
      * @deprecated Use the flagless version of {@link #save()}, {@link #saveLayer(RectF, Paint)} or
      *             {@link #saveLayerAlpha(RectF, int)}. For saveLayer() calls the clip
      *             was always restored for {@link #isHardwareAccelerated() Hardware accelerated}
@@ -329,6 +331,7 @@
     /**
      * The layer requires a per-pixel alpha channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -337,6 +340,7 @@
     /**
      * The layer requires full 8-bit precision for each color channel.
      *
+     * @removed
      * @deprecated This flag is ignored. Use the flagless version of {@link #saveLayer(RectF, Paint)}
      *             {@link #saveLayerAlpha(RectF, int)}.
      */
@@ -349,6 +353,7 @@
      * <code>saveLayerAlpha()</code> variants. Not passing this flag generally
      * triggers extremely poor performance with hardware accelerated rendering.
      *
+     * @removed
      * @deprecated This flag results in poor performance and the same effect can be achieved with
      *             a single layer or multiple draw commands with different clips.
      *
@@ -362,6 +367,7 @@
      * call to <code>saveLayer()</code> and <code>saveLayerAlpha()</code>
      * variants.
      *
+     * @removed There are no visible methods remaining that use this flag
      * <p class="note"><strong>Note:</strong> all methods that accept this flag
      * have flagless versions that are equivalent to passing this flag.
      */
@@ -393,6 +399,7 @@
      * restore() is made, those calls will be forgotten, and the settings that
      * existed before the save() will be reinstated.
      *
+     * @removed
      * @deprecated Use {@link #save()} instead.
      * @param saveFlags flag bits that specify which parts of the Canvas state
      *                  to save/restore
@@ -424,6 +431,7 @@
      * {@link Paint#getColorFilter() ColorFilter} are applied when the
      * offscreen bitmap is drawn back when restore() is called.
      *
+     * @removed
      * @deprecated Use {@link #saveLayer(RectF, Paint)} instead.
      * @param bounds May be null. The maximum size the offscreen bitmap
      *               needs to be (in local coordinates)
@@ -473,8 +481,10 @@
     /**
      * Helper version of saveLayer() that takes 4 values rather than a RectF.
      *
+     * @removed
      * @deprecated Use {@link #saveLayer(float, float, float, float, Paint)} instead.
      */
+    @Deprecated
     public int saveLayer(float left, float top, float right, float bottom, @Nullable Paint paint,
             @Saveflags int saveFlags) {
         return nSaveLayer(mNativeCanvasWrapper, left, top, right, bottom,
@@ -510,6 +520,7 @@
      * The {@code alpha} parameter is applied when the offscreen bitmap is
      * drawn back when restore() is called.
      *
+     * @removed
      * @deprecated Use {@link #saveLayerAlpha(RectF, int)} instead.
      * @param bounds    The maximum size the offscreen bitmap needs to be
      *                  (in local coordinates)
@@ -542,8 +553,10 @@
     /**
      * Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
      *
+     * @removed
      * @deprecated Use {@link #saveLayerAlpha(float, float, float, float, int)} instead.
      */
+    @Deprecated
     public int saveLayerAlpha(float left, float top, float right, float bottom, int alpha,
             @Saveflags int saveFlags) {
         alpha = Math.min(255, Math.max(0, alpha));
@@ -738,6 +751,14 @@
         return m;
     }
 
+    private static void checkValidClipOp(@NonNull Region.Op op) {
+        if (sCompatiblityVersion >= Build.VERSION_CODES.P
+                && op != Region.Op.INTERSECT && op != Region.Op.DIFFERENCE) {
+            throw new IllegalArgumentException(
+                    "Invalid Region.Op - only INTERSECT and DIFFERENCE are allowed");
+        }
+    }
+
     /**
      * Modify the current clip with the specified rectangle.
      *
@@ -750,9 +771,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(RectF)} and {@link #clipOutRect(RectF)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull RectF rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -770,9 +795,13 @@
      * are intended to only expand the clip as a result of a restore operation. This enables a view
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(Rect)} and {@link #clipOutRect(Rect)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(@NonNull Rect rect, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
                 op.nativeInt);
     }
@@ -846,10 +875,14 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipRect(float,float,float,float)} and
      * {@link #clipOutRect(float,float,float,float)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipRect(float left, float top, float right, float bottom,
             @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipRect(mNativeCanvasWrapper, left, top, right, bottom, op.nativeInt);
     }
 
@@ -932,9 +965,13 @@
      * parent to clip a canvas to clearly define the maximal drawing area of its children. The
      * recommended alternative calls are {@link #clipPath(Path)} and
      * {@link #clipOutPath(Path)};
+     *
+     * As of API Level API level {@value Build.VERSION_CODES#P} only {@link Region.Op#INTERSECT} and
+     * {@link Region.Op#DIFFERENCE} are valid Region.Op parameters.
      */
     @Deprecated
     public boolean clipPath(@NonNull Path path, @NonNull Region.Op op) {
+        checkValidClipOp(op);
         return nClipPath(mNativeCanvasWrapper, path.readOnlyNI(), op.nativeInt);
     }
 
@@ -1220,7 +1257,10 @@
     }
 
     /** @hide */
-    public static void setCompatibilityVersion(int apiLevel) { nSetCompatibilityVersion(apiLevel); }
+    public static void setCompatibilityVersion(int apiLevel) {
+        sCompatiblityVersion = apiLevel;
+        nSetCompatibilityVersion(apiLevel);
+    }
 
     private static native void nFreeCaches();
     private static native void nFreeTextLayoutCaches();
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 5ca0ad6..00dc22e 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -21,10 +21,12 @@
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
+import android.annotation.AnyThread;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.TestApi;
+import android.annotation.WorkerThread;
 import android.content.ContentResolver;
 import android.content.res.AssetFileDescriptor;
 import android.content.res.AssetManager;
@@ -65,6 +67,16 @@
 
     /**
      *  Source of the encoded image data.
+     *
+     *  <p>This object references the data that will be used to decode a
+     *  Drawable or Bitmap in {@link #decodeDrawable} or {@link #decodeBitmap}.
+     *  Constructing a {@code Source} (with one of the overloads of
+     *  {@code createSource}) can be done on any thread because the construction
+     *  simply captures values. The real work is done in decodeDrawable or
+     *  decodeBitmap.</p>
+     *
+     *  <p>Further, a Source object can be reused with different settings, or
+     *  even used simultaneously in multiple threads.</p>
      */
     public static abstract class Source {
         private Source() {}
@@ -120,7 +132,8 @@
                 int length = mBuffer.limit() - mBuffer.position();
                 return nCreate(mBuffer.array(), offset, length, this);
             }
-            return nCreate(mBuffer, mBuffer.position(), mBuffer.limit(), this);
+            ByteBuffer buffer = mBuffer.slice();
+            return nCreate(buffer, buffer.position(), buffer.limit(), this);
         }
     }
 
@@ -232,6 +245,8 @@
 
     /**
      * For backwards compatibility, this does *not* close the InputStream.
+     *
+     * Further, unlike other Sources, this one is not reusable.
      */
     private static class InputStreamSource extends Source {
         InputStreamSource(Resources res, InputStream is, int inputDensity) {
@@ -322,12 +337,17 @@
         final Resources mResources;
         final int       mResId;
         int             mResDensity;
+        private Object  mLock = new Object();
 
         @Override
         public Resources getResources() { return mResources; }
 
         @Override
-        public int getDensity() { return mResDensity; }
+        public int getDensity() {
+            synchronized (mLock) {
+                return mResDensity;
+            }
+        }
 
         @Override
         public ImageDecoder createImageDecoder() throws IOException {
@@ -336,10 +356,12 @@
             // keep it alive.
             InputStream is = mResources.openRawResource(mResId, value);
 
-            if (value.density == TypedValue.DENSITY_DEFAULT) {
-                mResDensity = DisplayMetrics.DENSITY_DEFAULT;
-            } else if (value.density != TypedValue.DENSITY_NONE) {
-                mResDensity = value.density;
+            synchronized (mLock) {
+                if (value.density == TypedValue.DENSITY_DEFAULT) {
+                    mResDensity = DisplayMetrics.DENSITY_DEFAULT;
+                } else if (value.density != TypedValue.DENSITY_NONE) {
+                    mResDensity = value.density;
+                }
             }
 
             return createFromAsset((AssetInputStream) is, this);
@@ -432,6 +454,18 @@
         public boolean isAnimated() {
             return mDecoder.mAnimated;
         }
+
+        /**
+         * If known, the color space the decoded bitmap will have. Note that the
+         * output color space is not guaranteed to be the color space the bitmap
+         * is encoded with. If not known (when the config is
+         * {@link Bitmap.Config#ALPHA_8} for instance), or there is an error,
+         * it is set to null.
+         */
+        @Nullable
+        public ColorSpace getColorSpace() {
+            return mDecoder.getColorSpace();
+        }
     };
 
     /** @removed
@@ -443,6 +477,9 @@
     /**
      *  Optional listener supplied to {@link #decodeDrawable} or
      *  {@link #decodeBitmap}.
+     *
+     *  <p>This is necessary in order to change the default settings of the
+     *  decode.</p>
      */
     public static interface OnHeaderDecodedListener {
         /**
@@ -534,6 +571,9 @@
 
         /**
          *  Retrieve the {@link Source} that was interrupted.
+         *
+         *  <p>This can be used for equality checking to find the Source which
+         *  failed to completely decode.</p>
          */
         @NonNull
         public Source getSource() {
@@ -582,16 +622,17 @@
     private final int     mHeight;
     private final boolean mAnimated;
 
-    private int     mDesiredWidth;
-    private int     mDesiredHeight;
-    private int     mAllocator = ALLOCATOR_DEFAULT;
-    private boolean mRequireUnpremultiplied = false;
-    private boolean mMutable = false;
-    private boolean mConserveMemory = false;
-    private boolean mDecodeAsAlphaMask = false;
-    private Rect    mCropRect;
-    private Rect    mOutPaddingRect;
-    private Source  mSource;
+    private int        mDesiredWidth;
+    private int        mDesiredHeight;
+    private int        mAllocator = ALLOCATOR_DEFAULT;
+    private boolean    mUnpremultipliedRequired = false;
+    private boolean    mMutable = false;
+    private boolean    mConserveMemory = false;
+    private boolean    mDecodeAsAlphaMask = false;
+    private ColorSpace mDesiredColorSpace = null;
+    private Rect       mCropRect;
+    private Rect       mOutPaddingRect;
+    private Source     mSource;
 
     private PostProcessor          mPostProcessor;
     private OnPartialImageListener mOnPartialImageListener;
@@ -645,6 +686,7 @@
      * @return a new Source object, which can be passed to
      *      {@link #decodeDrawable} or {@link #decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull Resources res, int resId)
     {
@@ -659,6 +701,7 @@
      * @return a new Source object, which can be passed to
      *      {@link #decodeDrawable} or {@link #decodeBitmap}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull ContentResolver cr,
             @NonNull Uri uri) {
@@ -670,6 +713,7 @@
      *
      * @hide
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull ContentResolver cr,
             @NonNull Uri uri, @Nullable Resources res) {
@@ -679,6 +723,7 @@
     /**
      * Create a new {@link Source} from a file in the "assets" directory.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull AssetManager assets, @NonNull String fileName) {
         return new AssetSource(assets, fileName);
@@ -696,6 +741,7 @@
      *      not within data.
      * @hide
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull byte[] data, int offset,
             int length) throws ArrayIndexOutOfBoundsException {
@@ -714,6 +760,7 @@
      * See {@link #createSource(byte[], int, int).
      * @hide
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull byte[] data) {
         return createSource(data, 0, data.length);
@@ -731,24 +778,35 @@
      * be modified, even after the {@code AnimatedImageDrawable} is returned.
      * {@code buffer}'s contents should never be modified during decode.</p>
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull ByteBuffer buffer) {
-        return new ByteBufferSource(buffer.slice());
+        return new ByteBufferSource(buffer);
     }
 
     /**
      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     *
+     * <p>Unlike other Sources, this one cannot be reused.</p>
+     *
      * @hide
      */
+    @AnyThread
+    @NonNull
     public static Source createSource(Resources res, InputStream is) {
         return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
     }
 
     /**
      * Internal API used to generate bitmaps for use by Drawables (i.e. BitmapDrawable)
+     *
+     * <p>Unlike other Sources, this one cannot be reused.</p>
+     *
      * @hide
      */
+    @AnyThread
     @TestApi
+    @NonNull
     public static Source createSource(Resources res, InputStream is, int density) {
         return new InputStreamSource(res, is, density);
     }
@@ -756,6 +814,7 @@
     /**
      * Create a new {@link Source} from a {@link java.io.File}.
      */
+    @AnyThread
     @NonNull
     public static Source createSource(@NonNull File file) {
         return new FileSource(file);
@@ -766,14 +825,16 @@
      *
      *  <p>This takes an input that functions like
      *  {@link BitmapFactory.Options#inSampleSize}. It returns a width and
-     *  height that can be acheived by sampling the encoded image. Other widths
+     *  height that can be achieved by sampling the encoded image. Other widths
      *  and heights may be supported, but will require an additional (internal)
      *  scaling step. Such internal scaling is *not* supported with
-     *  {@link #setRequireUnpremultiplied} set to {@code true}.</p>
+     *  {@link #setUnpremultipliedRequired} set to {@code true}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
      *  @return {@link android.util.Size} of the width and height after
      *      sampling.
+     *
+     *  @hide
      */
     @NonNull
     public Size getSampledSize(int sampleSize) {
@@ -789,14 +850,32 @@
     }
 
     // Modifiers
+    /** @removed
+     * @deprecated Renamed to {@link #setTargetSize}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setResize(int width, int height) {
+        this.setTargetSize(width, height);
+        return this;
+    }
+
     /**
-     *  Resize the output to have the following size.
+     *  Specify the size of the output {@link Drawable} or {@link Bitmap}.
+     *
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
+     *  <p>Only the last call to this or {@link #setTargetSampleSize} is
+     *  respected.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @param width must be greater than 0.
      *  @param height must be greater than 0.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setResize(int width, int height) {
+    public void setTargetSize(int width, int height) {
         if (width <= 0 || height <= 0) {
             throw new IllegalArgumentException("Dimensions must be positive! "
                     + "provided (" + width + ", " + height + ")");
@@ -804,21 +883,70 @@
 
         mDesiredWidth = width;
         mDesiredHeight = height;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setTargetSampleSize}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setResize(int sampleSize) {
+        this.setTargetSampleSize(sampleSize);
         return this;
     }
 
+    private int getTargetDimension(int original, int sampleSize, int computed) {
+        // Sampling will never result in a smaller size than 1.
+        if (sampleSize >= original) {
+            return 1;
+        }
+
+        // Use integer divide to find the desired size. If that is what
+        // getSampledSize computed, that is the size to use.
+        int target = original / sampleSize;
+        if (computed == target) {
+            return computed;
+        }
+
+        // If sampleSize does not divide evenly into original, the decoder
+        // may round in either direction. It just needs to get a result that
+        // is close.
+        int reverse = computed * sampleSize;
+        if (Math.abs(reverse - original) < sampleSize) {
+            // This is the size that can be decoded most efficiently.
+            return computed;
+        }
+
+        // The decoder could not get close (e.g. it is a DNG image).
+        return target;
+    }
+
     /**
-     *  Resize based on a sample size.
+     *  Set the target size with a sampleSize.
      *
-     *  <p>This has the same effect as passing the result of
-     *  {@link #getSampledSize} to {@link #setResize(int, int)}.</p>
+     *  <p>By default, the output size will match the size of the encoded
+     *  image, which can be retrieved from the {@link ImageInfo} in
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
+     *  <p>Requests the decoder to subsample the original image, returning a
+     *  smaller image to save memory. The sample size is the number of pixels
+     *  in either dimension that correspond to a single pixel in the output.
+     *  For example, sampleSize == 4 returns an image that is 1/4 the
+     *  width/height of the original, and 1/16 the number of pixels.</p>
+     *
+     *  <p>Must be greater than or equal to 1.</p>
+     *
+     *  <p>Only the last call to this or {@link #setTargetSize} is respected.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @param sampleSize Sampling rate of the encoded image.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setResize(int sampleSize) {
+    public void setTargetSampleSize(int sampleSize) {
         Size size = this.getSampledSize(sampleSize);
-        return this.setResize(size.getWidth(), size.getHeight());
+        int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth());
+        int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight());
+        this.setTargetSize(targetWidth, targetHeight);
     }
 
     private boolean requestedResize() {
@@ -832,7 +960,7 @@
      *  Will typically result in a {@link Bitmap.Config#HARDWARE}
      *  allocation, but may be software for small images. In addition, this will
      *  switch to software when HARDWARE is incompatible, e.g.
-     *  {@link #setMutable}, {@link #setDecodeAsAlphaMask}.
+     *  {@link #setMutableRequired}, {@link #setDecodeAsAlphaMaskEnabled}.
      */
     public static final int ALLOCATOR_DEFAULT = 0;
 
@@ -855,8 +983,8 @@
      *  Require a {@link Bitmap.Config#HARDWARE} {@link Bitmap}.
      *
      *  When this is combined with incompatible options, like
-     *  {@link #setMutable} or {@link #setDecodeAsAlphaMask}, {@link #decodeDrawable}
-     *  / {@link #decodeBitmap} will throw an
+     *  {@link #setMutableRequired} or {@link #setDecodeAsAlphaMaskEnabled},
+     *  {@link #decodeDrawable} / {@link #decodeBitmap} will throw an
      *  {@link java.lang.IllegalStateException}.
      */
     public static final int ALLOCATOR_HARDWARE = 3;
@@ -871,17 +999,18 @@
     /**
      *  Choose the backing for the pixel memory.
      *
-     *  This is ignored for animated drawables.
+     *  <p>This is ignored for animated drawables.</p>
+     *
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @param allocator Type of allocator to use.
-     *  @return this object for chaining.
      */
-    public ImageDecoder setAllocator(@Allocator int allocator) {
+    public void setAllocator(@Allocator int allocator) {
         if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) {
             throw new IllegalArgumentException("invalid allocator " + allocator);
         }
         mAllocator = allocator;
-        return this;
     }
 
     /**
@@ -905,18 +1034,35 @@
      *  {@link Drawable} will throw an {@link java.lang.IllegalStateException}.
      *  </p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      */
-    public ImageDecoder setRequireUnpremultiplied(boolean requireUnpremultiplied) {
-        mRequireUnpremultiplied = requireUnpremultiplied;
+    public void setUnpremultipliedRequired(boolean unpremultipliedRequired) {
+        mUnpremultipliedRequired = unpremultipliedRequired;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setUnpremultipliedRequired}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) {
+        this.setUnpremultipliedRequired(unpremultipliedRequired);
         return this;
     }
 
     /**
      *  Return whether the {@link Bitmap} will have unpremultiplied pixels.
      */
+    public boolean isUnpremultipliedRequired() {
+        return mUnpremultipliedRequired;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isUnpremultipliedRequired}.
+     */
+    @java.lang.Deprecated
     public boolean getRequireUnpremultiplied() {
-        return mRequireUnpremultiplied;
+        return this.isUnpremultipliedRequired();
     }
 
     /**
@@ -932,11 +1078,12 @@
      *  {@link Canvas} will be recorded immediately and then applied to each
      *  frame.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setPostProcessor(@Nullable PostProcessor p) {
+    public void setPostProcessor(@Nullable PostProcessor p) {
         mPostProcessor = p;
-        return this;
     }
 
     /**
@@ -953,11 +1100,12 @@
      *  <p>Will be called if there is an error in the input. Without one, an
      *  error will result in an Exception being thrown.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) {
+    public void setOnPartialImageListener(@Nullable OnPartialImageListener l) {
         mOnPartialImageListener = l;
-        return this;
     }
 
     /**
@@ -972,19 +1120,20 @@
      *  Crop the output to {@code subset} of the (possibly) scaled image.
      *
      *  <p>{@code subset} must be contained within the size set by
-     *  {@link #setResize} or the bounds of the image if setResize was not
-     *  called. Otherwise an {@link IllegalStateException} will be thrown by
+     *  {@link #setTargetSize} or the bounds of the image if setTargetSize was
+     *  not called. Otherwise an {@link IllegalStateException} will be thrown by
      *  {@link #decodeDrawable}/{@link #decodeBitmap}.</p>
      *
      *  <p>NOT intended as a replacement for
      *  {@link BitmapRegionDecoder#decodeRegion}. This supports all formats,
      *  but merely crops the output.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     *
      */
-    public ImageDecoder setCrop(@Nullable Rect subset) {
+    public void setCrop(@Nullable Rect subset) {
         mCropRect = subset;
-        return this;
     }
 
     /**
@@ -1001,13 +1150,13 @@
      *  If the image is a nine patch, this Rect will be set to the padding
      *  rectangle during decode. Otherwise it will not be modified.
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      *
      *  @hide
      */
-    public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) {
+    public void setOutPaddingRect(@NonNull Rect outPadding) {
         mOutPaddingRect = outPadding;
-        return this;
     }
 
     /**
@@ -1026,20 +1175,37 @@
      *  order to modify. Attempting to decode a mutable {@link Drawable} will
      *  throw an {@link java.lang.IllegalStateException}.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      */
-    public ImageDecoder setMutable(boolean mutable) {
+    public void setMutableRequired(boolean mutable) {
         mMutable = mutable;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setMutableRequired}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setMutable(boolean mutable) {
+        this.setMutableRequired(mutable);
         return this;
     }
 
     /**
      *  Return whether the {@link Bitmap} will be mutable.
      */
-    public boolean getMutable() {
+    public boolean isMutableRequired() {
         return mMutable;
     }
 
+    /** @removed
+     * @deprecated Renamed to {@link #isMutableRequired}.
+     */
+    @java.lang.Deprecated
+    public boolean getMutable() {
+        return this.isMutableRequired();
+    }
+
     /**
      *  Specify whether to potentially save RAM at the expense of quality.
      *
@@ -1052,11 +1218,11 @@
      *  This necessarily lowers the quality of the output, but saves half
      *  the memory used.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      */
-    public ImageDecoder setConserveMemory(boolean conserveMemory) {
+    public void setConserveMemory(boolean conserveMemory) {
         mConserveMemory = conserveMemory;
-        return this;
     }
 
     /**
@@ -1077,45 +1243,97 @@
      *  with only one channel, treat that channel as alpha. Otherwise this call has
      *  no effect.</p>
      *
-     *  <p>setDecodeAsAlphaMask is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
+     *  <p>This is incompatible with {@link #ALLOCATOR_HARDWARE}. Trying to
      *  combine them will result in {@link #decodeDrawable}/
      *  {@link #decodeBitmap} throwing an
      *  {@link java.lang.IllegalStateException}.</p>
      *
-     *  @return this object for chaining.
+     *  <p>Like all setters on ImageDecoder, this must be called inside
+     *  {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
      */
-    public ImageDecoder setDecodeAsAlphaMask(boolean decodeAsAlphaMask) {
-        mDecodeAsAlphaMask = decodeAsAlphaMask;
+    public void setDecodeAsAlphaMaskEnabled(boolean enabled) {
+        mDecodeAsAlphaMask = enabled;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
+     */
+    @java.lang.Deprecated
+    public ImageDecoder setDecodeAsAlphaMask(boolean enabled) {
+        this.setDecodeAsAlphaMaskEnabled(enabled);
         return this;
     }
 
     /** @removed
-     * @deprecated Call {@link #setDecodeAsAlphaMask} instead.
+     * @deprecated Renamed to {@link #setDecodeAsAlphaMaskEnabled}.
      */
     @java.lang.Deprecated
     public ImageDecoder setAsAlphaMask(boolean asAlphaMask) {
-        return this.setDecodeAsAlphaMask(asAlphaMask);
+        this.setDecodeAsAlphaMask(asAlphaMask);
+        return this;
     }
 
     /**
      *  Return whether to treat single channel input as alpha.
      *
-     *  <p>This returns whether {@link #setDecodeAsAlphaMask} was set to {@code true}.
-     *  It may still return {@code true} even if the image has more than one
-     *  channel and therefore will not be treated as an alpha mask.</p>
+     *  <p>This returns whether {@link #setDecodeAsAlphaMaskEnabled} was set to
+     *  {@code true}. It may still return {@code true} even if the image has
+     *  more than one channel and therefore will not be treated as an alpha
+     *  mask.</p>
      */
+    public boolean isDecodeAsAlphaMaskEnabled() {
+        return mDecodeAsAlphaMask;
+    }
+
+    /** @removed
+     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
+     */
+    @java.lang.Deprecated
     public boolean getDecodeAsAlphaMask() {
         return mDecodeAsAlphaMask;
     }
 
     /** @removed
-     * @deprecated Call {@link #getDecodeAsAlphaMask} instead.
+     * @deprecated Renamed to {@link #isDecodeAsAlphaMaskEnabled}.
      */
     @java.lang.Deprecated
     public boolean getAsAlphaMask() {
         return this.getDecodeAsAlphaMask();
     }
 
+    /**
+     * Specify the desired {@link ColorSpace} for the output.
+     *
+     * <p>If non-null, the decoder will try to decode into this
+     * color space. If it is null, which is the default, or the request cannot
+     * be met, the decoder will pick either the color space embedded in the
+     * image or the color space best suited for the requested image
+     * configuration (for instance {@link ColorSpace.Named#SRGB sRGB} for
+     * the {@link Bitmap.Config#ARGB_8888} configuration).</p>
+     *
+     * <p>{@link Bitmap.Config#RGBA_F16} always uses the
+     * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space).
+     * Bitmaps in other configurations without an embedded color space are
+     * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+     *
+     * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
+     * currently supported. An <code>IllegalArgumentException</code> will
+     * be thrown by the decode methods when setting a non-RGB color space
+     * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
+     *
+     * <p class="note">The specified color space's transfer function must be
+     * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
+     * <code>IllegalArgumentException</code> will be thrown by the decode methods
+     * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
+     * specified color space returns null.</p>
+     *
+     * <p>Like all setters on ImageDecoder, this must be called inside
+     * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p>
+     */
+    public void setTargetColorSpace(ColorSpace colorSpace) {
+        mDesiredColorSpace = colorSpace;
+    }
+
     @Override
     public void close() {
         mCloseGuard.close();
@@ -1151,9 +1369,20 @@
             }
         }
 
-        if (mPostProcessor != null && mRequireUnpremultiplied) {
+        if (mPostProcessor != null && mUnpremultipliedRequired) {
             throw new IllegalStateException("Cannot draw to unpremultiplied pixels!");
         }
+
+        if (mDesiredColorSpace != null) {
+            if (!(mDesiredColorSpace instanceof ColorSpace.Rgb)) {
+                throw new IllegalArgumentException("The target color space must use the "
+                            + "RGB color model - provided: " + mDesiredColorSpace);
+            }
+            if (((ColorSpace.Rgb) mDesiredColorSpace).getTransferParameters() == null) {
+                throw new IllegalArgumentException("The target color space must use an "
+                            + "ICC parametric transfer function - provided: " + mDesiredColorSpace);
+            }
+        }
     }
 
     private static void checkSubset(int width, int height, Rect r) {
@@ -1166,13 +1395,14 @@
         }
     }
 
+    @WorkerThread
     @NonNull
     private Bitmap decodeBitmapInternal() throws IOException {
         checkState();
         return nDecodeBitmap(mNativePtr, this, mPostProcessor != null,
                 mDesiredWidth, mDesiredHeight, mCropRect,
-                mMutable, mAllocator, mRequireUnpremultiplied,
-                mConserveMemory, mDecodeAsAlphaMask);
+                mMutable, mAllocator, mUnpremultipliedRequired,
+                mConserveMemory, mDecodeAsAlphaMask, mDesiredColorSpace);
     }
 
     private void callHeaderDecoded(@Nullable OnHeaderDecodedListener listener,
@@ -1194,10 +1424,12 @@
      *  @param listener for learning the {@link ImageInfo} and changing any
      *      default settings on the {@code ImageDecoder}. This will be called on
      *      the same thread as {@code decodeDrawable} before that method returns.
+     *      This is required in order to change any of the default settings.
      *  @return Drawable for displaying the image.
      *  @throws IOException if {@code src} is not found, is an unsupported
      *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src,
             @NonNull OnHeaderDecodedListener listener) throws IOException {
@@ -1208,6 +1440,7 @@
         return decodeDrawableImpl(src, listener);
     }
 
+    @WorkerThread
     @NonNull
     private static Drawable decodeDrawableImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
@@ -1215,7 +1448,7 @@
             decoder.mSource = src;
             decoder.callHeaderDecoded(listener, src);
 
-            if (decoder.mRequireUnpremultiplied) {
+            if (decoder.mUnpremultipliedRequired) {
                 // Though this could be supported (ignored) for opaque images,
                 // it seems better to always report this error.
                 throw new IllegalStateException("Cannot decode a Drawable " +
@@ -1268,8 +1501,18 @@
     }
 
     /**
-     * See {@link #decodeDrawable(Source, OnHeaderDecodedListener)}.
+     *  Create a {@link Drawable} from a {@code Source}.
+     *
+     *  <p>Since there is no {@link OnHeaderDecodedListener}, the default
+     *  settings will be used. In order to change any settings, call
+     *  {@link #decodeDrawable(Source, OnHeaderDecodedListener)} instead.</p>
+     *
+     *  @param src representing the encoded image.
+     *  @return Drawable for displaying the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Drawable decodeDrawable(@NonNull Source src)
             throws IOException {
@@ -1283,10 +1526,12 @@
      *  @param listener for learning the {@link ImageInfo} and changing any
      *      default settings on the {@code ImageDecoder}. This will be called on
      *      the same thread as {@code decodeBitmap} before that method returns.
+     *      This is required in order to change any of the default settings.
      *  @return Bitmap containing the image.
      *  @throws IOException if {@code src} is not found, is an unsupported
      *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src,
             @NonNull OnHeaderDecodedListener listener) throws IOException {
@@ -1297,6 +1542,7 @@
         return decodeBitmapImpl(src, listener);
     }
 
+    @WorkerThread
     @NonNull
     private static Bitmap decodeBitmapImpl(@NonNull Source src,
             @Nullable OnHeaderDecodedListener listener) throws IOException {
@@ -1353,7 +1599,7 @@
         float scale = (float) dstDensity / srcDensity;
         int scaledWidth = (int) (decoder.mWidth * scale + 0.5f);
         int scaledHeight = (int) (decoder.mHeight * scale + 0.5f);
-        decoder.setResize(scaledWidth, scaledHeight);
+        decoder.setTargetSize(scaledWidth, scaledHeight);
         return dstDensity;
     }
 
@@ -1362,9 +1608,24 @@
         return nGetMimeType(mNativePtr);
     }
 
+    @Nullable
+    private ColorSpace getColorSpace() {
+        return nGetColorSpace(mNativePtr);
+    }
+
     /**
-     *  See {@link #decodeBitmap(Source, OnHeaderDecodedListener)}.
+     *  Create a {@link Bitmap} from a {@code Source}.
+     *
+     *  <p>Since there is no {@link OnHeaderDecodedListener}, the default
+     *  settings will be used. In order to change any settings, call
+     *  {@link #decodeBitmap(Source, OnHeaderDecodedListener)} instead.</p>
+     *
+     *  @param src representing the encoded image.
+     *  @return Bitmap containing the image.
+     *  @throws IOException if {@code src} is not found, is an unsupported
+     *      format, or cannot be decoded for any reason.
      */
+    @WorkerThread
     @NonNull
     public static Bitmap decodeBitmap(@NonNull Source src) throws IOException {
         return decodeBitmapImpl(src, null);
@@ -1410,12 +1671,14 @@
             boolean doPostProcess,
             int width, int height,
             @Nullable Rect cropRect, boolean mutable,
-            int allocator, boolean requireUnpremul,
-            boolean conserveMemory, boolean decodeAsAlphaMask)
+            int allocator, boolean unpremulRequired,
+            boolean conserveMemory, boolean decodeAsAlphaMask,
+            @Nullable ColorSpace desiredColorSpace)
         throws IOException;
     private static native Size nGetSampledSize(long nativePtr,
                                                int sampleSize);
     private static native void nGetPadding(long nativePtr, @NonNull Rect outRect);
     private static native void nClose(long nativePtr);
     private static native String nGetMimeType(long nativePtr);
+    private static native ColorSpace nGetColorSpace(long nativePtr);
 }
diff --git a/graphics/java/android/graphics/Path.java b/graphics/java/android/graphics/Path.java
index 098cdc6..cd0862c 100644
--- a/graphics/java/android/graphics/Path.java
+++ b/graphics/java/android/graphics/Path.java
@@ -24,6 +24,8 @@
 import dalvik.annotation.optimization.CriticalNative;
 import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
 /**
  * The Path class encapsulates compound (multiple contour) geometric paths
  * consisting of straight line segments, quadratic curves, and cubic curves.
@@ -32,10 +34,14 @@
  * text on a path.
  */
 public class Path {
+
+    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Path.class.getClassLoader(), nGetFinalizer(), 48 /* dummy size */);
+
     /**
      * @hide
      */
-    public long mNativePath;
+    public final long mNativePath;
 
     /**
      * @hide
@@ -52,6 +58,7 @@
      */
     public Path() {
         mNativePath = nInit();
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -69,6 +76,7 @@
             }
         }
         mNativePath = nInit(valNative);
+        sRegistry.registerNativeAllocation(this, mNativePath);
     }
 
     /**
@@ -297,7 +305,7 @@
      *             a rectangle
      * @return     true if the path specifies a rectangle
      */
-    public boolean isRect(RectF rect) {
+    public boolean isRect(@Nullable  RectF rect) {
         return nIsRect(mNativePath, rect);
     }
 
@@ -771,15 +779,6 @@
         nTransform(mNativePath, matrix.native_instance);
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            nFinalize(mNativePath);
-            mNativePath = 0;  //  Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
     /** @hide */
     public final long readOnlyNI() {
         return mNativePath;
@@ -820,7 +819,7 @@
 
     private static native long nInit();
     private static native long nInit(long nPath);
-    private static native void nFinalize(long nPath);
+    private static native long nGetFinalizer();
     private static native void nSet(long native_dst, long nSrc);
     private static native void nComputeBounds(long nPath, RectF bounds);
     private static native void nIncReserve(long nPath, int extraPtCount);
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 97edf22..1eebd26 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -318,13 +318,17 @@
      * Retrieve the timestamp associated with the texture image set by the most recent call to
      * updateTexImage.
      *
-     * This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
-     * should be unaffected by time-of-day adjustments, and for a camera should be strictly
-     * monotonic but for a MediaPlayer may be reset when the position is set.  The
-     * specific meaning and zero point of the timestamp depends on the source providing images to
-     * the SurfaceTexture. Unless otherwise specified by the image source, timestamps cannot
-     * generally be compared across SurfaceTexture instances, or across multiple program
-     * invocations. It is mostly useful for determining time offsets between subsequent frames.
+     * <p>This timestamp is in nanoseconds, and is normally monotonically increasing. The timestamp
+     * should be unaffected by time-of-day adjustments. The specific meaning and zero point of the
+     * timestamp depends on the source providing images to the SurfaceTexture. Unless otherwise
+     * specified by the image source, timestamps cannot generally be compared across SurfaceTexture
+     * instances, or across multiple program invocations. It is mostly useful for determining time
+     * offsets between subsequent frames.</p>
+     *
+     * <p>For camera sources, timestamps should be strictly monotonic. Timestamps from MediaPlayer
+     * sources may be reset when the playback position is set. For EGL and Vulkan producers, the
+     * timestamp is the desired present time set with the EGL_ANDROID_presentation_time or
+     * VK_GOOGLE_display_timing extensions.</p>
      */
 
     public long getTimestamp() {
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index 5a8fa07..0d32075 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -33,8 +33,8 @@
     boolean isUserSelectable(String alias);
     void setUserSelectable(String alias, boolean isUserSelectable);
 
-    boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
-    boolean attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
+    int generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+    int attestKey(in String alias, in byte[] challenge, in int[] idAttestationFlags,
             out KeymasterCertificateChain chain);
     boolean setKeyPairCertificate(String alias, in byte[] userCert, in byte[] certChain);
 
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 2daf733..46a7fa8 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -246,6 +246,82 @@
     public static final String EXTRA_KEY_ACCESSIBLE = "android.security.extra.KEY_ACCESSIBLE";
 
     /**
+     * Indicates that a call to {@link #generateKeyPair} was successful.
+     * @hide
+     */
+    public static final int KEY_GEN_SUCCESS = 0;
+
+    /**
+     * An alias was missing from the key specifications when calling {@link #generateKeyPair}.
+     * @hide
+     */
+    public static final int KEY_GEN_MISSING_ALIAS = 1;
+
+    /**
+     * A key attestation challenge was provided to {@link #generateKeyPair}, but it shouldn't
+     * have been provided.
+     * @hide
+     */
+    public static final int KEY_GEN_SUPERFLUOUS_ATTESTATION_CHALLENGE = 2;
+
+    /**
+     * Algorithm not supported by {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_SUCH_ALGORITHM = 3;
+
+    /**
+     * Invalid algorithm parameters when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_INVALID_ALGORITHM_PARAMETERS = 4;
+
+    /**
+     * Keystore is not available when calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_NO_KEYSTORE_PROVIDER = 5;
+
+    /**
+     * General failure while calling {@link #generateKeyPair}
+     * @hide
+     */
+    public static final int KEY_GEN_FAILURE = 6;
+
+    /**
+     * Successful call to {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_SUCCESS = 0;
+
+    /**
+     * Attestation challenge missing when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_MISSING_CHALLENGE = 1;
+
+    /**
+     * The caller requested Device ID attestation when calling {@link #attestKey}, but has no
+     * permissions to get device identifiers.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_COLLECT_DATA = 2;
+
+    /**
+     * The underlying hardware does not support Device ID attestation or cannot attest to the
+     * identifiers that are stored on the device. This indicates permanent inability
+     * to get attestation records on the device.
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_CANNOT_ATTEST_IDS = 3;
+
+    /**
+     * General failure when calling {@link #attestKey}
+     * @hide
+     */
+    public static final int KEY_ATTESTATION_FAILURE = 4;
+
+    /**
      * Returns an {@code Intent} that can be used for credential
      * installation. The intent may be used without any extras, in
      * which case the user will be able to install credentials from
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 1924bbe..81644eb 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -16,6 +16,7 @@
 
 package android.security;
 
+import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.Application;
 import android.app.KeyguardManager;
@@ -66,6 +67,7 @@
     public static final int VALUE_CORRUPTED = 8;
     public static final int UNDEFINED_ACTION = 9;
     public static final int WRONG_PASSWORD = 10;
+    public static final int CANNOT_ATTEST_IDS = -66;
     public static final int HARDWARE_TYPE_UNAVAILABLE = -68;
 
     /**
@@ -278,7 +280,7 @@
     /**
      * Attempt to lock the keystore for {@code user}.
      *
-     * @param user Android user to lock.
+     * @param userId Android user to lock.
      * @return whether {@code user}'s keystore was locked.
      */
     public boolean lock(int userId) {
@@ -299,7 +301,7 @@
      * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or
      * created.
      *
-     * @param user Android user ID to operate on
+     * @param userId Android user ID to operate on
      * @param password user's keystore password. Should be the most recent value passed to
      * {@link #onUserPasswordChanged} for the user.
      *
@@ -545,6 +547,9 @@
         try {
             args = args != null ? args : new KeymasterArguments();
             entropy = entropy != null ? entropy : new byte[0];
+            if (!args.containsTag(KeymasterDefs.KM_TAG_USER_ID)) {
+                args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
+            }
             return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 09b3b9b..419eb24 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -243,13 +243,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), spec);
             } catch (IllegalStateException | IllegalArgumentException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -285,16 +279,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_BLOCK_MODE, mKeymasterBlockModes);
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterPaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
-        KeymasterUtils.addUserAuthArgs(args,
-                spec.isUserAuthenticationRequired(),
-                spec.getUserAuthenticationValidityDurationSeconds(),
-                spec.isUserAuthenticationValidWhileOnBody(),
-                spec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                spec.isUserConfirmationRequired());
-        if (spec.isTrustedUserPresenceRequired()) {
-            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
-        }
+        KeymasterUtils.addUserAuthArgs(args, spec);
         KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                 args,
                 mKeymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
index e33e3cd..d68a33d 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -344,13 +344,7 @@
                 // Check that user authentication related parameters are acceptable. This method
                 // will throw an IllegalStateException if there are issues (e.g., secure lock screen
                 // not set up).
-                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(),
-                        mSpec.isUserAuthenticationRequired(),
-                        mSpec.getUserAuthenticationValidityDurationSeconds(),
-                        mSpec.isUserAuthenticationValidWhileOnBody(),
-                        mSpec.isInvalidatedByBiometricEnrollment(),
-                        GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                        mSpec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(new KeymasterArguments(), mSpec);
             } catch (IllegalArgumentException | IllegalStateException e) {
                 throw new InvalidAlgorithmParameterException(e);
             }
@@ -541,13 +535,7 @@
         args.addEnums(KeymasterDefs.KM_TAG_PADDING, mKeymasterSignaturePaddings);
         args.addEnums(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigests);
 
-        KeymasterUtils.addUserAuthArgs(args,
-                mSpec.isUserAuthenticationRequired(),
-                mSpec.getUserAuthenticationValidityDurationSeconds(),
-                mSpec.isUserAuthenticationValidWhileOnBody(),
-                mSpec.isInvalidatedByBiometricEnrollment(),
-                GateKeeper.INVALID_SECURE_USER_ID /* boundToSpecificSecureUserId */,
-                mSpec.isUserConfirmationRequired());
+        KeymasterUtils.addUserAuthArgs(args, mSpec);
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME, mSpec.getKeyValidityStart());
         args.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
                 mSpec.getKeyValidityForOriginationEnd());
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index 05cc74a..fc86ca0 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -497,13 +497,7 @@
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterEncryptionPaddings);
                 importArgs.addEnums(KeymasterDefs.KM_TAG_PADDING,
                         KeyProperties.SignaturePadding.allToKeymaster(spec.getSignaturePaddings()));
-                KeymasterUtils.addUserAuthArgs(importArgs,
-                        spec.isUserAuthenticationRequired(),
-                        spec.getUserAuthenticationValidityDurationSeconds(),
-                        spec.isUserAuthenticationValidWhileOnBody(),
-                        spec.isInvalidatedByBiometricEnrollment(),
-                        spec.getBoundToSpecificSecureUserId(),
-                        spec.isUserConfirmationRequired());
+                KeymasterUtils.addUserAuthArgs(importArgs, spec);
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ACTIVE_DATETIME,
                         spec.getKeyValidityStart());
                 importArgs.addDateIfNotNull(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME,
@@ -700,13 +694,7 @@
             int[] keymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(
                     params.getEncryptionPaddings());
             args.addEnums(KeymasterDefs.KM_TAG_PADDING, keymasterPaddings);
-            KeymasterUtils.addUserAuthArgs(args,
-                    params.isUserAuthenticationRequired(),
-                    params.getUserAuthenticationValidityDurationSeconds(),
-                    params.isUserAuthenticationValidWhileOnBody(),
-                    params.isInvalidatedByBiometricEnrollment(),
-                    params.getBoundToSpecificSecureUserId(),
-                    params.isUserConfirmationRequired());
+            KeymasterUtils.addUserAuthArgs(args, params);
             KeymasterUtils.addMinMacLengthAuthorizationIfNecessary(
                     args,
                     keymasterAlgorithm,
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index da23c70..c342acd 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -21,6 +21,7 @@
 import android.annotation.Nullable;
 import android.app.KeyguardManager;
 import android.hardware.fingerprint.FingerprintManager;
+import android.security.GateKeeper;
 import android.security.KeyStore;
 import android.text.TextUtils;
 
@@ -232,7 +233,7 @@
  * key = (SecretKey) keyStore.getKey("key2", null);
  * }</pre>
  */
-public final class KeyGenParameterSpec implements AlgorithmParameterSpec {
+public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAuthArgs {
 
     private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
     private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
@@ -265,6 +266,7 @@
     private final boolean mInvalidatedByBiometricEnrollment;
     private final boolean mIsStrongBoxBacked;
     private final boolean mUserConfirmationRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     /**
      * @hide should be built with Builder
@@ -295,7 +297,8 @@
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             boolean isStrongBoxBacked,
-            boolean userConfirmationRequired) {
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         if (TextUtils.isEmpty(keyStoreAlias)) {
             throw new IllegalArgumentException("keyStoreAlias must not be empty");
         }
@@ -344,6 +347,7 @@
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mIsStrongBoxBacked = isStrongBoxBacked;
         mUserConfirmationRequired = userConfirmationRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -669,6 +673,22 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#setUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
+     * @hide
+     */
+    public long getBoundToSpecificSecureUserId() {
+        return GateKeeper.INVALID_SECURE_USER_ID;
+    }
+
+    /**
      * Builder of {@link KeyGenParameterSpec} instances.
      */
     public final static class Builder {
@@ -699,6 +719,7 @@
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mIsStrongBoxBacked = false;
         private boolean mUserConfirmationRequired;
+        private boolean mUnlockedDeviceRequired = false;
 
         /**
          * Creates a new instance of the {@code Builder}.
@@ -1267,6 +1288,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@code KeyGenParameterSpec}.
          */
         @NonNull
@@ -1297,7 +1330,8 @@
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mIsStrongBoxBacked,
-                    mUserConfirmationRequired);
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java
index b5b3281..22568ce 100644
--- a/keystore/java/android/security/keystore/KeyProtection.java
+++ b/keystore/java/android/security/keystore/KeyProtection.java
@@ -212,7 +212,7 @@
  * ...
  * }</pre>
  */
-public final class KeyProtection implements ProtectionParameter {
+public final class KeyProtection implements ProtectionParameter, UserAuthArgs {
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
     private final Date mKeyValidityForConsumptionEnd;
@@ -224,11 +224,13 @@
     private final boolean mRandomizedEncryptionRequired;
     private final boolean mUserAuthenticationRequired;
     private final int mUserAuthenticationValidityDurationSeconds;
+    private final boolean mTrustedUserPresenceRequired;
     private final boolean mUserAuthenticationValidWhileOnBody;
     private final boolean mInvalidatedByBiometricEnrollment;
     private final long mBoundToSecureUserId;
     private final boolean mCriticalToDeviceEncryption;
     private final boolean mUserConfirmationRequired;
+    private final boolean mUnlockedDeviceRequired;
 
     private KeyProtection(
             Date keyValidityStart,
@@ -242,11 +244,13 @@
             boolean randomizedEncryptionRequired,
             boolean userAuthenticationRequired,
             int userAuthenticationValidityDurationSeconds,
+            boolean trustedUserPresenceRequired,
             boolean userAuthenticationValidWhileOnBody,
             boolean invalidatedByBiometricEnrollment,
             long boundToSecureUserId,
             boolean criticalToDeviceEncryption,
-            boolean userConfirmationRequired) {
+            boolean userConfirmationRequired,
+            boolean unlockedDeviceRequired) {
         mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart);
         mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd);
         mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd);
@@ -260,11 +264,13 @@
         mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticationRequired = userAuthenticationRequired;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
+        mTrustedUserPresenceRequired = trustedUserPresenceRequired;
         mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody;
         mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment;
         mBoundToSecureUserId = boundToSecureUserId;
         mCriticalToDeviceEncryption = criticalToDeviceEncryption;
         mUserConfirmationRequired = userConfirmationRequired;
+        mUnlockedDeviceRequired = unlockedDeviceRequired;
     }
 
     /**
@@ -437,6 +443,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is authorized to be used only if a test of user presence has
+     * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls.
+     */
+    public boolean isTrustedUserPresenceRequired() {
+        return mTrustedUserPresenceRequired;
+    }
+
+    /**
      * Returns {@code true} if the key will be de-authorized when the device is removed from the
      * user's body.  This option has no effect on keys that don't have an authentication validity
      * duration, and has no effect if the device lacks an on-body sensor.
@@ -494,6 +508,15 @@
     }
 
     /**
+     * Returns {@code true} if the key cannot be used unless the device screen is unlocked.
+     *
+     * @see Builder#setUnlockedDeviceRequired(boolean)
+     */
+    public boolean isUnlockedDeviceRequired() {
+        return mUnlockedDeviceRequired;
+    }
+
+    /**
      * Builder of {@link KeyProtection} instances.
      */
     public final static class Builder {
@@ -509,9 +532,12 @@
         private boolean mRandomizedEncryptionRequired = true;
         private boolean mUserAuthenticationRequired;
         private int mUserAuthenticationValidityDurationSeconds = -1;
+        private boolean mTrustedUserPresenceRequired = false;
         private boolean mUserAuthenticationValidWhileOnBody;
         private boolean mInvalidatedByBiometricEnrollment = true;
         private boolean mUserConfirmationRequired;
+        private boolean mUnlockedDeviceRequired = false;
+
         private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID;
         private boolean mCriticalToDeviceEncryption = false;
 
@@ -811,6 +837,16 @@
         }
 
         /**
+         * Sets whether a test of user presence is required to be performed between the
+         * {@code Signature.initSign()} and {@code Signature.sign()} method calls.
+         */
+        @NonNull
+        public Builder setTrustedUserPresenceRequired(boolean required) {
+            mTrustedUserPresenceRequired = required;
+            return this;
+        }
+
+        /**
          * Sets whether the key will remain authorized only until the device is removed from the
          * user's body up to the limit of the authentication validity period (see
          * {@link #setUserAuthenticationValidityDurationSeconds} and
@@ -892,6 +928,18 @@
         }
 
         /**
+         * Sets whether the keystore requires the screen to be unlocked before allowing decryption
+         * using this key. If this is set to {@code true}, any attempt to decrypt using this key
+         * while the screen is locked will fail. A locked device requires a PIN, password,
+         * fingerprint, or other trusted factor to access.
+         */
+        @NonNull
+        public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) {
+            mUnlockedDeviceRequired = unlockedDeviceRequired;
+            return this;
+        }
+
+        /**
          * Builds an instance of {@link KeyProtection}.
          *
          * @throws IllegalArgumentException if a required field is missing
@@ -910,11 +958,13 @@
                     mRandomizedEncryptionRequired,
                     mUserAuthenticationRequired,
                     mUserAuthenticationValidityDurationSeconds,
+                    mTrustedUserPresenceRequired,
                     mUserAuthenticationValidWhileOnBody,
                     mInvalidatedByBiometricEnrollment,
                     mBoundToSecureUserId,
                     mCriticalToDeviceEncryption,
-                    mUserConfirmationRequired);
+                    mUserConfirmationRequired,
+                    mUnlockedDeviceRequired);
         }
     }
 }
diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java
index 4e28601..6e50121 100644
--- a/keystore/java/android/security/keystore/KeymasterUtils.java
+++ b/keystore/java/android/security/keystore/KeymasterUtils.java
@@ -16,7 +16,7 @@
 
 package android.security.keystore;
 
-import android.util.Log;
+import android.app.ActivityManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.security.GateKeeper;
 import android.security.KeyStore;
@@ -100,23 +100,27 @@
      *         state (e.g., secure lock screen not set up) for generating or importing keys that
      *         require user authentication.
      */
-    public static void addUserAuthArgs(KeymasterArguments args,
-            boolean userAuthenticationRequired,
-            int userAuthenticationValidityDurationSeconds,
-            boolean userAuthenticationValidWhileOnBody,
-            boolean invalidatedByBiometricEnrollment,
-            long boundToSpecificSecureUserId,
-            boolean userConfirmationRequired) {
-        if (userConfirmationRequired) {
+    public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) {
+        args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser());
+
+        if (spec.isUserConfirmationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
         }
 
-        if (!userAuthenticationRequired) {
+        if (spec.isTrustedUserPresenceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
+        }
+
+        if (spec.isUnlockedDeviceRequired()) {
+            args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED);
+        }
+
+        if (!spec.isUserAuthenticationRequired()) {
             args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
             return;
         }
 
-        if (userAuthenticationValidityDurationSeconds == -1) {
+        if (spec.getUserAuthenticationValidityDurationSeconds() == -1) {
             // Every use of this key needs to be authorized by the user. This currently means
             // fingerprint-only auth.
             FingerprintManager fingerprintManager =
@@ -132,9 +136,9 @@
             }
 
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
-            } else if (invalidatedByBiometricEnrollment) {
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
+            } else if (spec.isInvalidatedByBiometricEnrollment()) {
                 // The fingerprint-only SID will change on fingerprint enrollment or removal of all,
                 // enrolled fingerprints, invalidating the key.
                 sid = fingerprintOnlySid;
@@ -147,14 +151,14 @@
             args.addUnsignedLong(
                     KeymasterDefs.KM_TAG_USER_SECURE_ID, KeymasterArguments.toUint64(sid));
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, KeymasterDefs.HW_AUTH_FINGERPRINT);
-            if (userAuthenticationValidWhileOnBody) {
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 throw new ProviderException("Key validity extension while device is on-body is not "
                         + "supported for keys requiring fingerprint authentication");
             }
         } else {
             long sid;
-            if (boundToSpecificSecureUserId != GateKeeper.INVALID_SECURE_USER_ID) {
-                sid = boundToSpecificSecureUserId;
+            if (spec.getBoundToSpecificSecureUserId() != GateKeeper.INVALID_SECURE_USER_ID) {
+                sid = spec.getBoundToSpecificSecureUserId();
             } else {
                 // The key is authorized for use for the specified amount of time after the user has
                 // authenticated. Whatever unlocks the secure lock screen should authorize this key.
@@ -165,8 +169,8 @@
             args.addEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE,
                     KeymasterDefs.HW_AUTH_PASSWORD | KeymasterDefs.HW_AUTH_FINGERPRINT);
             args.addUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT,
-                    userAuthenticationValidityDurationSeconds);
-            if (userAuthenticationValidWhileOnBody) {
+                    spec.getUserAuthenticationValidityDurationSeconds());
+            if (spec.isUserAuthenticationValidWhileOnBody()) {
                 args.addBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
             }
         }
diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java
new file mode 100644
index 0000000..ad18ff8
--- /dev/null
+++ b/keystore/java/android/security/keystore/UserAuthArgs.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * @hide
+ *
+ * This is an interface to encapsulate the user authentication arguments that
+ * are passed to KeymasterUtils.addUserAuthArgs. Classes that represent
+ * authorization characteristics for new or imported keys can implement this
+ * interface to be passed to that method.
+ */
+public interface UserAuthArgs {
+
+    boolean isUserAuthenticationRequired();
+    int getUserAuthenticationValidityDurationSeconds();
+    boolean isUserAuthenticationValidWhileOnBody();
+    boolean isInvalidatedByBiometricEnrollment();
+    boolean isUserConfirmationRequired();
+    long getBoundToSpecificSecureUserId();
+    boolean isTrustedUserPresenceRequired();
+    boolean isUnlockedDeviceRequired();
+
+}
diff --git a/libs/hwui/PathParser.cpp b/libs/hwui/PathParser.cpp
index a48fdfc..47fcca9 100644
--- a/libs/hwui/PathParser.cpp
+++ b/libs/hwui/PathParser.cpp
@@ -156,10 +156,63 @@
     return;
 }
 
-bool PathParser::isVerbValid(char verb) {
-    verb = tolower(verb);
-    return verb == 'a' || verb == 'c' || verb == 'h' || verb == 'l' || verb == 'm' || verb == 'q' ||
-           verb == 's' || verb == 't' || verb == 'v' || verb == 'z';
+void PathParser::validateVerbAndPoints(char verb, size_t points, PathParser::ParseResult* result) {
+    size_t numberOfPointsExpected = -1;
+    switch (verb) {
+        case 'z':
+        case 'Z':
+            numberOfPointsExpected = 0;
+            break;
+        case 'm':
+        case 'l':
+        case 't':
+        case 'M':
+        case 'L':
+        case 'T':
+            numberOfPointsExpected = 2;
+            break;
+        case 'h':
+        case 'v':
+        case 'H':
+        case 'V':
+            numberOfPointsExpected = 1;
+            break;
+        case 'c':
+        case 'C':
+            numberOfPointsExpected = 6;
+            break;
+        case 's':
+        case 'q':
+        case 'S':
+        case 'Q':
+            numberOfPointsExpected = 4;
+            break;
+        case 'a':
+        case 'A':
+            numberOfPointsExpected = 7;
+            break;
+        default:
+            result->failureOccurred = true;
+            result->failureMessage += verb;
+            result->failureMessage += " is not a valid verb. ";
+            return;
+    }
+    if (numberOfPointsExpected == 0 && points == 0) {
+        return;
+    }
+    if (numberOfPointsExpected > 0 && points % numberOfPointsExpected == 0) {
+        return;
+    }
+
+    result->failureOccurred = true;
+    result->failureMessage += verb;
+    result->failureMessage += " needs to be followed by ";
+    if (numberOfPointsExpected > 0) {
+        result->failureMessage += "a multiple of ";
+    }
+    result->failureMessage += std::to_string(numberOfPointsExpected)
+            + " floats. However, " + std::to_string(points)
+            + " float(s) are found. ";
 }
 
 void PathParser::getPathDataFromAsciiString(PathData* data, ParseResult* result,
@@ -186,13 +239,11 @@
         end = nextStart(pathStr, strLen, end);
         std::vector<float> points;
         getFloats(&points, result, pathStr, start, end);
-        if (!isVerbValid(pathStr[start])) {
-            result->failureOccurred = true;
-            result->failureMessage = "Invalid pathData. Failure occurred at position " +
-                                     std::to_string(start) + " of path: " + pathStr;
-        }
-        // If either verb or points is not valid, return immediately.
+        validateVerbAndPoints(pathStr[start], points.size(), result);
         if (result->failureOccurred) {
+            // If either verb or points is not valid, return immediately.
+            result->failureMessage += "Failure occurred at position " +
+                                     std::to_string(start) + " of path: " + pathStr;
             return;
         }
         data->verbs.push_back(pathStr[start]);
@@ -203,9 +254,10 @@
     }
 
     if ((end - start) == 1 && start < strLen) {
-        if (!isVerbValid(pathStr[start])) {
-            result->failureOccurred = true;
-            result->failureMessage = "Invalid pathData. Failure occurred at position " +
+        validateVerbAndPoints(pathStr[start], 0, result);
+        if (result->failureOccurred) {
+            // If either verb or points is not valid, return immediately.
+            result->failureMessage += "Failure occurred at position " +
                                      std::to_string(start) + " of path: " + pathStr;
             return;
         }
diff --git a/libs/hwui/PathParser.h b/libs/hwui/PathParser.h
index e4ec7be..474eb97 100644
--- a/libs/hwui/PathParser.h
+++ b/libs/hwui/PathParser.h
@@ -43,7 +43,7 @@
     ANDROID_API static void getPathDataFromAsciiString(PathData* outData, ParseResult* result,
                                                        const char* pathStr, size_t strLength);
     static void dump(const PathData& data);
-    static bool isVerbValid(char verb);
+    static void validateVerbAndPoints(char verb, size_t points, ParseResult* result);
 };
 
 };      // namespace uirenderer
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fedcc10..d93a757 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -243,24 +243,11 @@
         return;
     }
 
-    if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
+    if (info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator, info.errorHandler)) {
         damageSelf(info);
     }
 
     if (!hasLayer()) {
-        Caches::getInstance().dumpMemoryUsage();
-        if (info.errorHandler) {
-            std::ostringstream err;
-            err << "Unable to create layer for " << getName();
-            const int maxTextureSize = Caches::getInstance().maxTextureSize;
-            if (getWidth() > maxTextureSize || getHeight() > maxTextureSize) {
-                err << ", size " << getWidth() << "x" << getHeight() << " exceeds max size "
-                    << maxTextureSize;
-            } else {
-                err << ", see logcat for more info";
-            }
-            info.errorHandler->onError(err.str());
-        }
         return;
     }
 
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index d9584db..293e45f 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -34,7 +34,8 @@
     }
 }
 
-bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer) {
+bool LayerDrawable::DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
+                              const SkRect* dstRect) {
     if (context == nullptr) {
         SkDEBUGF(("Attempting to draw LayerDrawable into an unsupported surface"));
         return false;
@@ -43,8 +44,8 @@
     SkMatrix layerTransform;
     layer->getTransform().copyTo(layerTransform);
     sk_sp<SkImage> layerImage;
-    int layerWidth = layer->getWidth();
-    int layerHeight = layer->getHeight();
+    const int layerWidth = layer->getWidth();
+    const int layerHeight = layer->getHeight();
     if (layer->getApi() == Layer::Api::OpenGL) {
         GlLayer* glLayer = static_cast<GlLayer*>(layer);
         GrGLTextureInfo externalTexture;
@@ -62,21 +63,21 @@
     }
 
     if (layerImage) {
-        SkMatrix textureMatrix;
-        layer->getTexTransform().copyTo(textureMatrix);
+        SkMatrix textureMatrixInv;
+        layer->getTexTransform().copyTo(textureMatrixInv);
         // TODO: after skia bug https://bugs.chromium.org/p/skia/issues/detail?id=7075 is fixed
         // use bottom left origin and remove flipV and invert transformations.
         SkMatrix flipV;
         flipV.setAll(1, 0, 0, 0, -1, 1, 0, 0, 1);
-        textureMatrix.preConcat(flipV);
-        textureMatrix.preScale(1.0f / layerWidth, 1.0f / layerHeight);
-        textureMatrix.postScale(layerWidth, layerHeight);
-        SkMatrix textureMatrixInv;
-        if (!textureMatrix.invert(&textureMatrixInv)) {
-            textureMatrixInv = textureMatrix;
+        textureMatrixInv.preConcat(flipV);
+        textureMatrixInv.preScale(1.0f / layerWidth, 1.0f / layerHeight);
+        textureMatrixInv.postScale(layerWidth, layerHeight);
+        SkMatrix textureMatrix;
+        if (!textureMatrixInv.invert(&textureMatrix)) {
+            textureMatrix = textureMatrixInv;
         }
 
-        SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrixInv);
+        SkMatrix matrix = SkMatrix::Concat(layerTransform, textureMatrix);
 
         SkPaint paint;
         paint.setAlpha(layer->getAlpha());
@@ -88,7 +89,20 @@
             canvas->save();
             canvas->concat(matrix);
         }
-        canvas->drawImage(layerImage.get(), 0, 0, &paint);
+        if (dstRect) {
+            SkMatrix matrixInv;
+            if (!matrix.invert(&matrixInv)) {
+                matrixInv = matrix;
+            }
+            SkRect srcRect = SkRect::MakeIWH(layerWidth, layerHeight);
+            matrixInv.mapRect(&srcRect);
+            SkRect skiaDestRect = *dstRect;
+            matrixInv.mapRect(&skiaDestRect);
+            canvas->drawImageRect(layerImage.get(), srcRect, skiaDestRect, &paint,
+                                  SkCanvas::kFast_SrcRectConstraint);
+        } else {
+            canvas->drawImage(layerImage.get(), 0, 0, &paint);
+        }
         // restore the original matrix
         if (nonIdentityMatrix) {
             canvas->restore();
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.h b/libs/hwui/pipeline/skia/LayerDrawable.h
index 3450387..18d1184 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.h
+++ b/libs/hwui/pipeline/skia/LayerDrawable.h
@@ -32,7 +32,8 @@
 public:
     explicit LayerDrawable(DeferredLayerUpdater* layerUpdater) : mLayerUpdater(layerUpdater) {}
 
-    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer);
+    static bool DrawLayer(GrContext* context, SkCanvas* canvas, Layer* layer,
+                          const SkRect* dstRect = nullptr);
 
 protected:
     virtual SkRect onGetBounds() override {
diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp
index 2fa56f6..6700748 100644
--- a/libs/hwui/pipeline/skia/ShaderCache.cpp
+++ b/libs/hwui/pipeline/skia/ShaderCache.cpp
@@ -83,10 +83,12 @@
     int maxTries = 3;
     while (valueSize > mObservedBlobValueSize && maxTries > 0) {
         mObservedBlobValueSize = std::min(valueSize, maxValueSize);
-        valueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
-        if (!valueBuffer) {
+        void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize);
+        if (!newValueBuffer) {
+            free(valueBuffer);
             return nullptr;
         }
+        valueBuffer = newValueBuffer;
         valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize);
         maxTries--;
     }
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 365d740..74cfb28 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -138,7 +138,9 @@
                                                               SkBudgeted::kYes, bitmap->info());
 
     Layer* layer = deferredLayer->backingLayer();
-    if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer)) {
+    const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
+    if (LayerDrawable::DrawLayer(mRenderThread.getGrContext(), tmpSurface->getCanvas(), layer,
+                                 &dstRect)) {
         sk_sp<SkImage> tmpImage = tmpSurface->makeImageSnapshot();
         if (tmpImage->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
             bitmap->notifyPixelsChanged();
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 0cd1c15..07052cd 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -22,6 +22,7 @@
 #include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
+#include "TreeInfo.h"
 #include "VectorDrawable.h"
 #include "utils/TraceUtils.h"
 
@@ -158,7 +159,7 @@
 }
 
 bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
-                                       bool wideColorGamut) {
+                                       bool wideColorGamut, ErrorHandler* errorHandler) {
     // compute the size of the surface (i.e. texture) to be allocated for this layer
     const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE;
     const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE;
@@ -182,6 +183,20 @@
             Matrix4 windowTransform;
             damageAccumulator.computeCurrentTransform(&windowTransform);
             node->getSkiaLayer()->inverseTransformInWindow = windowTransform;
+        } else {
+            String8 cachesOutput;
+            mRenderThread.cacheManager().dumpMemoryUsage(cachesOutput,
+                    &mRenderThread.renderState());
+            ALOGE("%s", cachesOutput.string());
+            if (errorHandler) {
+                std::ostringstream err;
+                err << "Unable to create layer for " << node->getName();
+                const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
+                err << ", size " << info.width() << "x" << info.height() << " max size "
+                    << maxTextureSize << " color type " << (int)info.colorType()
+                    << " has context " << (int)(mRenderThread.getGrContext() != nullptr);
+                errorHandler->onError(err.str());
+            }
         }
         return true;
     }
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h
index 3800194..38ad9c0 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.h
@@ -47,7 +47,7 @@
                       const BakedOpRenderer::LightInfo& lightInfo) override;
 
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
-                             bool wideColorGamut) override;
+                             bool wideColorGamut, ErrorHandler* errorHandler) override;
 
     void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip,
                      const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut,
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index d80a247..c2cc72a 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -49,6 +49,7 @@
 
 class AnimationContext;
 class DeferredLayerUpdater;
+class ErrorHandler;
 class Layer;
 class Rect;
 class RenderState;
@@ -74,8 +75,10 @@
      *
      *  @return true if the layer has been created or updated
      */
-    bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
-        return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut);
+    bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator,
+                             ErrorHandler* errorHandler) {
+        return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut,
+                errorHandler);
     }
 
     /**
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 246ab26..b1de497 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -31,6 +31,7 @@
 namespace uirenderer {
 
 class DeferredLayerUpdater;
+class ErrorHandler;
 
 namespace renderthread {
 
@@ -68,7 +69,7 @@
                               const BakedOpRenderer::LightInfo& lightInfo) = 0;
     virtual TaskManager* getTaskManager() = 0;
     virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
-                                     bool wideColorGamut) = 0;
+                                     bool wideColorGamut, ErrorHandler* errorHandler) = 0;
     virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0;
     virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0;
     virtual void unpinImages() = 0;
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index f3103fd..876af47 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -23,6 +23,7 @@
 #include "OpenGLReadback.h"
 #include "ProfileRenderer.h"
 #include "renderstate/RenderState.h"
+#include "TreeInfo.h"
 
 #include <cutils/properties.h>
 #include <strings.h>
@@ -202,7 +203,8 @@
 
 bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
                                          const DamageAccumulator& damageAccumulator,
-                                         bool wideColorGamut) {
+                                         bool wideColorGamut,
+                                         ErrorHandler* errorHandler) {
     RenderState& renderState = mRenderThread.renderState();
     OffscreenBufferPool& layerPool = renderState.layerPool();
     bool transformUpdateNeeded = false;
@@ -228,6 +230,22 @@
         node->getLayer()->setWindowTransform(windowTransform);
     }
 
+    if (!node->hasLayer()) {
+        Caches::getInstance().dumpMemoryUsage();
+        if (errorHandler) {
+            std::ostringstream err;
+            err << "Unable to create layer for " << node->getName();
+            const int maxTextureSize = Caches::getInstance().maxTextureSize;
+            if (node->getWidth() > maxTextureSize || node->getHeight() > maxTextureSize) {
+                err << ", size " << node->getWidth() << "x" << node->getHeight()
+                    << " exceeds max size " << maxTextureSize;
+            } else {
+                err << ", see logcat for more info";
+            }
+            errorHandler->onError(err.str());
+        }
+    }
+
     return transformUpdateNeeded;
 }
 
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index 118007c..9859e93 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -53,7 +53,7 @@
                       const BakedOpRenderer::LightInfo& lightInfo) override;
     TaskManager* getTaskManager() override;
     bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator,
-                             bool wideColorGamut) override;
+                             bool wideColorGamut, ErrorHandler* errorHandler) override;
     bool pinImages(std::vector<SkImage*>& mutableImages) override { return false; }
     bool pinImages(LsaVector<sk_sp<Bitmap>>& images) override;
     void unpinImages() override;
diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp
index e7496f7..02f740c 100644
--- a/libs/hwui/tests/unit/VectorDrawableTests.cpp
+++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp
@@ -242,7 +242,8 @@
         {"\n \t   z", true},   // Valid path data with leading spaces
         {"1-2e34567", false},  // Not starting with a verb and ill-formatted float
         {"f 4 5", false},      // Invalid verb
-        {"\r      ", false}    // Empty string
+        {"\r      ", false},   // Empty string
+        {"L1,0 L1,1 L0,1 z M1000", false}    // Not enough floats following verb M.
 };
 
 static bool hasSameVerbs(const PathData& from, const PathData& to) {
diff --git a/libs/incident/proto/android/privacy.proto b/libs/incident/proto/android/privacy.proto
index f29f57f..1ef36df 100644
--- a/libs/incident/proto/android/privacy.proto
+++ b/libs/incident/proto/android/privacy.proto
@@ -16,13 +16,13 @@
 
 syntax = "proto2";
 
-option java_package = "android";
+package android;
+
+option java_package = "com.android.incident";
 option java_multiple_files = true;
 
 import "google/protobuf/descriptor.proto";
 
-package android;
-
 enum Destination {
     // Fields or messages annotated with DEST_LOCAL must never be
     // extracted from the device automatically. They can be accessed
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
index 98e67c2..3643ca4 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
@@ -224,7 +224,9 @@
     s.append("GNSS_KPI_END").append("\n");
     GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
     if (stats != null) {
-      s.append("Power Metrics").append('\n');
+      s.append("Power Metrics").append("\n");
+      s.append("  Time on battery (min): "
+          + stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
       long[] t = stats.getTimeInGpsSignalQualityLevel();
       if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) {
         s.append("  Amount of time (while on battery) Top 4 Avg CN0 > " +
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index 86dfc9c..ca895fc 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -226,11 +226,10 @@
     }
 
     /**
-     * @hide
      * @return The "address" string of the device. This generally contains device-specific
      * parameters.
      */
-    public String getAddress() {
+    public @NonNull String getAddress() {
         return mPort.address();
     }
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e408a11..aeef215 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4596,8 +4596,7 @@
 
     private static boolean checkTypes(AudioDevicePort port) {
         return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) !=
-                    AudioDeviceInfo.TYPE_UNKNOWN &&
-                port.type() != AudioSystem.DEVICE_IN_BACK_MIC;
+                    AudioDeviceInfo.TYPE_UNKNOWN;
     }
 
     /**
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index cc7722a..66759e5 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -59,7 +59,7 @@
     public boolean supports(MediaFormat format) {
         if (format.containsKey(MediaFormat.KEY_MIME)) {
             String mimeType = format.getString(MediaFormat.KEY_MIME);
-            return MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType);
+            return MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType);
         }
         return false;
     }
@@ -67,7 +67,7 @@
     @Override
     public SubtitleTrack createTrack(MediaFormat format) {
         String mimeType = format.getString(MediaFormat.KEY_MIME);
-        if (MediaPlayer.MEDIA_MIMETYPE_TEXT_CEA_608.equals(mimeType)) {
+        if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mimeType)) {
             if (mCCWidget == null) {
                 mCCWidget = new Cea608CCWidget(mContext);
             }
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 56edace..72d52d3 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -727,18 +727,7 @@
             return false;
         }
 
-        if (format == ImageFormat.PRIVATE) {
-            // Usage need to be either USAGE0_GPU_SAMPLED_IMAGE or USAGE0_VIDEO_ENCODE or combined.
-            boolean isAllowed = (usage == HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
-            isAllowed = isAllowed || (usage == HardwareBuffer.USAGE_VIDEO_ENCODE);
-            isAllowed = isAllowed || (usage ==
-                    (HardwareBuffer.USAGE_VIDEO_ENCODE | HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE));
-            return isAllowed;
-        } else {
-            // Usage need to make the buffer CPU readable for explicit format.
-            return ((usage == HardwareBuffer.USAGE_CPU_READ_RARELY) ||
-                    (usage == HardwareBuffer.USAGE_CPU_READ_OFTEN));
-        }
+        return true;
     }
 
     /**
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index e0c567b..5173743 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -654,9 +654,11 @@
      * ID is returned.
      *
      * @param level the new security level, one of
-     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
-     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
-     * {@link #HW_SECURE_ALL}.
+     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO},
+     * {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO},
+     * {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
      *
      * @throws NotProvisionedException if provisioning is needed
      * @throws ResourceBusyException if required resources are in use
@@ -703,7 +705,9 @@
     public @interface KeyType {}
 
     /**
-     * Contains the opaque data an app uses to request keys from a license server
+     * Contains the opaque data an app uses to request keys from a license server.
+     * These request types may or may not be generated by a given plugin. Refer
+     * to plugin vendor documentation for more information.
      */
     public static final class KeyRequest {
         private byte[] mData;
@@ -728,8 +732,8 @@
         public static final int REQUEST_TYPE_RELEASE = 2;
 
         /**
-         * Keys are already loaded. No license request is necessary, and no
-         * key request data is returned.
+         * Keys are already loaded and are available for use. No license request is necessary, and
+         * no key request data is returned.
          */
         public static final int REQUEST_TYPE_NONE = 3;
 
@@ -1140,8 +1144,9 @@
      * implementation.
      */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({SECURITY_LEVEL_UNKNOWN, SW_SECURE_CRYPTO, SW_SECURE_DECODE,
-            HW_SECURE_CRYPTO, HW_SECURE_DECODE, HW_SECURE_ALL})
+    @IntDef({SECURITY_LEVEL_UNKNOWN, SECURITY_LEVEL_SW_SECURE_CRYPTO,
+            SECURITY_LEVEL_SW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_CRYPTO,
+            SECURITY_LEVEL_HW_SECURE_DECODE, SECURITY_LEVEL_HW_SECURE_ALL})
     public @interface SecurityLevel {}
 
     /**
@@ -1153,31 +1158,31 @@
     /**
      * DRM key management uses software-based whitebox crypto.
      */
-    public static final int SW_SECURE_CRYPTO = 1;
+    public static final int SECURITY_LEVEL_SW_SECURE_CRYPTO = 1;
 
     /**
      * DRM key management and decoding use software-based whitebox crypto.
      */
-    public static final int SW_SECURE_DECODE = 2;
+    public static final int SECURITY_LEVEL_SW_SECURE_DECODE = 2;
 
     /**
      * DRM key management and crypto operations are performed within a hardware
      * backed trusted execution environment.
      */
-    public static final int HW_SECURE_CRYPTO = 3;
+    public static final int SECURITY_LEVEL_HW_SECURE_CRYPTO = 3;
 
     /**
      * DRM key management, crypto operations and decoding of content are
      * performed within a hardware backed trusted execution environment.
      */
-    public static final int HW_SECURE_DECODE = 4;
+    public static final int SECURITY_LEVEL_HW_SECURE_DECODE = 4;
 
     /**
      * DRM key management, crypto operations, decoding of content and all
      * handling of the media (compressed and uncompressed) is handled within a
      * hardware backed trusted execution environment.
      */
-    public static final int HW_SECURE_ALL = 5;
+    public static final int SECURITY_LEVEL_HW_SECURE_ALL = 5;
 
     /**
      * The maximum security level supported by the device. This is the default
@@ -1203,9 +1208,9 @@
      * @param sessionId the session to query.
      * <p>
      * @return one of {@link #SECURITY_LEVEL_UNKNOWN},
-     * {@link #SW_SECURE_CRYPTO}, {@link #SW_SECURE_DECODE},
-     * {@link #HW_SECURE_CRYPTO}, {@link #HW_SECURE_DECODE} or
-     * {@link #HW_SECURE_ALL}.
+     * {@link #SECURITY_LEVEL_SW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_SW_SECURE_DECODE},
+     * {@link #SECURITY_LEVEL_HW_SECURE_CRYPTO}, {@link #SECURITY_LEVEL_HW_SECURE_DECODE} or
+     * {@link #SECURITY_LEVEL_HW_SECURE_ALL}.
      */
     @SecurityLevel
     public native int getSecurityLevel(@NonNull byte[] sessionId);
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index 3bfbcc2..384326f 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -174,10 +174,20 @@
     public static final String MIMETYPE_TEXT_VTT = "text/vtt";
 
     /**
+     * MIME type for SubRip (SRT) container.
+     */
+    public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+
+    /**
      * MIME type for CEA-608 closed caption data.
      */
     public static final String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
 
+    /**
+     * MIME type for CEA-708 closed caption data.
+     */
+    public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+
     private Map<String, Object> mMap;
 
     /**
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index fe5e822..aef31b1 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2416,7 +2416,7 @@
          * Gets the track type.
          * @return TrackType which indicates if the track is video, audio, timed text.
          */
-        public int getTrackType() {
+        public @TrackType int getTrackType() {
             return mTrackType;
         }
 
@@ -2450,6 +2450,19 @@
         public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
         public static final int MEDIA_TRACK_TYPE_METADATA = 5;
 
+        /** @hide */
+        @IntDef(flag = false, prefix = "MEDIA_TRACK_TYPE", value = {
+                MEDIA_TRACK_TYPE_UNKNOWN,
+                MEDIA_TRACK_TYPE_VIDEO,
+                MEDIA_TRACK_TYPE_AUDIO,
+                MEDIA_TRACK_TYPE_TIMEDTEXT,
+                MEDIA_TRACK_TYPE_SUBTITLE,
+                MEDIA_TRACK_TYPE_METADATA }
+        )
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface TrackType {}
+
+
         final int mTrackType;
         final MediaFormat mFormat;
 
@@ -2600,26 +2613,30 @@
      */
     /**
      * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
+     * @deprecated use {@link MediaFormat#MIMETYPE_TEXT_SUBRIP}
      */
-    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = MediaFormat.MIMETYPE_TEXT_SUBRIP;
 
     /**
      * MIME type for WebVTT subtitle data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
+    public static final String MEDIA_MIMETYPE_TEXT_VTT = MediaFormat.MIMETYPE_TEXT_VTT;
 
     /**
      * MIME type for CEA-608 closed caption data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
+    public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = MediaFormat.MIMETYPE_TEXT_CEA_608;
 
     /**
      * MIME type for CEA-708 closed caption data.
      * @hide
+     * @deprecated
      */
-    public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708";
+    public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = MediaFormat.MIMETYPE_TEXT_CEA_708;
 
     /*
      * A helper function to check if the mime type is supported by media framework.
@@ -3108,7 +3125,7 @@
      * this function is called.
      * </p>
      * <p>
-     * Currently, only timed text tracks or audio tracks can be selected via this method.
+     * Currently, only timed text, subtitle or audio tracks can be selected via this method.
      * In addition, the support for selecting an audio track at runtime is pretty limited
      * in that an audio track can only be selected in the <em>Prepared</em> state.
      * </p>
@@ -3795,29 +3812,158 @@
     private OnTimedTextListener mOnTimedTextListener;
 
     /**
-     * Interface definition of a callback to be invoked when a
-     * track has data available.
-     *
-     * @hide
+     * Interface definition of a callback to be invoked when a player subtitle track has new
+     * subtitle data available.
+     * See the {@link MediaPlayer#setOnSubtitleDataListener(OnSubtitleDataListener, Handler)}
+     * method for the description of which track will report data through this listener.
      */
-    public interface OnSubtitleDataListener
-    {
-        public void onSubtitleData(MediaPlayer mp, SubtitleData data);
+    public interface OnSubtitleDataListener {
+        /**
+         * Method called when new subtitle data is available
+         * @param mp the player that reports the new subtitle data
+         * @param data the subtitle data
+         */
+        public void onSubtitleData(@NonNull MediaPlayer mp, @NonNull SubtitleData data);
     }
 
     /**
-     * Register a callback to be invoked when a track has data available.
-     *
-     * @param listener the callback that will be run
-     *
-     * @hide
+     * Sets the listener to be invoked when a subtitle track has new data available.
+     * The subtitle data comes from a subtitle track previously selected with
+     * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are
+     * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings
+     * can be determined by {@link TrackInfo#getFormat()}).<br>
+     * See {@link SubtitleData} for an example of querying subtitle encoding.
+     * @param listener the listener called when new data is available
+     * @param handler the {@link Handler} that receives the listener events
      */
-    public void setOnSubtitleDataListener(OnSubtitleDataListener listener)
+    public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener,
+            @NonNull Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Illegal null handler");
+        }
+        setOnSubtitleDataListenerInt(listener, handler);
+    }
+    /**
+     * Sets the listener to be invoked when a subtitle track has new data available.
+     * The subtitle data comes from a subtitle track previously selected with
+     * {@link #selectTrack(int)}. Use {@link #getTrackInfo()} to determine which tracks are
+     * subtitles (of type {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}), Subtitle track encodings
+     * can be determined by {@link TrackInfo#getFormat()}).<br>
+     * See {@link SubtitleData} for an example of querying subtitle encoding.<br>
+     * The listener will be called on the same thread as the one in which the MediaPlayer was
+     * created.
+     * @param listener the listener called when new data is available
+     */
+    public void setOnSubtitleDataListener(@NonNull OnSubtitleDataListener listener)
     {
-        mOnSubtitleDataListener = listener;
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        setOnSubtitleDataListenerInt(listener, null);
+    }
+
+    /**
+     * Clears the listener previously set with
+     * {@link #setOnSubtitleDataListener(OnSubtitleDataListener)} or
+     * {@link #setOnSubtitleDataListener(OnSubtitleDataListener, Handler)}.
+     */
+    public void clearOnSubtitleDataListener() {
+        setOnSubtitleDataListenerInt(null, null);
+    }
+
+    private void setOnSubtitleDataListenerInt(
+            @Nullable OnSubtitleDataListener listener, @Nullable Handler handler) {
+        synchronized (this) {
+            mOnSubtitleDataListener = listener;
+            mOnSubtitleDataHandler = handler;
+        }
     }
 
     private OnSubtitleDataListener mOnSubtitleDataListener;
+    private Handler mOnSubtitleDataHandler;
+
+    /**
+     * Interface definition of a callback to be invoked when discontinuity in the normal progression
+     * of the media time is detected.
+     * The "normal progression" of media time is defined as the expected increase of the playback
+     * position when playing media, relative to the playback speed (for instance every second, media
+     * time increases by two seconds when playing at 2x).<br>
+     * Discontinuities are encountered in the following cases:
+     * <ul>
+     * <li>when the player is starved for data and cannot play anymore</li>
+     * <li>when the player encounters a playback error</li>
+     * <li>when the a seek operation starts, and when it's completed</li>
+     * <li>when the playback speed changes</li>
+     * <li>when the playback state changes</li>
+     * <li>when the player is reset</li>
+     * </ul>
+     * See the
+     * {@link MediaPlayer#setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)}
+     * method to set a listener for these events.
+     */
+    public interface OnMediaTimeDiscontinuityListener {
+        /**
+         * Called to indicate a time discontinuity has occured.
+         * @param mp the MediaPlayer for which the discontinuity has occured.
+         * @param mts the timestamp that correlates media time, system time and clock rate,
+         *     or {@link MediaTimestamp#TIMESTAMP_UNKNOWN} in an error case.
+         */
+        public void onMediaTimeDiscontinuity(@NonNull MediaPlayer mp, @NonNull MediaTimestamp mts);
+    }
+
+    /**
+     * Sets the listener to be invoked when a media time discontinuity is encountered.
+     * @param listener the listener called after a discontinuity
+     * @param handler the {@link Handler} that receives the listener events
+     */
+    public void setOnMediaTimeDiscontinuityListener(
+            @NonNull OnMediaTimeDiscontinuityListener listener, @NonNull Handler handler) {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("Illegal null handler");
+        }
+        setOnMediaTimeDiscontinuityListenerInt(listener, handler);
+    }
+
+    /**
+     * Sets the listener to be invoked when a media time discontinuity is encountered.
+     * The listener will be called on the same thread as the one in which the MediaPlayer was
+     * created.
+     * @param listener the listener called after a discontinuity
+     */
+    public void setOnMediaTimeDiscontinuityListener(
+            @NonNull OnMediaTimeDiscontinuityListener listener)
+    {
+        if (listener == null) {
+            throw new IllegalArgumentException("Illegal null listener");
+        }
+        setOnMediaTimeDiscontinuityListenerInt(listener, null);
+    }
+
+    /**
+     * Clears the listener previously set with
+     * {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener)}
+     * or {@link #setOnMediaTimeDiscontinuityListener(OnMediaTimeDiscontinuityListener, Handler)}
+     */
+    public void clearOnMediaTimeDiscontinuityListener() {
+        setOnMediaTimeDiscontinuityListenerInt(null, null);
+    }
+
+    private void setOnMediaTimeDiscontinuityListenerInt(
+            @Nullable OnMediaTimeDiscontinuityListener listener, @Nullable Handler handler) {
+        synchronized (this) {
+            mOnMediaTimeDiscontinuityListener = listener;
+            mOnMediaTimeDiscontinuityHandler = handler;
+        }
+    }
+
+    private OnMediaTimeDiscontinuityListener mOnMediaTimeDiscontinuityListener;
+    private Handler mOnMediaTimeDiscontinuityHandler;
 
     /**
      * Interface definition of a callback to be invoked when a
@@ -3953,8 +4099,8 @@
 
     /** The player was started because it was used as the next player for another
      * player, which just completed playback.
+     * @see android.media.MediaPlayer#setNextMediaPlayer(MediaPlayer)
      * @see android.media.MediaPlayer.OnInfoListener
-     * @hide
      */
     public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
 
diff --git a/media/java/android/media/MediaTimestamp.java b/media/java/android/media/MediaTimestamp.java
index 5ea6bbe..938dd14 100644
--- a/media/java/android/media/MediaTimestamp.java
+++ b/media/java/android/media/MediaTimestamp.java
@@ -37,6 +37,11 @@
 public final class MediaTimestamp
 {
     /**
+     * An unknown media timestamp value
+     */
+    public static final MediaTimestamp TIMESTAMP_UNKNOWN = new MediaTimestamp(-1, -1, 0.0f);
+
+    /**
      * Get the media time of the anchor in microseconds.
      */
     public long getAnchorMediaTimeUs() {
@@ -82,4 +87,15 @@
         nanoTime = 0;
         clockRate = 1.0f;
     }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+
+        final MediaTimestamp that = (MediaTimestamp) obj;
+        return (this.mediaTimeUs == that.mediaTimeUs)
+                && (this.nanoTime == that.nanoTime)
+                && (this.clockRate == that.clockRate);
+    }
 }
diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java
index 004efea..d6399a4 100644
--- a/media/java/android/media/MicrophoneInfo.java
+++ b/media/java/android/media/MicrophoneInfo.java
@@ -17,6 +17,7 @@
 package android.media;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.util.Pair;
 
 import java.lang.annotation.Retention;
@@ -224,12 +225,11 @@
     }
 
     /**
-     * @hide
      * Returns The "address" string of the microphone that corresponds to the
      * address returned by {@link AudioDeviceInfo#getAddress()}
      * @return the address of the microphone
      */
-    public String getAddress() {
+    public @NonNull String getAddress() {
         return mAddress;
     }
 
diff --git a/media/java/android/media/SubtitleData.java b/media/java/android/media/SubtitleData.java
index 3e6f6f9..9797828 100644
--- a/media/java/android/media/SubtitleData.java
+++ b/media/java/android/media/SubtitleData.java
@@ -16,26 +16,50 @@
 
 package android.media;
 
+import android.annotation.NonNull;
 import android.os.Parcel;
 
 /**
- * @hide
- *
- * Class to hold the subtitle track's data, including:
+ * Class encapsulating subtitle data, as received through the
+ * {@link MediaPlayer.OnSubtitleDataListener} interface.
+ * The subtitle data includes:
  * <ul>
- * <li> Track index</li>
- * <li> Start time (in microseconds) of the data</li>
- * <li> Duration (in microseconds) of the data</li>
- * <li> A byte-array of the data</li>
+ * <li> the track index</li>
+ * <li> the start time (in microseconds) of the data</li>
+ * <li> the duration (in microseconds) of the data</li>
+ * <li> the actual data.</li>
  * </ul>
- *
- * <p> To receive the subtitle data, applications need to do the following:
- *
- * <ul>
- * <li> Select a track of type MEDIA_TRACK_TYPE_SUBTITLE with {@link MediaPlayer.selectTrack(int)</li>
- * <li> Implement the {@link MediaPlayer.OnSubtitleDataListener} interface</li>
- * <li> Register the {@link MediaPlayer.OnSubtitleDataListener} callback on a MediaPlayer object</li>
- * </ul>
+ * The data is stored in a byte-array, and is encoded in one of the supported in-band
+ * subtitle formats. The subtitle encoding is determined by the MIME type of the
+ * {@link MediaPlayer.TrackInfo} of the subtitle track, one of
+ * {@link MediaFormat#MIMETYPE_TEXT_CEA_608}, {@link MediaFormat#MIMETYPE_TEXT_CEA_708},
+ * {@link MediaFormat#MIMETYPE_TEXT_VTT}.
+ * <p>
+ * Here is an example of iterating over the tracks of a {@link MediaPlayer}, and checking which
+ * encoding is used for the subtitle tracks:
+ * <p>
+ * <pre class="prettyprint">
+ * MediaPlayer mp = new MediaPlayer();
+ * mp.setDataSource(myContentLocation);
+ * mp.prepare(); // synchronous prepare, ready to use when method returns
+ * final TrackInfo[] trackInfos = mp.getTrackInfo();
+ * for (TrackInfo info : trackInfo) {
+ *     if (info.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
+ *         final String mime = info.getFormat().getString(MediaFormat.KEY_MIME);
+ *         if (MediaFormat.MIMETYPE_TEXT_CEA_608.equals(mime) {
+ *             // subtitle encoding is CEA 608
+ *         } else if (MediaFormat.MIMETYPE_TEXT_CEA_708.equals(mime) {
+ *             // subtitle encoding is CEA 708
+ *         } else if (MediaFormat.MIMETYPE_TEXT_VTT.equals(mime) {
+ *             // subtitle encoding is WebVTT
+ *         }
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * See
+ * {@link MediaPlayer#setOnSubtitleDataListener(android.media.MediaPlayer.OnSubtitleDataListener, android.os.Handler)}
+ * to receive subtitle data from a MediaPlayer object.
  *
  * @see android.media.MediaPlayer
  */
@@ -48,25 +72,47 @@
     private long mDurationUs;
     private byte[] mData;
 
+    /** @hide */
     public SubtitleData(Parcel parcel) {
         if (!parseParcel(parcel)) {
             throw new IllegalArgumentException("parseParcel() fails");
         }
     }
 
+    /**
+     * Returns the index of the MediaPlayer track which contains this subtitle data.
+     * @return an index in the array returned by {@link MediaPlayer#getTrackInfo()}.
+     */
     public int getTrackIndex() {
         return mTrackIndex;
     }
 
+    /**
+     * Returns the media time at which the subtitle should be displayed, expressed in microseconds.
+     * @return the display start time for the subtitle
+     */
     public long getStartTimeUs() {
         return mStartTimeUs;
     }
 
+    /**
+     * Returns the duration in microsecond during which the subtitle should be displayed.
+     * @return the display duration for the subtitle
+     */
     public long getDurationUs() {
         return mDurationUs;
     }
 
-    public byte[] getData() {
+    /**
+     * Returns the encoded data for the subtitle content.
+     * Encoding format depends on the subtitle type, refer to
+     * <a href="https://en.wikipedia.org/wiki/CEA-708">CEA 708</a>,
+     * <a href="https://en.wikipedia.org/wiki/EIA-608">CEA/EIA 608</a> and
+     * <a href="https://www.w3.org/TR/webvtt1/">WebVTT</a>, defined by the MIME type
+     * of the subtitle track.
+     * @return the encoded subtitle data
+     */
+    public @NonNull byte[] getData() {
         return mData;
     }
 
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 3affee5c0..bd0019f 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -50,6 +50,4 @@
     void setPlaybackToLocal(in AudioAttributes attributes);
     void setPlaybackToRemote(int control, int max);
     void setCurrentVolume(int currentVolume);
-
-    String getCallingPackage();
 }
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index 893bd3c..9634c7f 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -25,30 +25,33 @@
  * @hide
  */
 oneway interface ISessionCallback {
-    void onCommand(String command, in Bundle args, in ResultReceiver cb);
-    void onMediaButton(in Intent mediaButtonIntent, int sequenceNumber, in ResultReceiver cb);
+    void onCommand(String packageName, int pid, int uid, String command, in Bundle args,
+            in ResultReceiver cb);
+    void onMediaButton(String packageName, int pid, int uid, in Intent mediaButtonIntent,
+            int sequenceNumber, in ResultReceiver cb);
 
     // These callbacks are for the TransportPerformer
-    void onPrepare();
-    void onPrepareFromMediaId(String mediaId, in Bundle extras);
-    void onPrepareFromSearch(String query, in Bundle extras);
-    void onPrepareFromUri(in Uri uri, in Bundle extras);
-    void onPlay();
-    void onPlayFromMediaId(String mediaId, in Bundle extras);
-    void onPlayFromSearch(String query, in Bundle extras);
-    void onPlayFromUri(in Uri uri, in Bundle extras);
-    void onSkipToTrack(long id);
-    void onPause();
-    void onStop();
-    void onNext();
-    void onPrevious();
-    void onFastForward();
-    void onRewind();
-    void onSeekTo(long pos);
-    void onRate(in Rating rating);
-    void onCustomAction(String action, in Bundle args);
+    void onPrepare(String packageName, int pid, int uid);
+    void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+            in Bundle extras);
+    void onPrepareFromSearch(String packageName, int pid, int uid, String query, in Bundle extras);
+    void onPrepareFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras);
+    void onPlay(String packageName, int pid, int uid);
+    void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId, in Bundle extras);
+    void onPlayFromSearch(String packageName, int pid, int uid, String query, in Bundle extras);
+    void onPlayFromUri(String packageName, int pid, int uid, in Uri uri, in Bundle extras);
+    void onSkipToTrack(String packageName, int pid, int uid, long id);
+    void onPause(String packageName, int pid, int uid);
+    void onStop(String packageName, int pid, int uid);
+    void onNext(String packageName, int pid, int uid);
+    void onPrevious(String packageName, int pid, int uid);
+    void onFastForward(String packageName, int pid, int uid);
+    void onRewind(String packageName, int pid, int uid);
+    void onSeekTo(String packageName, int pid, int uid, long pos);
+    void onRate(String packageName, int pid, int uid, in Rating rating);
+    void onCustomAction(String packageName, int pid, int uid, String action, in Bundle args);
 
     // These callbacks are for volume handling
-    void onAdjustVolume(int direction);
-    void onSetVolumeTo(int value);
+    void onAdjustVolume(String packageName, int pid, int uid, int direction);
+    void onSetVolumeTo(String packageName, int pid, int uid, int value);
 }
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 249bcdc..06f5863 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -36,8 +36,8 @@
  * @hide
  */
 interface ISessionController {
-    void sendCommand(String command, in Bundle args, in ResultReceiver cb);
-    boolean sendMediaButton(in KeyEvent mediaButton);
+    void sendCommand(String packageName, String command, in Bundle args, in ResultReceiver cb);
+    boolean sendMediaButton(String packageName, in KeyEvent mediaButton);
     void registerCallbackListener(in ISessionControllerCallback cb);
     void unregisterCallbackListener(in ISessionControllerCallback cb);
     boolean isTransportControlEnabled();
@@ -46,28 +46,28 @@
     PendingIntent getLaunchPendingIntent();
     long getFlags();
     ParcelableVolumeInfo getVolumeAttributes();
-    void adjustVolume(int direction, int flags, String packageName);
-    void setVolumeTo(int value, int flags, String packageName);
+    void adjustVolume(String packageName, int direction, int flags);
+    void setVolumeTo(String packageName, int value, int flags);
 
     // These commands are for the TransportControls
-    void prepare();
-    void prepareFromMediaId(String mediaId, in Bundle extras);
-    void prepareFromSearch(String string, in Bundle extras);
-    void prepareFromUri(in Uri uri, in Bundle extras);
-    void play();
-    void playFromMediaId(String mediaId, in Bundle extras);
-    void playFromSearch(String string, in Bundle extras);
-    void playFromUri(in Uri uri, in Bundle extras);
-    void skipToQueueItem(long id);
-    void pause();
-    void stop();
-    void next();
-    void previous();
-    void fastForward();
-    void rewind();
-    void seekTo(long pos);
-    void rate(in Rating rating);
-    void sendCustomAction(String action, in Bundle args);
+    void prepare(String packageName);
+    void prepareFromMediaId(String packageName, String mediaId, in Bundle extras);
+    void prepareFromSearch(String packageName, String string, in Bundle extras);
+    void prepareFromUri(String packageName, in Uri uri, in Bundle extras);
+    void play(String packageName);
+    void playFromMediaId(String packageName, String mediaId, in Bundle extras);
+    void playFromSearch(String packageName, String string, in Bundle extras);
+    void playFromUri(String packageName, in Uri uri, in Bundle extras);
+    void skipToQueueItem(String packageName, long id);
+    void pause(String packageName);
+    void stop(String packageName);
+    void next(String packageName);
+    void previous(String packageName);
+    void fastForward(String packageName);
+    void rewind(String packageName);
+    void seekTo(String packageName, long pos);
+    void rate(String packageName, in Rating rating);
+    void sendCustomAction(String packageName, String action, in Bundle args);
     MediaMetadata getMetadata();
     PlaybackState getPlaybackState();
     ParceledListSlice getQueue();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 622900f..f16804c 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -133,7 +133,7 @@
             return false;
         }
         try {
-            return mSessionBinder.sendMediaButton(keyEvent);
+            return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent);
         } catch (RemoteException e) {
             // System is dead. =(
         }
@@ -301,7 +301,7 @@
      */
     public void setVolumeTo(int value, int flags) {
         try {
-            mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
+            mSessionBinder.setVolumeTo(mContext.getPackageName(), value, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling setVolumeTo.", e);
         }
@@ -322,7 +322,7 @@
      */
     public void adjustVolume(int direction, int flags) {
         try {
-            mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
+            mSessionBinder.adjustVolume(mContext.getPackageName(), direction, flags);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
         }
@@ -388,7 +388,7 @@
             throw new IllegalArgumentException("command cannot be null or empty");
         }
         try {
-            mSessionBinder.sendCommand(command, args, cb);
+            mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb);
         } catch (RemoteException e) {
             Log.d(TAG, "Dead object in sendCommand.", e);
         }
@@ -600,7 +600,7 @@
          */
         public void prepare() {
             try {
-                mSessionBinder.prepare();
+                mSessionBinder.prepare(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare.", e);
             }
@@ -624,7 +624,7 @@
                         "You must specify a non-empty String for prepareFromMediaId.");
             }
             try {
-                mSessionBinder.prepareFromMediaId(mediaId, extras);
+                mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
             }
@@ -650,7 +650,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.prepareFromSearch(query, extras);
+                mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
             }
@@ -674,7 +674,7 @@
                         "You must specify a non-empty Uri for prepareFromUri.");
             }
             try {
-                mSessionBinder.prepareFromUri(uri, extras);
+                mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
             }
@@ -685,7 +685,7 @@
          */
         public void play() {
             try {
-                mSessionBinder.play();
+                mSessionBinder.play(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play.", e);
             }
@@ -704,7 +704,7 @@
                         "You must specify a non-empty String for playFromMediaId.");
             }
             try {
-                mSessionBinder.playFromMediaId(mediaId, extras);
+                mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
             }
@@ -726,7 +726,7 @@
                 query = "";
             }
             try {
-                mSessionBinder.playFromSearch(query, extras);
+                mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + query + ").", e);
             }
@@ -745,7 +745,7 @@
                         "You must specify a non-empty Uri for playFromUri.");
             }
             try {
-                mSessionBinder.playFromUri(uri, extras);
+                mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling play(" + uri + ").", e);
             }
@@ -757,7 +757,7 @@
          */
         public void skipToQueueItem(long id) {
             try {
-                mSessionBinder.skipToQueueItem(id);
+                mSessionBinder.skipToQueueItem(mContext.getPackageName(), id);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
             }
@@ -769,7 +769,7 @@
          */
         public void pause() {
             try {
-                mSessionBinder.pause();
+                mSessionBinder.pause(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling pause.", e);
             }
@@ -781,7 +781,7 @@
          */
         public void stop() {
             try {
-                mSessionBinder.stop();
+                mSessionBinder.stop(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling stop.", e);
             }
@@ -794,7 +794,7 @@
          */
         public void seekTo(long pos) {
             try {
-                mSessionBinder.seekTo(pos);
+                mSessionBinder.seekTo(mContext.getPackageName(), pos);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling seekTo.", e);
             }
@@ -806,7 +806,7 @@
          */
         public void fastForward() {
             try {
-                mSessionBinder.fastForward();
+                mSessionBinder.fastForward(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling fastForward.", e);
             }
@@ -817,7 +817,7 @@
          */
         public void skipToNext() {
             try {
-                mSessionBinder.next();
+                mSessionBinder.next(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling next.", e);
             }
@@ -829,7 +829,7 @@
          */
         public void rewind() {
             try {
-                mSessionBinder.rewind();
+                mSessionBinder.rewind(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rewind.", e);
             }
@@ -840,7 +840,7 @@
          */
         public void skipToPrevious() {
             try {
-                mSessionBinder.previous();
+                mSessionBinder.previous(mContext.getPackageName());
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling previous.", e);
             }
@@ -855,7 +855,7 @@
          */
         public void setRating(Rating rating) {
             try {
-                mSessionBinder.rate(rating);
+                mSessionBinder.rate(mContext.getPackageName(), rating);
             } catch (RemoteException e) {
                 Log.wtf(TAG, "Error calling rate.", e);
             }
@@ -890,7 +890,7 @@
                 throw new IllegalArgumentException("CustomAction cannot be null.");
             }
             try {
-                mSessionBinder.sendCustomAction(action, args);
+                mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args);
             } catch (RemoteException e) {
                 Log.d(TAG, "Dead object in sendCustomAction.", e);
             }
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index b8d01c4..5e8b8ca 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -39,6 +39,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.service.media.MediaBrowserService;
 import android.text.TextUtils;
 import android.util.Log;
@@ -103,6 +104,16 @@
      */
     public static final int FLAG_EXCLUSIVE_GLOBAL_PRIORITY = 1 << 16;
 
+    /**
+     * @hide
+     */
+    public static final int INVALID_UID = -1;
+
+    /**
+     * @hide
+     */
+    public static final int INVALID_PID = -1;
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true, value = {
@@ -501,6 +512,22 @@
     }
 
     /**
+     * Gets the controller information who sent the current request.
+     * <p>
+     * Note: This is only valid while in a request callback, such as {@link Callback#onPlay}.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link Callback} methods.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final @NonNull RemoteUserInfo getCurrentControllerInfo() {
+        if (mCallback == null || mCallback.mCurrentControllerInfo == null) {
+            throw new IllegalStateException(
+                    "This should be called inside of MediaSession.Callback methods");
+        }
+        return mCallback.mCurrentControllerInfo;
+    }
+
+    /**
      * Notify the system that the remote volume changed.
      *
      * @param provider The provider that is handling volume changes.
@@ -528,16 +555,14 @@
      * @hide
      */
     public String getCallingPackage() {
-        try {
-            return mBinder.getCallingPackage();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Dead object in getCallingPackage.", e);
+        if (mCallback != null) {
+            return mCallback.mCurrentControllerInfo.getPackageName();
         }
         return null;
     }
 
-    private void dispatchPrepare() {
-        postToCallback(CallbackMessageHandler.MSG_PREPARE);
+    private void dispatchPrepare(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PREPARE, null, extras);
     }
 
     private void dispatchPrepareFromMediaId(String mediaId, Bundle extras) {
@@ -552,8 +577,8 @@
         postToCallback(CallbackMessageHandler.MSG_PREPARE_URI, uri, extras);
     }
 
-    private void dispatchPlay() {
-        postToCallback(CallbackMessageHandler.MSG_PLAY);
+    private void dispatchPlay(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PLAY, null, extras);
     }
 
     private void dispatchPlayFromMediaId(String mediaId, Bundle extras) {
@@ -568,69 +593,61 @@
         postToCallback(CallbackMessageHandler.MSG_PLAY_URI, uri, extras);
     }
 
-    private void dispatchSkipToItem(long id) {
-        postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id);
+    private void dispatchSkipToItem(long id, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SKIP_TO_ITEM, id, extras);
     }
 
-    private void dispatchPause() {
-        postToCallback(CallbackMessageHandler.MSG_PAUSE);
+    private void dispatchPause(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PAUSE, null, extras);
     }
 
-    private void dispatchStop() {
-        postToCallback(CallbackMessageHandler.MSG_STOP);
+    private void dispatchStop(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_STOP, null, extras);
     }
 
-    private void dispatchNext() {
-        postToCallback(CallbackMessageHandler.MSG_NEXT);
+    private void dispatchNext(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_NEXT, null, extras);
     }
 
-    private void dispatchPrevious() {
-        postToCallback(CallbackMessageHandler.MSG_PREVIOUS);
+    private void dispatchPrevious(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_PREVIOUS, null, extras);
     }
 
-    private void dispatchFastForward() {
-        postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD);
+    private void dispatchFastForward(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_FAST_FORWARD, null, extras);
     }
 
-    private void dispatchRewind() {
-        postToCallback(CallbackMessageHandler.MSG_REWIND);
+    private void dispatchRewind(Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_REWIND, null, extras);
     }
 
-    private void dispatchSeekTo(long pos) {
-        postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos);
+    private void dispatchSeekTo(long pos, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SEEK_TO, pos, extras);
     }
 
-    private void dispatchRate(Rating rating) {
-        postToCallback(CallbackMessageHandler.MSG_RATE, rating);
+    private void dispatchRate(Rating rating, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_RATE, rating, extras);
     }
 
-    private void dispatchCustomAction(String action, Bundle args) {
-        postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
+    private void dispatchCustomAction(String action, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, extras);
     }
 
-    private void dispatchMediaButton(Intent mediaButtonIntent) {
-        postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
+    private void dispatchMediaButton(Intent mediaButtonIntent, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent, extras);
     }
 
-    private void dispatchAdjustVolume(int direction) {
-        postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction);
+    private void dispatchAdjustVolume(int direction, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_ADJUST_VOLUME, direction, extras);
     }
 
-    private void dispatchSetVolumeTo(int volume) {
-        postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume);
+    private void dispatchSetVolumeTo(int volume, Bundle extras) {
+        postToCallback(CallbackMessageHandler.MSG_SET_VOLUME, volume, extras);
     }
 
-    private void postToCallback(int what) {
-        postToCallback(what, null);
-    }
-
-    private void postCommand(String command, Bundle args, ResultReceiver resultCb) {
+    private void postCommand(String command, Bundle args, ResultReceiver resultCb, Bundle extras) {
         Command cmd = new Command(command, args, resultCb);
-        postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
-    }
-
-    private void postToCallback(int what, Object obj) {
-        postToCallback(what, obj, null);
+        postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd, extras);
     }
 
     private void postToCallback(int what, Object obj, Bundle extras) {
@@ -734,9 +751,13 @@
      * and the system. A callback may be set using {@link #setCallback}.
      */
     public abstract static class Callback {
+
         private MediaSession mSession;
         private CallbackMessageHandler mHandler;
         private boolean mMediaPlayPauseKeyPending;
+        private String mCallingPackage;
+        private int mCallingPid;
+        private int mCallingUid;
 
         public Callback() {
         }
@@ -1023,24 +1044,26 @@
         private WeakReference<MediaSession> mMediaSession;
 
         public CallbackStub(MediaSession session) {
-            mMediaSession = new WeakReference<MediaSession>(session);
+            mMediaSession = new WeakReference<>(session);
         }
 
         @Override
-        public void onCommand(String command, Bundle args, ResultReceiver cb) {
+        public void onCommand(String packageName, int pid, int uid, String command, Bundle args,
+                ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.postCommand(command, args, cb);
+                session.postCommand(command, args, cb, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onMediaButton(Intent mediaButtonIntent, int sequenceNumber,
-                ResultReceiver cb) {
+        public void onMediaButton(String packageName, int pid, int uid, Intent mediaButtonIntent,
+                int sequenceNumber, ResultReceiver cb) {
             MediaSession session = mMediaSession.get();
             try {
                 if (session != null) {
-                    session.dispatchMediaButton(mediaButtonIntent);
+                    session.dispatchMediaButton(
+                            mediaButtonIntent, createExtraBundle(packageName, pid, uid));
                 }
             } finally {
                 if (cb != null) {
@@ -1050,165 +1073,191 @@
         }
 
         @Override
-        public void onPrepare() {
+        public void onPrepare(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepare();
+                session.dispatchPrepare(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+        public void onPrepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromMediaId(mediaId, extras);
+                session.dispatchPrepareFromMediaId(
+                        mediaId, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPrepareFromSearch(String query, Bundle extras) {
+        public void onPrepareFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromSearch(query, extras);
+                session.dispatchPrepareFromSearch(
+                        query, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPrepareFromUri(Uri uri, Bundle extras) {
+        public void onPrepareFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrepareFromUri(uri, extras);
+                session.dispatchPrepareFromUri(uri,
+                        createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPlay() {
+        public void onPlay(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlay();
+                session.dispatchPlay(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPlayFromMediaId(String mediaId, Bundle extras) {
+        public void onPlayFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromMediaId(mediaId, extras);
+                session.dispatchPlayFromMediaId(
+                        mediaId, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onPlayFromSearch(String query, Bundle extras) {
+        public void onPlayFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromSearch(query, extras);
+                session.dispatchPlayFromSearch(query, createExtraBundle(packageName, pid, uid,
+                        extras));
             }
         }
 
         @Override
-        public void onPlayFromUri(Uri uri, Bundle extras) {
+        public void onPlayFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPlayFromUri(uri, extras);
+                session.dispatchPlayFromUri(uri, createExtraBundle(packageName, pid, uid, extras));
             }
         }
 
         @Override
-        public void onSkipToTrack(long id) {
+        public void onSkipToTrack(String packageName, int pid, int uid, long id) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSkipToItem(id);
+                session.dispatchSkipToItem(id, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPause() {
+        public void onPause(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPause();
+                session.dispatchPause(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onStop() {
+        public void onStop(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchStop();
+                session.dispatchStop(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onNext() {
+        public void onNext(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchNext();
+                session.dispatchNext(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onPrevious() {
+        public void onPrevious(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchPrevious();
+                session.dispatchPrevious(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onFastForward() {
+        public void onFastForward(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchFastForward();
+                session.dispatchFastForward(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onRewind() {
+        public void onRewind(String packageName, int pid, int uid) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchRewind();
+                session.dispatchRewind(createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onSeekTo(long pos) {
+        public void onSeekTo(String packageName, int pid, int uid, long pos) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSeekTo(pos);
+                session.dispatchSeekTo(pos, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onRate(Rating rating) {
+        public void onRate(String packageName, int pid, int uid, Rating rating) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchRate(rating);
+                session.dispatchRate(rating, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onCustomAction(String action, Bundle args) {
+        public void onCustomAction(String packageName, int pid, int uid, String action,
+                Bundle args) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchCustomAction(action, args);
+                session.dispatchCustomAction(
+                        action, createExtraBundle(packageName, pid, uid, args));
             }
         }
 
         @Override
-        public void onAdjustVolume(int direction) {
+        public void onAdjustVolume(String packageName, int pid, int uid, int direction) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchAdjustVolume(direction);
+                session.dispatchAdjustVolume(direction, createExtraBundle(packageName, pid, uid));
             }
         }
 
         @Override
-        public void onSetVolumeTo(int value) {
+        public void onSetVolumeTo(String packageName, int pid, int uid, int value) {
             MediaSession session = mMediaSession.get();
             if (session != null) {
-                session.dispatchSetVolumeTo(value);
+                session.dispatchSetVolumeTo(value, createExtraBundle(packageName, pid, uid));
             }
         }
 
+        private Bundle createExtraBundle(String packageName, int pid, int uid) {
+            return createExtraBundle(packageName, pid, uid, null);
+        }
+
+        private Bundle createExtraBundle(String packageName, int pid, int uid,
+                Bundle originalBundle) {
+            Bundle bundle = new Bundle();
+            bundle.putString(CallbackMessageHandler.EXTRA_KEY_CALLING_PACKAGE, packageName);
+            bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_PID, pid);
+            bundle.putInt(CallbackMessageHandler.EXTRA_KEY_CALLING_UID, uid);
+            if (originalBundle != null) {
+                bundle.putBundle(CallbackMessageHandler.EXTRA_KEY_ORIGINAL_BUNDLE, originalBundle);
+            }
+            return bundle;
+        }
     }
 
     /**
@@ -1272,7 +1321,8 @@
             return 0;
         }
 
-        public static final Creator<MediaSession.QueueItem> CREATOR = new Creator<MediaSession.QueueItem>() {
+        public static final Creator<MediaSession.QueueItem> CREATOR =
+                new Creator<MediaSession.QueueItem>() {
 
             @Override
             public MediaSession.QueueItem createFromParcel(Parcel p) {
@@ -1329,6 +1379,15 @@
 
     private class CallbackMessageHandler extends Handler {
 
+        private static final String EXTRA_KEY_CALLING_PACKAGE =
+                "android.media.session.extra.CALLING_PACKAGE";
+        private static final String EXTRA_KEY_CALLING_PID =
+                "android.media.session.extra.CALLING_PID";
+        private static final String EXTRA_KEY_CALLING_UID =
+                "android.media.session.extra.CALLING_UID";
+        private static final String EXTRA_KEY_ORIGINAL_BUNDLE =
+                "android.media.session.extra.ORIGINAL_BUNDLE";
+
         private static final int MSG_COMMAND = 1;
         private static final int MSG_MEDIA_BUTTON = 2;
         private static final int MSG_PREPARE = 3;
@@ -1355,6 +1414,8 @@
 
         private MediaSession.Callback mCallback;
 
+        private RemoteUserInfo mCurrentControllerInfo;
+
         public CallbackMessageHandler(Looper looper, MediaSession.Callback callback) {
             super(looper, null, true);
             mCallback = callback;
@@ -1367,21 +1428,17 @@
             msg.sendToTarget();
         }
 
-        public void post(int what, Object obj) {
-            obtainMessage(what, obj).sendToTarget();
-        }
-
-        public void post(int what) {
-            post(what, null);
-        }
-
-        public void post(int what, Object obj, int arg1) {
-            obtainMessage(what, arg1, 0, obj).sendToTarget();
-        }
-
         @Override
         public void handleMessage(Message msg) {
             VolumeProvider vp;
+            Bundle bundle = msg.getData();
+            Bundle originalBundle = bundle.getBundle(EXTRA_KEY_ORIGINAL_BUNDLE);
+
+            mCurrentControllerInfo = new RemoteUserInfo(
+                    bundle.getString(EXTRA_KEY_CALLING_PACKAGE),
+                    bundle.getInt(EXTRA_KEY_CALLING_PID, INVALID_PID),
+                    bundle.getInt(EXTRA_KEY_CALLING_UID, INVALID_UID));
+
             switch (msg.what) {
                 case MSG_COMMAND:
                     Command cmd = (Command) msg.obj;
@@ -1394,25 +1451,25 @@
                     mCallback.onPrepare();
                     break;
                 case MSG_PREPARE_MEDIA_ID:
-                    mCallback.onPrepareFromMediaId((String) msg.obj, msg.getData());
+                    mCallback.onPrepareFromMediaId((String) msg.obj, originalBundle);
                     break;
                 case MSG_PREPARE_SEARCH:
-                    mCallback.onPrepareFromSearch((String) msg.obj, msg.getData());
+                    mCallback.onPrepareFromSearch((String) msg.obj, originalBundle);
                     break;
                 case MSG_PREPARE_URI:
-                    mCallback.onPrepareFromUri((Uri) msg.obj, msg.getData());
+                    mCallback.onPrepareFromUri((Uri) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY:
                     mCallback.onPlay();
                     break;
                 case MSG_PLAY_MEDIA_ID:
-                    mCallback.onPlayFromMediaId((String) msg.obj, msg.getData());
+                    mCallback.onPlayFromMediaId((String) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY_SEARCH:
-                    mCallback.onPlayFromSearch((String) msg.obj, msg.getData());
+                    mCallback.onPlayFromSearch((String) msg.obj, originalBundle);
                     break;
                 case MSG_PLAY_URI:
-                    mCallback.onPlayFromUri((Uri) msg.obj, msg.getData());
+                    mCallback.onPlayFromUri((Uri) msg.obj, originalBundle);
                     break;
                 case MSG_SKIP_TO_ITEM:
                     mCallback.onSkipToQueueItem((Long) msg.obj);
@@ -1442,7 +1499,7 @@
                     mCallback.onSetRating((Rating) msg.obj);
                     break;
                 case MSG_CUSTOM_ACTION:
-                    mCallback.onCustomAction((String) msg.obj, msg.getData());
+                    mCallback.onCustomAction((String) msg.obj, originalBundle);
                     break;
                 case MSG_ADJUST_VOLUME:
                     synchronized (mLock) {
@@ -1464,6 +1521,7 @@
                     mCallback.handleMediaPlayPauseKeySingleTapIfPending();
                     break;
             }
+            mCurrentControllerInfo = null;
         }
     }
 }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 3b12fca..519af1b 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -37,7 +37,9 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.service.media.MediaBrowserService;
 import android.service.notification.NotificationListenerService;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -340,19 +342,20 @@
     }
 
     /**
-     * Returns whether the api
+     * Returns whether the app is trusted.
+     * <p>
+     * An app is trusted if the app holds the android.Manifest.permission.MEDIA_CONTENT_CONTROL
+     * permission or has an enabled notification listener.
      *
-     * @param packageName packageName
-     * @param pid pid of the app
-     * @param uid uid of the app
-     * @hide
+     * @param userInfo The remote user info
      */
-    public boolean isTrusted(@NonNull String packageName, int pid, int uid) {
-        if (packageName == null) {
+    public boolean isTrustedForMediaControl(RemoteUserInfo userInfo) {
+        if (userInfo.getPackageName() == null) {
             return false;
         }
         try {
-            return mService.isTrusted(packageName, pid, uid);
+            return mService.isTrusted(
+                    userInfo.getPackageName(), userInfo.getPid(), userInfo.getUid());
         } catch (RemoteException e) {
             Log.wtf(TAG, "Cannot communicate with the service.", e);
         }
@@ -763,6 +766,56 @@
         public abstract void onAddressedPlayerChanged(ComponentName mediaButtonReceiver);
     }
 
+    /**
+     * Information of a remote user of {@link MediaSession} or {@link MediaBrowserService}.
+     * This can be used to decide whether the remote user is trusted app.
+     *
+     * @see #isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public static final class RemoteUserInfo {
+        private String mPackageName;
+        private int mPid;
+        private int mUid;
+
+        public RemoteUserInfo(String packageName, int pid, int uid) {
+            mPackageName = packageName;
+            mPid = pid;
+            mUid = uid;
+        }
+
+        /**
+         * @return package name of the controller
+         */
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        /**
+         * @return pid of the controller
+         */
+        public int getPid() {
+            return mPid;
+        }
+
+        /**
+         * @return uid of the controller
+         */
+        public int getUid() {
+            return mUid;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof RemoteUserInfo)) {
+                return false;
+            }
+            RemoteUserInfo otherUserInfo = (RemoteUserInfo) obj;
+            return TextUtils.equals(mPackageName, otherUserInfo.mPackageName)
+                    && mPid == otherUserInfo.mPid
+                    && mUid == otherUserInfo.mUid;
+        }
+    }
+
     private static final class SessionsChangedWrapper {
         private Context mContext;
         private OnActiveSessionsChangedListener mListener;
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index 1244d7f..7976f67 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -314,7 +314,9 @@
     public void addStorage(StorageVolume storage) {
         MtpStorage mtpStorage = mManager.addMtpStorage(storage);
         mStorageMap.put(storage.getPath(), mtpStorage);
-        mServer.addStorage(mtpStorage);
+        if (mServer != null) {
+            mServer.addStorage(mtpStorage);
+        }
     }
 
     public void removeStorage(StorageVolume storage) {
@@ -322,7 +324,9 @@
         if (mtpStorage == null) {
             return;
         }
-        mServer.removeStorage(mtpStorage);
+        if (mServer != null) {
+            mServer.removeStorage(mtpStorage);
+        }
         mManager.removeMtpStorage(mtpStorage);
         mStorageMap.remove(storage.getPath());
     }
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index 4fc43ea..6d1bd45 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -31,6 +31,8 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
+import android.media.session.MediaSessionManager;
+import android.media.session.MediaSessionManager.RemoteUserInfo;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -112,6 +114,8 @@
      */
     private class ConnectionRecord implements IBinder.DeathRecipient {
         String pkg;
+        int uid;
+        int pid;
         Bundle rootHints;
         IMediaBrowserServiceCallbacks callbacks;
         BrowserRoot root;
@@ -199,6 +203,7 @@
         public void connect(final String pkg, final Bundle rootHints,
                 final IMediaBrowserServiceCallbacks callbacks) {
 
+            final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             if (!isValidPackage(pkg, uid)) {
                 throw new IllegalArgumentException("Package/uid mismatch: uid=" + uid
@@ -215,9 +220,14 @@
 
                         final ConnectionRecord connection = new ConnectionRecord();
                         connection.pkg = pkg;
+                        connection.pid = pid;
+                        connection.uid = uid;
                         connection.rootHints = rootHints;
                         connection.callbacks = callbacks;
+
+                        mCurConnection = connection;
                         connection.root = MediaBrowserService.this.onGetRoot(pkg, uid, rootHints);
+                        mCurConnection = null;
 
                         // If they didn't return something, don't allow this client.
                         if (connection.root == null) {
@@ -505,21 +515,36 @@
      * media browser service when connecting and retrieving the root id for browsing, or null if
      * none. The contents of this bundle may affect the information returned when browsing.
      *
-     * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or
-     *             {@link #onLoadItem}.
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
      * @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
      * @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
      * @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
      */
     public final Bundle getBrowserRootHints() {
         if (mCurConnection == null) {
-            throw new IllegalStateException("This should be called inside of onLoadChildren or"
-                    + " onLoadItem methods");
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
         }
         return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
     }
 
     /**
+     * Gets the browser information who sent the current request.
+     *
+     * @throws IllegalStateException If this method is called outside of {@link #onGetRoot} or
+     *             {@link #onLoadChildren} or {@link #onLoadItem}.
+     * @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
+     */
+    public final RemoteUserInfo getCurrentBrowserInfo() {
+        if (mCurConnection == null) {
+            throw new IllegalStateException("This should be called inside of onGetRoot or"
+                    + " onLoadChildren or onLoadItem methods");
+        }
+        return new RemoteUserInfo(mCurConnection.pkg, mCurConnection.pid, mCurConnection.uid);
+    }
+
+    /**
      * Notifies all connected media browsers that the children of
      * the specified parent id have changed in some way.
      * This will cause browsers to fetch subscribed content again.
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4c20f05..54541f0 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -729,15 +729,15 @@
 
     GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_UNKNOWN", "I");
     gSecurityLevels.kSecurityLevelUnknown = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_CRYPTO", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_CRYPTO", "I");
     gSecurityLevels.kSecurityLevelSwSecureCrypto = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "SW_SECURE_DECODE", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_SW_SECURE_DECODE", "I");
     gSecurityLevels.kSecurityLevelSwSecureDecode = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_CRYPTO", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_CRYPTO", "I");
     gSecurityLevels.kSecurityLevelHwSecureCrypto = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_DECODE", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_DECODE", "I");
     gSecurityLevels.kSecurityLevelHwSecureDecode = env->GetStaticIntField(clazz, field);
-    GET_STATIC_FIELD_ID(field, clazz, "HW_SECURE_ALL", "I");
+    GET_STATIC_FIELD_ID(field, clazz, "SECURITY_LEVEL_HW_SECURE_ALL", "I");
     gSecurityLevels.kSecurityLevelHwSecureAll = env->GetStaticIntField(clazz, field);
 
     jmethodID getMaxSecurityLevel;
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 12d7440..4d8c96f 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "MtpDatabaseJNI"
 #include "utils/Log.h"
+#include "utils/String8.h"
 
 #include "android_media_Utils.h"
 #include "mtp.h"
@@ -161,7 +162,7 @@
     virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
 
     virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
-                                            MtpString& outFilePath,
+                                            MtpStringBuffer& outFilePath,
                                             int64_t& outFileLength,
                                             MtpObjectFormat& outFormat);
     virtual MtpResponseCode         beginDeleteObject(MtpObjectHandle handle);
@@ -287,7 +288,7 @@
     jint* handles = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(handles[i]);
+        list->push_back(handles[i]);
     env->ReleaseIntArrayElements(array, handles, 0);
     env->DeleteLocalRef(array);
 
@@ -316,7 +317,7 @@
     jint* formats = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(formats[i]);
+        list->push_back(formats[i]);
     env->ReleaseIntArrayElements(array, formats, 0);
     env->DeleteLocalRef(array);
 
@@ -334,7 +335,7 @@
     jint* formats = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(formats[i]);
+        list->push_back(formats[i]);
     env->ReleaseIntArrayElements(array, formats, 0);
     env->DeleteLocalRef(array);
 
@@ -352,7 +353,7 @@
     jint* properties = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(properties[i]);
+        list->push_back(properties[i]);
     env->ReleaseIntArrayElements(array, properties, 0);
     env->DeleteLocalRef(array);
 
@@ -370,7 +371,7 @@
     jint* properties = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(properties[i]);
+        list->push_back(properties[i]);
     env->ReleaseIntArrayElements(array, properties, 0);
     env->DeleteLocalRef(array);
 
@@ -826,7 +827,7 @@
 
 MtpResponseCode MtpDatabase::getObjectInfo(MtpObjectHandle handle,
                                              MtpObjectInfo& info) {
-    MtpString       path;
+    MtpStringBuffer path;
     int64_t         length;
     MtpObjectFormat format;
 
@@ -861,8 +862,8 @@
     info.mAssociationType = MTP_ASSOCIATION_TYPE_UNDEFINED;
 
     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
-    MtpString temp(reinterpret_cast<char16_t*>(str));
-    info.mName = strdup((const char *)temp);
+    MtpStringBuffer temp(str);
+    info.mName = strdup(temp);
     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
 
     // read EXIF data for thumbnail information
@@ -901,9 +902,10 @@
         case MTP_FORMAT_TIFF:
         case MTP_FORMAT_TIFF_EP:
         case MTP_FORMAT_DEFINED: {
-            std::unique_ptr<FileStream> stream(new FileStream(path));
+            String8 temp(path);
+            std::unique_ptr<FileStream> stream(new FileStream(temp));
             piex::PreviewImageData image_data;
-            if (!GetExifFromRawImage(stream.get(), path, image_data)) {
+            if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
                 // Couldn't parse EXIF data from a image file via piex.
                 break;
             }
@@ -922,7 +924,7 @@
 }
 
 void* MtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
-    MtpString path;
+    MtpStringBuffer path;
     int64_t length;
     MtpObjectFormat format;
     void* result = NULL;
@@ -957,9 +959,10 @@
             case MTP_FORMAT_TIFF:
             case MTP_FORMAT_TIFF_EP:
             case MTP_FORMAT_DEFINED: {
-                std::unique_ptr<FileStream> stream(new FileStream(path));
+                String8 temp(path);
+                std::unique_ptr<FileStream> stream(new FileStream(temp));
                 piex::PreviewImageData image_data;
-                if (!GetExifFromRawImage(stream.get(), path, image_data)) {
+                if (!GetExifFromRawImage(stream.get(), temp, image_data)) {
                     // Couldn't parse EXIF data from a image file via piex.
                     break;
                 }
@@ -992,7 +995,7 @@
 }
 
 MtpResponseCode MtpDatabase::getObjectFilePath(MtpObjectHandle handle,
-                                                 MtpString& outFilePath,
+                                                 MtpStringBuffer& outFilePath,
                                                  int64_t& outFileLength,
                                                  MtpObjectFormat& outFormat) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
@@ -1004,8 +1007,7 @@
     }
 
     jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
-    outFilePath.setTo(reinterpret_cast<char16_t*>(str),
-                      strlen16(reinterpret_cast<char16_t*>(str)));
+    outFilePath.set(str);
     env->ReleaseCharArrayElements(mStringBuffer, str, 0);
 
     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
@@ -1146,7 +1148,7 @@
     jint* handles = env->GetIntArrayElements(array, 0);
     jsize length = env->GetArrayLength(array);
     for (int i = 0; i < length; i++)
-        list->push(handles[i]);
+        list->push_back(handles[i]);
     env->ReleaseIntArrayElements(array, handles, 0);
     env->DeleteLocalRef(array);
 
diff --git a/media/jni/android_mtp_MtpDevice.cpp b/media/jni/android_mtp_MtpDevice.cpp
index d42c39e..060eaf9 100644
--- a/media/jni/android_mtp_MtpDevice.cpp
+++ b/media/jni/android_mtp_MtpDevice.cpp
@@ -258,7 +258,7 @@
                 return NULL;
             }
             for (size_t i = 0; i < size; ++i) {
-                elements[i] = deviceInfo->mOperations->itemAt(i);
+                elements[i] = static_cast<int>(deviceInfo->mOperations->at(i));
             }
             env->SetObjectField(info, field_deviceInfo_operationsSupported, operations.get());
         }
@@ -274,7 +274,7 @@
                 return NULL;
             }
             for (size_t i = 0; i < size; ++i) {
-                elements[i] = deviceInfo->mEvents->itemAt(i);
+                elements[i] = static_cast<int>(deviceInfo->mEvents->at(i));
             }
             env->SetObjectField(info, field_deviceInfo_eventsSupported, events.get());
         }
@@ -296,7 +296,7 @@
     int length = storageIDs->size();
     jintArray array = env->NewIntArray(length);
     // FIXME is this cast safe?
-    env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->array());
+    env->SetIntArrayRegion(array, 0, length, (const jint *)storageIDs->data());
 
     delete storageIDs;
     return array;
@@ -350,7 +350,7 @@
     int length = handles->size();
     jintArray array = env->NewIntArray(length);
     // FIXME is this cast safe?
-    env->SetIntArrayRegion(array, 0, length, (const jint *)handles->array());
+    env->SetIntArrayRegion(array, 0, length, (const jint *)handles->data());
 
     delete handles;
     return array;
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 8730e06..c60590a 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -67,10 +67,10 @@
     int controlFd = dup(jniGetFDFromFileDescriptor(env, jControlFd));
     MtpServer* server = new MtpServer(getMtpDatabase(env, javaDatabase), controlFd,
             usePtp,
-            MtpString((deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : ""),
-            MtpString((deviceInfoModelStr != NULL) ? deviceInfoModelStr : ""),
-            MtpString((deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : ""),
-            MtpString((deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : ""));
+            (deviceInfoManufacturerStr != NULL) ? deviceInfoManufacturerStr : "",
+            (deviceInfoModelStr != NULL) ? deviceInfoModelStr : "",
+            (deviceInfoDeviceVersionStr != NULL) ? deviceInfoDeviceVersionStr : "",
+            (deviceInfoSerialNumberStr != NULL) ? deviceInfoSerialNumberStr : "");
     if (deviceInfoManufacturerStr != NULL) {
         env->ReleaseStringUTFChars(deviceInfoManufacturer, deviceInfoManufacturerStr);
     }
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 7828c4c..dbdf5e16 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -35,6 +35,7 @@
 import android.os.Bundle;
 import android.provider.Settings;
 import android.support.v4.widget.SwipeRefreshLayout;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TypedValue;
@@ -531,7 +532,7 @@
 
     private String getHeaderTitle() {
         NetworkInfo info = mCm.getNetworkInfo(mNetwork);
-        if (info == null) {
+        if (info == null || TextUtils.isEmpty(info.getExtraInfo())) {
             return getString(R.string.action_bar_label);
         }
         NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
index 4ad4b24..de2659f 100644
--- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
+++ b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java
@@ -30,7 +30,7 @@
     private static final String TAG = "ExtAssistant.CI";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    static final double DISMISS_TO_VIEW_RATIO_LIMIT = .8;
+    static final double DISMISS_TO_VIEW_RATIO_LIMIT = .4;
     static final int STREAK_LIMIT = 2;
     static final String ATT_DISMISSALS = "dismisses";
     static final String ATT_VIEWS = "views";
diff --git a/packages/MtpDocumentsProvider/perf_tests/Android.mk b/packages/MtpDocumentsProvider/perf_tests/Android.mk
index 6504af1..e873157 100644
--- a/packages/MtpDocumentsProvider/perf_tests/Android.mk
+++ b/packages/MtpDocumentsProvider/perf_tests/Android.mk
@@ -8,5 +8,6 @@
 LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
 LOCAL_CERTIFICATE := media
+LOCAL_COMPATIBILITY_SUITE += device-tests
 
 include $(BUILD_PACKAGE)
diff --git a/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml b/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml
new file mode 100644
index 0000000..8b7292b
--- /dev/null
+++ b/packages/MtpDocumentsProvider/perf_tests/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs MtpDocumentsProviderPerfTests metric instrumentation.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-metric-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="MtpDocumentsProviderPerfTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.mtp.perftests" />
+    </test>
+</configuration>
diff --git a/packages/PrintSpooler/res/layout/select_printer_activity.xml b/packages/PrintSpooler/res/layout/select_printer_activity.xml
index 564802a..91beff6 100644
--- a/packages/PrintSpooler/res/layout/select_printer_activity.xml
+++ b/packages/PrintSpooler/res/layout/select_printer_activity.xml
@@ -42,7 +42,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginBottom="12dip"
                 android:src="@*android:drawable/ic_grayedout_printer"
-                android:contentDescription="@string/print_searching_for_printers">
+                android:importantForAccessibility="no">
             </ImageView>
 
             <TextView
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 584fc34..6368607 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -500,10 +500,10 @@
     <string name="keep_screen_on">Stay awake</string>
     <!-- setting Checkbox summary whether to keep the screen on when plugged in  -->
     <string name="keep_screen_on_summary">Screen will never sleep while charging</string>
-    <!-- Setting Checkbox title whether to enable bluetooth HCI snoop log -->
+    <!-- Setting Checkbox title whether to enable Bluetooth HCI snoop log -->
     <string name="bt_hci_snoop_log">Enable Bluetooth HCI snoop log</string>
-    <!-- setting Checkbox summary whether to capture all bluetooth HCI packets in a file -->
-    <string name="bt_hci_snoop_log_summary">Capture all bluetooth HCI packets in a file</string>
+    <!-- setting Checkbox summary whether to capture all Bluetooth HCI packets in a file -->
+    <string name="bt_hci_snoop_log_summary">Capture all Bluetooth HCI packets in a file (Toggle Bluetooth after changing this setting)</string>
     <!-- setting Checkbox title whether to enable OEM unlock [CHAR_LIMIT=35] -->
     <string name="oem_unlock_enable">OEM unlocking</string>
     <!-- setting Checkbox summary whether to enable OEM unlock [CHAR_LIMIT=50] -->
@@ -543,30 +543,30 @@
     <!-- UI debug setting: Select Bluetooth AVRCP Version -->
     <string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Codec -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
     <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec -->
-    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection -->
+    <string name="bluetooth_select_a2dp_codec_type_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Sample Rate -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Sample Rate Selection -->
     <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate -->
-    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate -->
+    <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection -->
     <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample -->
-    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Bits Per Sample -->
+    <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Bits Per Sample</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio Channel Mode -->
+    <!-- UI debug setting: Trigger Bluetooth Audio Channel Mode Selection -->
     <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string>
-    <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode -->
-    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string>
+    <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Channel Mode -->
+    <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Channel Mode</string>
 
-    <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality -->
+    <!-- UI debug setting: Trigger Bluetooth Audio LDAC Playback Quality Selection -->
     <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string>
     <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality -->
-    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string>
+    <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Trigger Bluetooth Audio LDAC Codec\u000ASelection: Playback Quality</string>
 
     <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
     <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index 7728f66..aeb4a85 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -378,7 +378,7 @@
 
         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        return dpm.isMeteredDataDisabledForUser(enforcedAdmin.component, packageName, userId)
+        return dpm.isMeteredDataDisabledPackageForUser(enforcedAdmin.component, packageName, userId)
                 ? enforcedAdmin : null;
     }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index 48b7c35..42b3156 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -16,20 +16,20 @@
 
 package com.android.settingslib;
 
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.os.UserHandle;
+import android.support.v14.preference.SwitchPreference;
 import android.support.v4.content.res.TypedArrayUtils;
 import android.support.v7.preference.PreferenceManager;
 import android.support.v7.preference.PreferenceViewHolder;
-import android.support.v14.preference.SwitchPreference;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.View;
 import android.widget.TextView;
 
-import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-
 /**
  * Version of SwitchPreference that can be disabled by a device admin
  * using a user restriction.
@@ -37,7 +37,7 @@
 public class RestrictedSwitchPreference extends SwitchPreference {
     RestrictedPreferenceHelper mHelper;
     boolean mUseAdditionalSummary = false;
-    String mRestrictedSwitchSummary = null;
+    CharSequence mRestrictedSwitchSummary;
 
     public RestrictedSwitchPreference(Context context, AttributeSet attrs,
             int defStyleAttr, int defStyleRes) {
@@ -57,19 +57,15 @@
 
             final TypedValue restrictedSwitchSummary = attributes.peekValue(
                     R.styleable.RestrictedSwitchPreference_restrictedSwitchSummary);
-            CharSequence data = null;
             if (restrictedSwitchSummary != null
                     && restrictedSwitchSummary.type == TypedValue.TYPE_STRING) {
                 if (restrictedSwitchSummary.resourceId != 0) {
-                    data = context.getString(restrictedSwitchSummary.resourceId);
+                    mRestrictedSwitchSummary =
+                        context.getText(restrictedSwitchSummary.resourceId);
                 } else {
-                    data = restrictedSwitchSummary.string;
+                    mRestrictedSwitchSummary = restrictedSwitchSummary.string;
                 }
             }
-            mRestrictedSwitchSummary = data == null ? null : data.toString();
-        }
-        if (mRestrictedSwitchSummary == null) {
-            mRestrictedSwitchSummary = context.getString(R.string.disabled_by_admin);
         }
         if (mUseAdditionalSummary) {
             setLayoutResource(R.layout.restricted_switch_preference);
@@ -94,6 +90,15 @@
     public void onBindViewHolder(PreferenceViewHolder holder) {
         super.onBindViewHolder(holder);
         mHelper.onBindViewHolder(holder);
+
+        CharSequence switchSummary;
+        if (mRestrictedSwitchSummary == null) {
+            switchSummary = getContext().getText(isChecked()
+                ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+        } else {
+            switchSummary = mRestrictedSwitchSummary;
+        }
+
         final View restrictedIcon = holder.findViewById(R.id.restricted_icon);
         final View switchWidget = holder.findViewById(android.R.id.switch_widget);
         if (restrictedIcon != null) {
@@ -102,12 +107,13 @@
         if (switchWidget != null) {
             switchWidget.setVisibility(isDisabledByAdmin() ? View.GONE : View.VISIBLE);
         }
+
         if (mUseAdditionalSummary) {
             final TextView additionalSummaryView = (TextView) holder.findViewById(
                     R.id.additional_summary);
             if (additionalSummaryView != null) {
                 if (isDisabledByAdmin()) {
-                    additionalSummaryView.setText(mRestrictedSwitchSummary);
+                    additionalSummaryView.setText(switchSummary);
                     additionalSummaryView.setVisibility(View.VISIBLE);
                 } else {
                     additionalSummaryView.setVisibility(View.GONE);
@@ -117,7 +123,7 @@
             final TextView summaryView = (TextView) holder.findViewById(android.R.id.summary);
             if (summaryView != null) {
                 if (isDisabledByAdmin()) {
-                    summaryView.setText(mRestrictedSwitchSummary);
+                    summaryView.setText(switchSummary);
                     summaryView.setVisibility(View.VISIBLE);
                 }
                 // No need to change the visibility to GONE in the else case here since Preference
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index ee12191..f9f80eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -163,6 +163,11 @@
         return mService.getActiveDevice();
     }
 
+    public boolean isAudioOn() {
+        if (mService == null) return false;
+        return mService.isAudioOn();
+    }
+
     public boolean isPreferred(BluetoothDevice device) {
         if (mService == null) return false;
         return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index e2c7747..835ff07 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -20,8 +20,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.PowerManager;
+import android.provider.Settings.Global;
 import android.provider.Settings.Secure;
+import android.util.KeyValueListParser;
 import android.util.Log;
+import android.util.Slog;
 
 /**
  * Utilities related to battery saver.
@@ -34,10 +37,49 @@
 
     private static final boolean DEBUG = false;
 
-    // Broadcast action for SystemUI to show the battery saver confirmation dialog.
+    private static final String SYSUI_PACKAGE = "com.android.systemui";
+
+    /** Broadcast action for SystemUI to show the battery saver confirmation dialog. */
     public static final String ACTION_SHOW_START_SAVER_CONFIRMATION = "PNW.startSaverConfirmation";
 
     /**
+     * Broadcast action for SystemUI to show the notification that suggests turning on
+     * automatic battery saver.
+     */
+    public static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION
+            = "PNW.autoSaverSuggestion";
+
+    private static class Parameters {
+        private final Context mContext;
+
+        /**
+         * We show the auto battery saver suggestion notification when the user manually enables
+         * battery saver for the START_NTH time through the END_NTH time.
+         * (We won't show it for END_NTH + 1 time and after.)
+         */
+        private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4;
+        private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8;
+
+        public final int startNth;
+        public final int endNth;
+
+        public Parameters(Context context) {
+            mContext = context;
+
+            final String newValue = Global.getString(mContext.getContentResolver(),
+                    Global.LOW_POWER_MODE_SUGGESTION_PARAMS);
+            final KeyValueListParser parser = new KeyValueListParser(',');
+            try {
+                parser.setString(newValue);
+            } catch (IllegalArgumentException e) {
+                Slog.wtf(TAG, "Bad constants: " + newValue);
+            }
+            startNth = parser.getInt("start_nth", AUTO_SAVER_SUGGESTION_START_NTH);
+            endNth = parser.getInt("end_nth", AUTO_SAVER_SUGGESTION_END_NTH);
+        }
+    }
+
+    /**
      * Enable / disable battery saver by user request.
      * - If it's the first time and needFirstTimeWarning, show the first time dialog.
      * - If it's 4th time through 8th time, show the schedule suggestion notification.
@@ -62,11 +104,19 @@
 
         if (context.getSystemService(PowerManager.class).setPowerSaveMode(enable)) {
             if (enable) {
-                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT,
-                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1);
+                final int count =
+                        Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1;
+                Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count);
 
-                // TODO If enabling, and the count is between 4 and 8 (inclusive), then
-                // show the "battery saver schedule suggestion" notification.
+                final Parameters parameters = new Parameters(context);
+
+                if ((count >= parameters.startNth)
+                        && (count <= parameters.endNth)
+                        && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0
+                        && Secure.getInt(cr,
+                        Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) {
+                    showAutoBatterySaverSuggestion(context);
+                }
             }
 
             return true;
@@ -79,13 +129,34 @@
                 Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 0) != 0) {
             return false; // Already shown.
         }
-        final Intent i = new Intent(ACTION_SHOW_START_SAVER_CONFIRMATION);
-        context.sendBroadcast(i);
-
+        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_START_SAVER_CONFIRMATION));
         return true;
     }
 
+    private static void showAutoBatterySaverSuggestion(Context context) {
+        context.sendBroadcast(getSystemUiBroadcast(ACTION_SHOW_AUTO_SAVER_SUGGESTION));
+    }
+
+    private static Intent getSystemUiBroadcast(String action) {
+        final Intent i = new Intent(action);
+        i.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        i.setPackage(SYSUI_PACKAGE);
+        return i;
+    }
+
     private static void setBatterySaverConfirmationAcknowledged(Context context) {
         Secure.putInt(context.getContentResolver(), Secure.LOW_POWER_WARNING_ACKNOWLEDGED, 1);
     }
+
+    public static void suppressAutoBatterySaver(Context context) {
+        Secure.putInt(context.getContentResolver(),
+                Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 1);
+    }
+
+    public static void scheduleAutoBatterySaver(Context context, int level) {
+        if (Global.getInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0)
+                == 0) {
+            Global.putInt(context.getContentResolver(), Global.LOW_POWER_MODE_TRIGGER_LEVEL, level);
+        }
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
index 81a2d43..3e3c039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/StringUtil.java
@@ -41,10 +41,11 @@
     public static final int SECONDS_PER_DAY = 24 * 60 * 60;
 
     /**
-    * Returns elapsed time for the given millis, in the following format:
-    * 2d 5h 40m 29s
-    * @param context the application context
-     * @param millis the elapsed time in milli seconds
+     * Returns elapsed time for the given millis, in the following format:
+     * 2 days, 5 hr, 40 min, 29 sec
+     *
+     * @param context     the application context
+     * @param millis      the elapsed time in milli seconds
      * @param withSeconds include seconds?
      * @return the formatted elapsed time
      */
@@ -92,7 +93,7 @@
 
         final Locale locale = context.getResources().getConfiguration().locale;
         final MeasureFormat measureFormat = MeasureFormat.getInstance(
-                locale, FormatWidth.NARROW);
+                locale, FormatWidth.SHORT);
         sb.append(measureFormat.formatMeasures(measureArray));
 
         if (measureArray.length == 1 && MeasureUnit.MINUTE.equals(measureArray[0].getUnit())) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 78045f9..50dfc26 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -43,8 +43,6 @@
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -896,6 +894,13 @@
             summary.append(WifiUtils.buildLoggingSummary(this, config));
         }
 
+        if (config != null && (WifiUtils.isMeteredOverridden(config) || config.meteredHint)) {
+            return mContext.getResources().getString(
+                    R.string.preference_summary_default_combination,
+                    WifiUtils.getMeteredLabel(mContext, config),
+                    summary.toString());
+        }
+
         // If Speed label and summary are both present, use the preference combination to combine
         // the two, else return the non-null one.
         if (getSpeedLabel() != null && summary.length() != 0) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index 4cd23f9..9347674 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -50,7 +50,9 @@
                 }
             };
     private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
-            .clearCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
+            .clearCapabilities()
+            .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
     private final ConnectivityManager.NetworkCallback mNetworkCallback = new ConnectivityManager
             .NetworkCallback() {
         @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 8f80527..a128b54 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -213,6 +213,7 @@
 
         mNetworkRequest = new NetworkRequest.Builder()
                 .clearCapabilities()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                 .build();
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
index 61efabc..bee2fa1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java
@@ -16,12 +16,15 @@
 
 package com.android.settingslib.wifi;
 
+import android.content.Context;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.os.SystemClock;
 import android.support.annotation.VisibleForTesting;
 
+import com.android.settingslib.R;
+
 import java.util.Map;
 
 public class WifiUtils {
@@ -198,4 +201,18 @@
         // speed wil be determined by mRssi
         return timedScore.getScore().calculateBadge(result.level);
     }
+
+    public static String getMeteredLabel(Context context, WifiConfiguration config) {
+        // meteredOverride is whether the user manually set the metered setting or not.
+        // meteredHint is whether the network itself is telling us that it is metered
+        if (config.meteredOverride == WifiConfiguration.METERED_OVERRIDE_METERED
+                || (config.meteredHint && !isMeteredOverridden(config))) {
+            return context.getString(R.string.wifi_metered_label);
+        }
+        return context.getString(R.string.wifi_unmetered_label);
+    }
+
+    public static boolean isMeteredOverridden(WifiConfiguration config) {
+        return config.meteredOverride != WifiConfiguration.METERED_OVERRIDE_NONE;
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
new file mode 100644
index 0000000..117f447
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HeadsetProfileTest.java
@@ -0,0 +1,60 @@
+package com.android.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+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 HeadsetProfileTest {
+
+    @Mock
+    private LocalBluetoothAdapter mAdapter;
+    @Mock
+    private CachedBluetoothDeviceManager mDeviceManager;
+    @Mock
+    private LocalBluetoothProfileManager mProfileManager;
+    @Mock
+    private BluetoothHeadset mService;
+    
+    private BluetoothProfile.ServiceListener mServiceListener;
+    private HeadsetProfile mProfile;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        Context context = spy(RuntimeEnvironment.application);
+
+        doAnswer((invocation) -> {
+            mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1];
+            return null;
+        }).when(mAdapter).getProfileProxy(any(Context.class), any(), eq(BluetoothProfile.HEADSET));
+
+        mProfile = new HeadsetProfile(context, mAdapter, mDeviceManager, mProfileManager);
+        mServiceListener.onServiceConnected(BluetoothProfile.HEADSET, mService);
+    }
+
+    @Test
+    public void bluetoothProfile_shouldReturnTheAudioStatusFromBlueToothHeadsetService() {
+        when(mService.isAudioOn()).thenReturn(true);
+        assertThat(mProfile.isAudioOn()).isTrue();
+
+        when(mService.isAudioOn()).thenReturn(false);
+        assertThat(mProfile.isAudioOn()).isFalse();
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
index c11687b..05247ba 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/PowerUtilTest.java
@@ -137,9 +137,9 @@
                 true /* basedOnUsage */);
 
         // shortened string should not have percentage
-        assertThat(info).isEqualTo("Less than 15m remaining");
+        assertThat(info).isEqualTo("Less than 15 min remaining");
         // Add percentage to string when provided
-        assertThat(info2).isEqualTo("Less than 15m remaining (10%)");
+        assertThat(info2).isEqualTo("Less than 15 min remaining (10%)");
     }
 
     @Test
@@ -154,9 +154,9 @@
                 false /* basedOnUsage */);
 
         // We only add special mention for the long string
-        assertThat(info).isEqualTo("About 1d 6h left based on your usage");
+        assertThat(info).isEqualTo("About 1 day, 6 hr left based on your usage");
         // shortened string should not have extra text
-        assertThat(info2).isEqualTo("About 1d 6h left (10%)");
+        assertThat(info2).isEqualTo("About 1 day, 6 hr left (10%)");
     }
 
     @Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
index 532c755..743951a 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/utils/StringUtilTest.java
@@ -43,7 +43,7 @@
     @Test
     public void testFormatElapsedTime_WithSeconds_ShowSeconds() {
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "5m 30s";
+        final String expectedTime = "5 min, 30 sec";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
                 .isEqualTo(expectedTime);
@@ -52,7 +52,7 @@
     @Test
     public void testFormatElapsedTime_NoSeconds_DoNotShowSeconds() {
         final double testMillis = 5 * DateUtils.MINUTE_IN_MILLIS + 30 * DateUtils.SECOND_IN_MILLIS;
-        final String expectedTime = "6m";
+        final String expectedTime = "6 min";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
                 .isEqualTo(expectedTime);
@@ -62,7 +62,7 @@
     public void testFormatElapsedTime_TimeMoreThanOneDay_ShowCorrectly() {
         final double testMillis = 2 * DateUtils.DAY_IN_MILLIS
                 + 4 * DateUtils.HOUR_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
-        final String expectedTime = "2d 4h 15m";
+        final String expectedTime = "2 days, 4 hr, 15 min";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
                 .isEqualTo(expectedTime);
@@ -71,7 +71,7 @@
     @Test
     public void testFormatElapsedTime_ZeroFieldsInTheMiddleDontShow() {
         final double testMillis = 2 * DateUtils.DAY_IN_MILLIS + 15 * DateUtils.MINUTE_IN_MILLIS;
-        final String expectedTime = "2d 15m";
+        final String expectedTime = "2 days, 15 min";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
                 .isEqualTo(expectedTime);
@@ -80,7 +80,7 @@
     @Test
     public void testFormatElapsedTime_FormatZero_WithSeconds() {
         final double testMillis = 0;
-        final String expectedTime = "0s";
+        final String expectedTime = "0 sec";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, true).toString())
                 .isEqualTo(expectedTime);
@@ -89,7 +89,7 @@
     @Test
     public void testFormatElapsedTime_FormatZero_NoSeconds() {
         final double testMillis = 0;
-        final String expectedTime = "0m";
+        final String expectedTime = "0 min";
 
         assertThat(StringUtil.formatElapsedTime(mContext, testMillis, false).toString())
                 .isEqualTo(expectedTime);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
index 91d9f91..d25adde 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/wifi/WifiUtilsTest.java
@@ -27,6 +27,7 @@
 import android.net.ScoredNetwork;
 import android.net.WifiKey;
 import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.os.Bundle;
 import android.os.Parcelable;
@@ -61,6 +62,8 @@
     private WifiNetworkScoreCache mockWifiNetworkScoreCache;
     @Mock
     private AccessPoint mAccessPoint;
+    @Mock
+    WifiConfiguration mWifiConfig;
 
     @Before
     public void setUp() {
@@ -98,6 +101,52 @@
         WifiUtils.getVisibilityStatus(mAccessPoint);
     }
 
+    @Test
+    public void testGetMeteredLabel_returnsCorrectValues() {
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NONE;
+        assertThat(WifiUtils.getMeteredLabel(mContext, mWifiConfig)).isEqualTo("Metered");
+
+        mWifiConfig.meteredHint = false;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
+        assertThat(WifiUtils.getMeteredLabel(mContext, mWifiConfig)).isEqualTo("Metered");
+
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
+        assertThat(WifiUtils.getMeteredLabel(mContext, mWifiConfig)).isEqualTo("Metered");
+
+        mWifiConfig.meteredHint = false;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+        assertThat(WifiUtils.getMeteredLabel(mContext, mWifiConfig)).isEqualTo("Unmetered");
+
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+        assertThat(WifiUtils.getMeteredLabel(mContext, mWifiConfig)).isEqualTo("Unmetered");
+    }
+
+    @Test
+    public void testIsMeteredOverridden_returnsCorrectValues() {
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NONE;
+        assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isFalse();
+
+        mWifiConfig.meteredHint = false;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
+        assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
+
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
+        assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
+
+        mWifiConfig.meteredHint = false;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+        assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
+
+        mWifiConfig.meteredHint = true;
+        mWifiConfig.meteredOverride = WifiConfiguration.METERED_OVERRIDE_NOT_METERED;
+        assertThat(WifiUtils.isMeteredOverridden(mWifiConfig)).isTrue();
+    }
+
     private static ArrayList<ScanResult> buildScanResultCache() {
         ArrayList<ScanResult> scanResults = new ArrayList<>();
         for (int i = 0; i < 5; i++) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 6d9da9b..f43e719 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1512,105 +1512,360 @@
 
         s.dumpHistoricalOperations(p, SecureSettingsProto.HISTORICAL_OPERATIONS);
 
-        // This uses the same order as in Settings.Secure.
+        // This uses the same order as in SecureSettingsProto.
 
-        // Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED intentionally excluded since it's deprecated.
-        // Settings.Secure.BUGREPORT_IN_POWER_MENU intentionally excluded since it's deprecated.
-        // Settings.Secure.ADB_ENABLED intentionally excluded since it's deprecated.
-        // Settings.Secure.ALLOW_MOCK_LOCATION intentionally excluded since it's deprecated.
+        final long accessibilityToken = p.start(SecureSettingsProto.ACCESSIBILITY);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_ENABLED,
+                SecureSettingsProto.Accessibility.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                SecureSettingsProto.Accessibility.ENABLED_ACCESSIBILITY_SERVICES);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
+                SecureSettingsProto.Accessibility.AUTOCLICK_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
+                SecureSettingsProto.Accessibility.AUTOCLICK_DELAY);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
+                SecureSettingsProto.Accessibility.BUTTON_TARGET_COMPONENT);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
+                SecureSettingsProto.Accessibility.CAPTIONING_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE,
+                SecureSettingsProto.Accessibility.CAPTIONING_LOCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
+                SecureSettingsProto.Accessibility.CAPTIONING_PRESET);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
+                SecureSettingsProto.Accessibility.CAPTIONING_BACKGROUND_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
+                SecureSettingsProto.Accessibility.CAPTIONING_FOREGROUND_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
+                SecureSettingsProto.Accessibility.CAPTIONING_EDGE_TYPE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
+                SecureSettingsProto.Accessibility.CAPTIONING_EDGE_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
+                SecureSettingsProto.Accessibility.CAPTIONING_WINDOW_COLOR);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE,
+                SecureSettingsProto.Accessibility.CAPTIONING_TYPEFACE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
+                SecureSettingsProto.Accessibility.CAPTIONING_FONT_SCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
+                SecureSettingsProto.Accessibility.DISPLAY_DALTONIZER_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
+                SecureSettingsProto.Accessibility.DISPLAY_DALTONIZER);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
+                SecureSettingsProto.Accessibility.DISPLAY_INVERSION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
+                SecureSettingsProto.Accessibility.DISPLAY_MAGNIFICATION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
+                SecureSettingsProto.Accessibility.DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
+                SecureSettingsProto.Accessibility.DISPLAY_MAGNIFICATION_SCALE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
+                SecureSettingsProto.Accessibility.HIGH_TEXT_CONTRAST_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
+                SecureSettingsProto.Accessibility.LARGE_POINTER_ICON);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
+                SecureSettingsProto.Accessibility.SHORTCUT_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
+                SecureSettingsProto.Accessibility.SHORTCUT_ON_LOCK_SCREEN);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
+                SecureSettingsProto.Accessibility.SHORTCUT_DIALOG_SHOWN);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+                SecureSettingsProto.Accessibility.SHORTCUT_TARGET_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SecureSettingsProto.Accessibility.SOFT_KEYBOARD_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD,
+                SecureSettingsProto.Accessibility.SPEAK_PASSWORD);
+        dumpSetting(s, p,
+                Settings.Secure.TOUCH_EXPLORATION_ENABLED,
+                SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                SecureSettingsProto.Accessibility.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
+        p.end(accessibilityToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
+                SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
+
+        final long aovToken = p.start(SecureSettingsProto.ALWAYS_ON_VPN);
+        dumpSetting(s, p,
+                Settings.Secure.ALWAYS_ON_VPN_APP,
+                SecureSettingsProto.AlwaysOnVpn.APP);
+        dumpSetting(s, p,
+                Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
+                SecureSettingsProto.AlwaysOnVpn.LOCKDOWN);
+        p.end(aovToken);
+
         dumpSetting(s, p,
                 Settings.Secure.ANDROID_ID,
                 SecureSettingsProto.ANDROID_ID);
-        // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
-        // Settings.Secure.DATA_ROAMING intentionally excluded since it's deprecated.
         dumpSetting(s, p,
-                Settings.Secure.DEFAULT_INPUT_METHOD,
-                SecureSettingsProto.DEFAULT_INPUT_METHOD);
+                Settings.Secure.ANR_SHOW_BACKGROUND,
+                SecureSettingsProto.ANR_SHOW_BACKGROUND);
+
+        final long assistToken = p.start(SecureSettingsProto.ASSIST);
         dumpSetting(s, p,
-                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
-                SecureSettingsProto.SELECTED_INPUT_METHOD_SUBTYPE);
+                Settings.Secure.ASSISTANT,
+                SecureSettingsProto.Assist.ASSISTANT);
         dumpSetting(s, p,
-                Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY,
-                SecureSettingsProto.INPUT_METHODS_SUBTYPE_HISTORY);
+                Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                SecureSettingsProto.Assist.STRUCTURE_ENABLED);
         dumpSetting(s, p,
-                Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
-                SecureSettingsProto.INPUT_METHOD_SELECTOR_VISIBILITY);
+                Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
+                SecureSettingsProto.Assist.SCREENSHOT_ENABLED);
         dumpSetting(s, p,
-                Settings.Secure.VOICE_INTERACTION_SERVICE,
-                SecureSettingsProto.VOICE_INTERACTION_SERVICE);
+                Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
+                SecureSettingsProto.Assist.DISCLOSURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_ENABLED,
+                SecureSettingsProto.Assist.GESTURE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
+                SecureSettingsProto.Assist.GESTURE_SENSITIVITY);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
+                SecureSettingsProto.Assist.GESTURE_SILENCE_ALERTS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
+                SecureSettingsProto.Assist.GESTURE_WAKE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
+                SecureSettingsProto.Assist.GESTURE_SETUP_COMPLETE);
+        p.end(assistToken);
+
+        final long autofillToken = p.start(SecureSettingsProto.AUTOFILL);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_SERVICE,
-                SecureSettingsProto.AUTOFILL_SERVICE);
+                SecureSettingsProto.Autofill.SERVICE);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
-                SecureSettingsProto.AUTOFILL_FEATURE_FIELD_CLASSIFICATION);
+                SecureSettingsProto.Autofill.FEATURE_FIELD_CLASSIFICATION);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
-                SecureSettingsProto.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE);
+                SecureSettingsProto.Autofill.USER_DATA_MAX_USER_DATA_SIZE);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
-                SecureSettingsProto.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE);
+                SecureSettingsProto.Autofill.USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT,
-                SecureSettingsProto.AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT);
+                SecureSettingsProto.Autofill.USER_DATA_MAX_CATEGORY_COUNT);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
-                SecureSettingsProto.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH);
+                SecureSettingsProto.Autofill.USER_DATA_MAX_VALUE_LENGTH);
         dumpSetting(s, p,
                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
-                SecureSettingsProto.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH);
-        // Settings.Secure.DEVICE_PROVISIONED intentionally excluded since it's deprecated.
+                SecureSettingsProto.Autofill.USER_DATA_MIN_VALUE_LENGTH);
         dumpSetting(s, p,
-                Settings.Secure.USER_SETUP_COMPLETE,
-                SecureSettingsProto.USER_SETUP_COMPLETE);
-        // Whether the current user has been set up via setup wizard (0 = false, 1 = true). This
-        // value differs from USER_SETUP_COMPLETE in that it can be reset back to 0 in case
-        // SetupWizard has been re-enabled on TV devices.
+                Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
+                SecureSettingsProto.Autofill.SERVICE_SEARCH_URI);
+        p.end(autofillToken);
+
+        final long asmToken = p.start(SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER);
         dumpSetting(s, p,
-                Settings.Secure.TV_USER_SETUP_COMPLETE,
-                SecureSettingsProto.TV_USER_SETUP_COMPLETE);
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
+                SecureSettingsProto.AutomaticStorageManager.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
+                SecureSettingsProto.AutomaticStorageManager.DAYS_TO_RETAIN);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
+                SecureSettingsProto.AutomaticStorageManager.BYTES_CLEARED);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
+                SecureSettingsProto.AutomaticStorageManager.LAST_RUN);
+        dumpSetting(s, p,
+                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
+                SecureSettingsProto.AutomaticStorageManager.TURNED_OFF_BY_POLICY);
+        p.end(asmToken);
+
+        final long backupToken = p.start(SecureSettingsProto.BACKUP);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_ENABLED,
+                SecureSettingsProto.Backup.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_AUTO_RESTORE,
+                SecureSettingsProto.Backup.AUTO_RESTORE);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_PROVISIONED,
+                SecureSettingsProto.Backup.PROVISIONED);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_TRANSPORT,
+                SecureSettingsProto.Backup.TRANSPORT);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_MANAGER_CONSTANTS,
+                SecureSettingsProto.Backup.MANAGER_CONSTANTS);
+        dumpSetting(s, p,
+                Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS,
+                SecureSettingsProto.Backup.LOCAL_TRANSPORT_PARAMETERS);
+        dumpSetting(s, p,
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE,
+                SecureSettingsProto.Backup.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+        p.end(backupToken);
+
+        // Settings.Secure.BLUETOOTH_ON intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
+                SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
+
+        final long cameraToken = p.start(SecureSettingsProto.CAMERA);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_GESTURE_DISABLED,
+                SecureSettingsProto.Camera.GESTURE_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
+                SecureSettingsProto.Camera.DOUBLE_TAP_POWER_GESTURE_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
+                SecureSettingsProto.Camera.DOUBLE_TWIST_TO_FLIP_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED,
+                SecureSettingsProto.Camera.LIFT_TRIGGER_ENABLED);
+        p.end(cameraToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.CARRIER_APPS_HANDLED,
+                SecureSettingsProto.CARRIER_APPS_HANDLED);
+        dumpSetting(s, p,
+                Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
+                SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
         dumpRepeatedSetting(s, p,
                 Settings.Secure.COMPLETED_CATEGORY_PREFIX,
                 SecureSettingsProto.COMPLETED_CATEGORIES);
         dumpSetting(s, p,
-                Settings.Secure.ENABLED_INPUT_METHODS,
-                SecureSettingsProto.ENABLED_INPUT_METHODS);
+                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
+                SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
+        dumpSetting(s, p,
+                Settings.Secure.DEVICE_PAIRED,
+                SecureSettingsProto.DEVICE_PAIRED);
+        dumpSetting(s, p,
+                Settings.Secure.DIALER_DEFAULT_APPLICATION,
+                SecureSettingsProto.DIALER_DEFAULT_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.DISPLAY_DENSITY_FORCED,
+                SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+        dumpSetting(s, p,
+                Settings.Secure.DOUBLE_TAP_TO_WAKE,
+                SecureSettingsProto.DOUBLE_TAP_TO_WAKE);
+
+        final long dozeToken = p.start(SecureSettingsProto.DOZE);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_ENABLED,
+                SecureSettingsProto.Doze.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_ALWAYS_ON,
+                SecureSettingsProto.Doze.ALWAYS_ON);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_PICK_UP,
+                SecureSettingsProto.Doze.PULSE_ON_PICK_UP);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
+                SecureSettingsProto.Doze.PULSE_ON_LONG_PRESS);
+        dumpSetting(s, p,
+                Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
+                SecureSettingsProto.Doze.PULSE_ON_DOUBLE_TAP);
+        p.end(dozeToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
+                SecureSettingsProto.EMERGENCY_ASSISTANCE_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
+                SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
+                SecureSettingsProto.IMMERSIVE_MODE_CONFIRMATIONS);
+
+        final long incallToken = p.start(SecureSettingsProto.INCALL);
+        dumpSetting(s, p,
+                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
+                SecureSettingsProto.Incall.POWER_BUTTON_BEHAVIOR);
+        dumpSetting(s, p,
+                Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
+                SecureSettingsProto.Incall.BACK_BUTTON_BEHAVIOR);
+        p.end(incallToken);
+
+        final long inputMethodsToken = p.start(SecureSettingsProto.INPUT_METHODS);
+        dumpSetting(s, p,
+                Settings.Secure.DEFAULT_INPUT_METHOD,
+                SecureSettingsProto.InputMethods.DEFAULT_INPUT_METHOD);
         dumpSetting(s, p,
                 Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
-                SecureSettingsProto.DISABLED_SYSTEM_INPUT_METHODS);
+                SecureSettingsProto.InputMethods.DISABLED_SYSTEM_INPUT_METHODS);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_INPUT_METHODS,
+                SecureSettingsProto.InputMethods.ENABLED_INPUT_METHODS);
+        dumpSetting(s, p,
+                Settings.Secure.INPUT_METHODS_SUBTYPE_HISTORY,
+                SecureSettingsProto.InputMethods.SUBTYPE_HISTORY);
+        dumpSetting(s, p,
+                Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
+                SecureSettingsProto.InputMethods.METHOD_SELECTOR_VISIBILITY);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE,
+                SecureSettingsProto.InputMethods.SELECTED_INPUT_METHOD_SUBTYPE);
         dumpSetting(s, p,
                 Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
-                SecureSettingsProto.SHOW_IME_WITH_HARD_KEYBOARD);
-        // Settings.Secure.HTTP_PROXY intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.ALWAYS_ON_VPN_APP,
-                SecureSettingsProto.ALWAYS_ON_VPN_APP);
-        dumpSetting(s, p,
-                Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
-                SecureSettingsProto.ALWAYS_ON_VPN_LOCKDOWN);
+                SecureSettingsProto.InputMethods.SHOW_IME_WITH_HARD_KEYBOARD);
+        p.end(inputMethodsToken);
+
         dumpSetting(s, p,
                 Settings.Secure.INSTALL_NON_MARKET_APPS,
                 SecureSettingsProto.INSTALL_NON_MARKET_APPS);
         dumpSetting(s, p,
-                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
-                SecureSettingsProto.UNKNOWN_SOURCES_DEFAULT_REVERSED);
+                Settings.Secure.INSTANT_APPS_ENABLED,
+                SecureSettingsProto.INSTANT_APPS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.KEYGUARD_SLICE_URI,
+                SecureSettingsProto.KEYGUARD_SLICE_URI);
+        dumpSetting(s, p,
+                Settings.Secure.LAST_SETUP_SHOWN,
+                SecureSettingsProto.LAST_SETUP_SHOWN);
+
+        final long locationToken = p.start(SecureSettingsProto.LOCATION);
         // Settings.Secure.LOCATION_PROVIDERS_ALLOWED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCATION_MODE,
-                SecureSettingsProto.LOCATION_MODE);
+                SecureSettingsProto.Location.MODE);
         dumpSetting(s, p,
                 Settings.Secure.LOCATION_CHANGER,
-                SecureSettingsProto.LOCATION_CHANGER);
+                SecureSettingsProto.Location.CHANGER);
+        p.end(locationToken);
+
+        final long lockScreenToken = p.start(SecureSettingsProto.LOCK_SCREEN);
         // Settings.Secure.LOCK_BIOMETRIC_WEAK_FLAGS intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
-                SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
         // Settings.Secure.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
         // Settings.Secure.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
         // Settings.Secure.LOCK_PATTERN_TACTICLE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
-                SecureSettingsProto.LOCK_SCREEN_LOCK_AFTER_TIMEOUT);
+                SecureSettingsProto.LockScreen.LOCK_AFTER_TIMEOUT);
         // Settings.Secure.LOCK_SCREEN_OWNER_INFO intentionally excluded since it's deprecated.
         // Settings.Secure.LOCK_SCREEN_APPWIDGET_IDS intentionally excluded since it's deprecated.
         // Settings.Secure.LOCK_SCREEN_FALLBACK_APPWIDGET_ID intentionally excluded since it's deprecated.
@@ -1618,162 +1873,371 @@
         // Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
-                SecureSettingsProto.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+                SecureSettingsProto.LockScreen.ALLOW_PRIVATE_NOTIFICATIONS);
         dumpSetting(s, p,
                 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
-                SecureSettingsProto.LOCK_SCREEN_ALLOW_REMOTE_INPUT);
+                SecureSettingsProto.LockScreen.ALLOW_REMOTE_INPUT);
         dumpSetting(s, p,
-                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING,
-                SecureSettingsProto.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING);
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                SecureSettingsProto.LockScreen.SHOW_NOTIFICATIONS);
+        p.end(lockScreenToken);
+
         dumpSetting(s, p,
-                Settings.Secure.TRUST_AGENTS_INITIALIZED,
-                SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
-        // Settings.Secure.LOGGING_ID intentionally excluded since it's deprecated.
-        // Settings.Secure.NETWORK_PREFERENCE intentionally excluded since it's deprecated.
+                Settings.Secure.LOCK_TO_APP_EXIT_LOCKED,
+                SecureSettingsProto.LOCK_TO_APP_EXIT_LOCKED);
         dumpSetting(s, p,
-                Settings.Secure.PARENTAL_CONTROL_ENABLED,
-                SecureSettingsProto.PARENTAL_CONTROL_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
-                SecureSettingsProto.PARENTAL_CONTROL_LAST_UPDATE);
-        dumpSetting(s, p,
-                Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
-                SecureSettingsProto.PARENTAL_CONTROL_REDIRECT_URL);
-        dumpSetting(s, p,
-                Settings.Secure.SETTINGS_CLASSNAME,
-                SecureSettingsProto.SETTINGS_CLASSNAME);
-        // Settings.Secure.USB_MASS_STORAGE_ENABLED intentionally excluded since it's deprecated.
-        // Settings.Secure.USE_GOOGLE_MAIL intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
-                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
-                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
-                SecureSettingsProto.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
-                SecureSettingsProto.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
-        dumpSetting(s, p,
-                Settings.Secure.TOUCH_EXPLORATION_ENABLED,
-                SecureSettingsProto.TOUCH_EXPLORATION_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                SecureSettingsProto.ENABLED_ACCESSIBILITY_SERVICES);
-        dumpSetting(s, p,
-                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
-                SecureSettingsProto.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES);
-        dumpSetting(s, p,
-                Settings.Secure.KEYGUARD_SLICE_URI,
-                SecureSettingsProto.KEYGUARD_SLICE_URI);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD,
-                SecureSettingsProto.ACCESSIBILITY_SPEAK_PASSWORD);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                SecureSettingsProto.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_LOCALE,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_LOCALE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_PRESET,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_PRESET);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_BACKGROUND_COLOR);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_FOREGROUND_COLOR);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_TYPE,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_EDGE_TYPE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_EDGE_COLOR,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_EDGE_COLOR);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_WINDOW_COLOR);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_TYPEFACE,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_TYPEFACE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_CAPTIONING_FONT_SCALE,
-                SecureSettingsProto.ACCESSIBILITY_CAPTIONING_FONT_SCALE);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER,
-                SecureSettingsProto.ACCESSIBILITY_DISPLAY_DALTONIZER);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
-                SecureSettingsProto.ACCESSIBILITY_AUTOCLICK_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
-                SecureSettingsProto.ACCESSIBILITY_AUTOCLICK_DELAY);
-        dumpSetting(s, p,
-                Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
-                SecureSettingsProto.ACCESSIBILITY_LARGE_POINTER_ICON);
+                Settings.Secure.LOCKDOWN_IN_POWER_MENU,
+                SecureSettingsProto.LOCKDOWN_IN_POWER_MENU);
         dumpSetting(s, p,
                 Settings.Secure.LONG_PRESS_TIMEOUT,
                 SecureSettingsProto.LONG_PRESS_TIMEOUT);
+
+        final long managedProfileToken = p.start(SecureSettingsProto.MANAGED_PROFILE);
+        dumpSetting(s, p,
+                Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
+                SecureSettingsProto.ManagedProfile.CONTACT_REMOTE_SEARCH);
+        p.end(managedProfileToken);
+
+        final long mountToken = p.start(SecureSettingsProto.MOUNT);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND,
+                SecureSettingsProto.Mount.PLAY_NOTIFICATION_SND);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_AUTOSTART,
+                SecureSettingsProto.Mount.UMS_AUTOSTART);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_PROMPT,
+                SecureSettingsProto.Mount.UMS_PROMPT);
+        dumpSetting(s, p,
+                Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
+                SecureSettingsProto.Mount.UMS_NOTIFY_ENABLED);
+        p.end(mountToken);
+
         dumpSetting(s, p,
                 Settings.Secure.MULTI_PRESS_TIMEOUT,
                 SecureSettingsProto.MULTI_PRESS_TIMEOUT);
+
+        final long nfcPaymentToken = p.start(SecureSettingsProto.NFC_PAYMENT);
+        dumpSetting(s, p,
+                Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
+                SecureSettingsProto.NfcPayment.DEFAULT_COMPONENT);
+        dumpSetting(s, p,
+                Settings.Secure.NFC_PAYMENT_FOREGROUND,
+                SecureSettingsProto.NfcPayment.FOREGROUND);
+        dumpSetting(s, p,
+                Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
+                SecureSettingsProto.NfcPayment.PAYMENT_SERVICE_SEARCH_URI);
+        p.end(nfcPaymentToken);
+
+        final long nightDisplayToken = p.start(SecureSettingsProto.NIGHT_DISPLAY);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
+                SecureSettingsProto.NightDisplay.ACTIVATED);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
+                SecureSettingsProto.NightDisplay.AUTO_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+                SecureSettingsProto.NightDisplay.COLOR_TEMPERATURE);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
+                SecureSettingsProto.NightDisplay.CUSTOM_START_TIME);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
+                SecureSettingsProto.NightDisplay.CUSTOM_END_TIME);
+        dumpSetting(s, p,
+                Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
+                SecureSettingsProto.NightDisplay.LAST_ACTIVATED_TIME);
+        p.end(nightDisplayToken);
+
+        final long notificationToken = p.start(SecureSettingsProto.NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
+                SecureSettingsProto.Notification.ENABLED_ASSISTANT);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
+                SecureSettingsProto.Notification.ENABLED_LISTENERS);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
+                SecureSettingsProto.Notification.ENABLED_POLICY_ACCESS_PACKAGES);
+        dumpSetting(s, p,
+                Settings.Secure.NOTIFICATION_BADGING,
+                SecureSettingsProto.Notification.BADGING);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING,
+                SecureSettingsProto.Notification.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING);
+        p.end(notificationToken);
+
+        final long packageVerifierToken = p.start(SecureSettingsProto.PACKAGE_VERIFIER);
+        dumpSetting(s, p,
+                Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
+                SecureSettingsProto.PackageVerifier.USER_CONSENT);
+        dumpSetting(s, p,
+                Settings.Secure.PACKAGE_VERIFIER_STATE,
+                SecureSettingsProto.PackageVerifier.STATE);
+        p.end(packageVerifierToken);
+
+        final long parentalControlToken = p.start(SecureSettingsProto.PARENTAL_CONTROL);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_ENABLED,
+                SecureSettingsProto.ParentalControl.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
+                SecureSettingsProto.ParentalControl.LAST_UPDATE);
+        dumpSetting(s, p,
+                Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL,
+                SecureSettingsProto.ParentalControl.REDIRECT_URL);
+        p.end(parentalControlToken);
+
+        final long printServiceToken = p.start(SecureSettingsProto.PRINT_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.PRINT_SERVICE_SEARCH_URI,
+                SecureSettingsProto.PrintService.SEARCH_URI);
         dumpSetting(s, p,
                 Settings.Secure.ENABLED_PRINT_SERVICES,
-                SecureSettingsProto.ENABLED_PRINT_SERVICES);
+                SecureSettingsProto.PrintService.ENABLED_PRINT_SERVICES);
         dumpSetting(s, p,
                 Settings.Secure.DISABLED_PRINT_SERVICES,
-                SecureSettingsProto.DISABLED_PRINT_SERVICES);
+                SecureSettingsProto.PrintService.DISABLED_PRINT_SERVICES);
+        p.end(printServiceToken);
+
+        final long qsToken = p.start(SecureSettingsProto.QS);
         dumpSetting(s, p,
-                Settings.Secure.DISPLAY_DENSITY_FORCED,
-                SecureSettingsProto.DISPLAY_DENSITY_FORCED);
+                Settings.Secure.QS_TILES,
+                SecureSettingsProto.QuickSettings.TILES);
+        dumpSetting(s, p,
+                Settings.Secure.QS_AUTO_ADDED_TILES,
+                SecureSettingsProto.QuickSettings.AUTO_ADDED_TILES);
+        p.end(qsToken);
+
+        final long rotationToken = p.start(SecureSettingsProto.ROTATION);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
+                SecureSettingsProto.Rotation.SHOW_ROTATION_SUGGESTIONS);
+        dumpSetting(s, p,
+                Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
+                SecureSettingsProto.Rotation.NUM_ROTATION_SUGGESTIONS_ACCEPTED);
+        p.end(rotationToken);
+
+        final long screensaverToken = p.start(SecureSettingsProto.SCREENSAVER);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ENABLED,
+                SecureSettingsProto.Screensaver.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_COMPONENTS,
+                SecureSettingsProto.Screensaver.COMPONENTS);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+                SecureSettingsProto.Screensaver.ACTIVATE_ON_DOCK);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+                SecureSettingsProto.Screensaver.ACTIVATE_ON_SLEEP);
+        dumpSetting(s, p,
+                Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
+                SecureSettingsProto.Screensaver.DEFAULT_COMPONENT);
+        p.end(screensaverToken);
+
+        final long searchToken = p.start(SecureSettingsProto.SEARCH);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
+                SecureSettingsProto.Search.GLOBAL_SEARCH_ACTIVITY);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_NUM_PROMOTED_SOURCES,
+                SecureSettingsProto.Search.NUM_PROMOTED_SOURCES);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_RESULTS_TO_DISPLAY,
+                SecureSettingsProto.Search.MAX_RESULTS_TO_DISPLAY);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
+                SecureSettingsProto.Search.MAX_RESULTS_PER_SOURCE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
+                SecureSettingsProto.Search.WEB_RESULTS_OVERRIDE_LIMIT);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS,
+                SecureSettingsProto.Search.PROMOTED_SOURCE_DEADLINE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SOURCE_TIMEOUT_MILLIS,
+                SecureSettingsProto.Search.SOURCE_TIMEOUT_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PREFILL_MILLIS,
+                SecureSettingsProto.Search.PREFILL_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_STAT_AGE_MILLIS,
+                SecureSettingsProto.Search.MAX_STAT_AGE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS,
+                SecureSettingsProto.Search.MAX_SOURCE_EVENT_AGE_MILLIS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING,
+                SecureSettingsProto.Search.MIN_IMPRESSIONS_FOR_SOURCE_RANKING);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING,
+                SecureSettingsProto.Search.MIN_CLICKS_FOR_SOURCE_RANKING);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_MAX_SHORTCUTS_RETURNED,
+                SecureSettingsProto.Search.MAX_SHORTCUTS_RETURNED);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_QUERY_THREAD_CORE_POOL_SIZE,
+                SecureSettingsProto.Search.QUERY_THREAD_CORE_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_QUERY_THREAD_MAX_POOL_SIZE,
+                SecureSettingsProto.Search.QUERY_THREAD_MAX_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE,
+                SecureSettingsProto.Search.SHORTCUT_REFRESH_CORE_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE,
+                SecureSettingsProto.Search.SHORTCUT_REFRESH_MAX_POOL_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_THREAD_KEEPALIVE_SECONDS,
+                SecureSettingsProto.Search.THREAD_KEEPALIVE_SECONDS);
+        dumpSetting(s, p,
+                Settings.Secure.SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT,
+                SecureSettingsProto.Search.PER_SOURCE_CONCURRENT_QUERY_LIMIT);
+        p.end(searchToken);
+
+        final long spellCheckerToken = p.start(SecureSettingsProto.SPELL_CHECKER);
+        dumpSetting(s, p,
+                Settings.Secure.SPELL_CHECKER_ENABLED,
+                SecureSettingsProto.SpellChecker.ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_SPELL_CHECKER,
+                SecureSettingsProto.SpellChecker.SELECTED);
+        dumpSetting(s, p,
+                Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
+                SecureSettingsProto.SpellChecker.SELECTED_SUBTYPE);
+        p.end(spellCheckerToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.SETTINGS_CLASSNAME,
+                SecureSettingsProto.SETTINGS_CLASSNAME);
+        dumpSetting(s, p,
+                Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
+                SecureSettingsProto.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION);
+        dumpSetting(s, p,
+                Settings.Secure.SKIP_FIRST_USE_HINTS,
+                SecureSettingsProto.SKIP_FIRST_USE_HINTS);
+        dumpSetting(s, p,
+                Settings.Secure.SLEEP_TIMEOUT,
+                SecureSettingsProto.SLEEP_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.Secure.SMS_DEFAULT_APPLICATION,
+                SecureSettingsProto.SMS_DEFAULT_APPLICATION);
+        dumpSetting(s, p,
+                Settings.Secure.SYNC_PARENT_SOUNDS,
+                SecureSettingsProto.SYNC_PARENT_SOUNDS);
+        dumpSetting(s, p,
+                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
+                SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.TRUST_AGENTS_INITIALIZED,
+                SecureSettingsProto.TRUST_AGENTS_INITIALIZED);
+
+        final long ttsToken = p.start(SecureSettingsProto.TTS);
         // Settings.Secure.TTS_USE_DEFAULTS intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_RATE,
-                SecureSettingsProto.TTS_DEFAULT_RATE);
+                SecureSettingsProto.Tts.DEFAULT_RATE);
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_PITCH,
-                SecureSettingsProto.TTS_DEFAULT_PITCH);
+                SecureSettingsProto.Tts.DEFAULT_PITCH);
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_SYNTH,
-                SecureSettingsProto.TTS_DEFAULT_SYNTH);
+                SecureSettingsProto.Tts.DEFAULT_SYNTH);
         // Settings.Secure.TTS_DEFAULT_LANG intentionally excluded since it's deprecated.
         // Settings.Secure.TTS_DEFAULT_COUNTRY intentionally excluded since it's deprecated.
         // Settings.Secure.TTS_DEFAULT_VARIANT intentionally excluded since it's deprecated.
         dumpSetting(s, p,
                 Settings.Secure.TTS_DEFAULT_LOCALE,
-                SecureSettingsProto.TTS_DEFAULT_LOCALE);
+                SecureSettingsProto.Tts.DEFAULT_LOCALE);
         dumpSetting(s, p,
                 Settings.Secure.TTS_ENABLED_PLUGINS,
-                SecureSettingsProto.TTS_ENABLED_PLUGINS);
+                SecureSettingsProto.Tts.ENABLED_PLUGINS);
+        p.end(ttsToken);
+
+        final long ttyToken = p.start(SecureSettingsProto.TTY);
+        dumpSetting(s, p,
+                Settings.Secure.TTY_MODE_ENABLED,
+                SecureSettingsProto.Tty.TTY_MODE_ENABLED);
+        dumpSetting(s, p,
+                Settings.Secure.PREFERRED_TTY_MODE,
+                SecureSettingsProto.Tty.PREFERRED_TTY_MODE);
+        p.end(ttyToken);
+
+        final long tvToken = p.start(SecureSettingsProto.TV);
+        // Whether the current user has been set up via setup wizard (0 = false, 1 = true). This
+        // value differs from USER_SETUP_COMPLETE in that it can be reset back to 0 in case
+        // SetupWizard has been re-enabled on TV devices.
+        dumpSetting(s, p,
+                Settings.Secure.TV_USER_SETUP_COMPLETE,
+                SecureSettingsProto.Tv.USER_SETUP_COMPLETE);
+        dumpSetting(s, p,
+                Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
+                SecureSettingsProto.Tv.INPUT_HIDDEN_INPUTS);
+        dumpSetting(s, p,
+                Settings.Secure.TV_INPUT_CUSTOM_LABELS,
+                SecureSettingsProto.Tv.INPUT_CUSTOM_LABELS);
+        p.end(tvToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.UI_NIGHT_MODE,
+                SecureSettingsProto.UI_NIGHT_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.UNKNOWN_SOURCES_DEFAULT_REVERSED,
+                SecureSettingsProto.UNKNOWN_SOURCES_DEFAULT_REVERSED);
+        dumpSetting(s, p,
+                Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
+                SecureSettingsProto.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED);
+        dumpSetting(s, p,
+                Settings.Secure.USER_SETUP_COMPLETE,
+                SecureSettingsProto.USER_SETUP_COMPLETE);
+
+        final long voiceToken = p.start(SecureSettingsProto.VOICE);
+        dumpSetting(s, p,
+                Settings.Secure.VOICE_INTERACTION_SERVICE,
+                SecureSettingsProto.Voice.INTERACTION_SERVICE);
+        dumpSetting(s, p,
+                Settings.Secure.VOICE_RECOGNITION_SERVICE,
+                SecureSettingsProto.Voice.RECOGNITION_SERVICE);
+        p.end(voiceToken);
+
+        final long volumeToken = p.start(SecureSettingsProto.VOLUME);
+        dumpSetting(s, p,
+                Settings.Secure.VOLUME_HUSH_GESTURE,
+                SecureSettingsProto.Volume.HUSH_GESTURE);
+        dumpSetting(s, p,
+                Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
+                SecureSettingsProto.Volume.UNSAFE_VOLUME_MUSIC_ACTIVE_MS);
+        p.end(volumeToken);
+
+        final long vrToken = p.start(SecureSettingsProto.VR);
+        dumpSetting(s, p,
+                Settings.Secure.VR_DISPLAY_MODE,
+                SecureSettingsProto.Vr.DISPLAY_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.ENABLED_VR_LISTENERS,
+                SecureSettingsProto.Vr.ENABLED_LISTENERS);
+        p.end(vrToken);
+
+        dumpSetting(s, p,
+                Settings.Secure.WAKE_GESTURE_ENABLED,
+                SecureSettingsProto.WAKE_GESTURE_ENABLED);
+
+        // Please insert new settings using the same order as in SecureSettingsProto.
+        p.end(token);
+
+        // Settings.Secure.DEVELOPMENT_SETTINGS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.BUGREPORT_IN_POWER_MENU intentionally excluded since it's deprecated.
+        // Settings.Secure.ADB_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.ALLOW_MOCK_LOCATION intentionally excluded since it's deprecated.
+        // Settings.Secure.DATA_ROAMING intentionally excluded since it's deprecated.
+        // Settings.Secure.DEVICE_PROVISIONED intentionally excluded since it's deprecated.
+        // Settings.Secure.HTTP_PROXY intentionally excluded since it's deprecated.
+        // Settings.Secure.LOGGING_ID intentionally excluded since it's deprecated.
+        // Settings.Secure.NETWORK_PREFERENCE intentionally excluded since it's deprecated.
+        // Settings.Secure.USB_MASS_STORAGE_ENABLED intentionally excluded since it's deprecated.
+        // Settings.Secure.USE_GOOGLE_MAIL intentionally excluded since it's deprecated.
         // Settings.Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON intentionally excluded since it's deprecated.
         // Settings.Secure.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY intentionally excluded since it's deprecated.
         // Settings.Secure.WIFI_NUM_OPEN_NETWORKS_KEPT intentionally excluded since it's deprecated.
@@ -1792,365 +2256,11 @@
         // Settings.Secure.WIFI_WATCHDOG_PING_TIMEOUT_MS intentionally excluded since it's deprecated.
         // Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT intentionally excluded since it's deprecated.
         // Settings.Secure.WIFI_MOBILE_DATA_TRANSITION_WAKELOCK_TIMEOUT_MS intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS,
-                SecureSettingsProto.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS);
         // Settings.Secure.BACKGROUND_DATA intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
-                SecureSettingsProto.ALLOWED_GEOLOCATION_ORIGINS);
-        dumpSetting(s, p,
-                Settings.Secure.PREFERRED_TTY_MODE,
-                SecureSettingsProto.PREFERRED_TTY_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
-                SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.TTY_MODE_ENABLED,
-                SecureSettingsProto.TTY_MODE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_ENABLED,
-                SecureSettingsProto.BACKUP_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_AUTO_RESTORE,
-                SecureSettingsProto.BACKUP_AUTO_RESTORE);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_PROVISIONED,
-                SecureSettingsProto.BACKUP_PROVISIONED);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_TRANSPORT,
-                SecureSettingsProto.BACKUP_TRANSPORT);
-        dumpSetting(s, p,
-                Settings.Secure.LAST_SETUP_SHOWN,
-                SecureSettingsProto.LAST_SETUP_SHOWN);
         // Settings.Secure.WIFI_IDLE_MS intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_GLOBAL_SEARCH_ACTIVITY,
-                SecureSettingsProto.SEARCH_GLOBAL_SEARCH_ACTIVITY);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_NUM_PROMOTED_SOURCES,
-                SecureSettingsProto.SEARCH_NUM_PROMOTED_SOURCES);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MAX_RESULTS_TO_DISPLAY,
-                SecureSettingsProto.SEARCH_MAX_RESULTS_TO_DISPLAY);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MAX_RESULTS_PER_SOURCE,
-                SecureSettingsProto.SEARCH_MAX_RESULTS_PER_SOURCE);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT,
-                SecureSettingsProto.SEARCH_WEB_RESULTS_OVERRIDE_LIMIT);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS,
-                SecureSettingsProto.SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_SOURCE_TIMEOUT_MILLIS,
-                SecureSettingsProto.SEARCH_SOURCE_TIMEOUT_MILLIS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_PREFILL_MILLIS,
-                SecureSettingsProto.SEARCH_PREFILL_MILLIS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MAX_STAT_AGE_MILLIS,
-                SecureSettingsProto.SEARCH_MAX_STAT_AGE_MILLIS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS,
-                SecureSettingsProto.SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING,
-                SecureSettingsProto.SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING,
-                SecureSettingsProto.SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_MAX_SHORTCUTS_RETURNED,
-                SecureSettingsProto.SEARCH_MAX_SHORTCUTS_RETURNED);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_QUERY_THREAD_CORE_POOL_SIZE,
-                SecureSettingsProto.SEARCH_QUERY_THREAD_CORE_POOL_SIZE);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_QUERY_THREAD_MAX_POOL_SIZE,
-                SecureSettingsProto.SEARCH_QUERY_THREAD_MAX_POOL_SIZE);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE,
-                SecureSettingsProto.SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE,
-                SecureSettingsProto.SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_THREAD_KEEPALIVE_SECONDS,
-                SecureSettingsProto.SEARCH_THREAD_KEEPALIVE_SECONDS);
-        dumpSetting(s, p,
-                Settings.Secure.SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT,
-                SecureSettingsProto.SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT);
-        dumpSetting(s, p,
-                Settings.Secure.MOUNT_PLAY_NOTIFICATION_SND,
-                SecureSettingsProto.MOUNT_PLAY_NOTIFICATION_SND);
-        dumpSetting(s, p,
-                Settings.Secure.MOUNT_UMS_AUTOSTART,
-                SecureSettingsProto.MOUNT_UMS_AUTOSTART);
-        dumpSetting(s, p,
-                Settings.Secure.MOUNT_UMS_PROMPT,
-                SecureSettingsProto.MOUNT_UMS_PROMPT);
-        dumpSetting(s, p,
-                Settings.Secure.MOUNT_UMS_NOTIFY_ENABLED,
-                SecureSettingsProto.MOUNT_UMS_NOTIFY_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ANR_SHOW_BACKGROUND,
-                SecureSettingsProto.ANR_SHOW_BACKGROUND);
-        dumpSetting(s, p,
-                Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION,
-                SecureSettingsProto.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION);
-        dumpSetting(s, p,
-                Settings.Secure.VOICE_RECOGNITION_SERVICE,
-                SecureSettingsProto.VOICE_RECOGNITION_SERVICE);
-        dumpSetting(s, p,
-                Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
-                SecureSettingsProto.PACKAGE_VERIFIER_USER_CONSENT);
-        dumpSetting(s, p,
-                Settings.Secure.SELECTED_SPELL_CHECKER,
-                SecureSettingsProto.SELECTED_SPELL_CHECKER);
-        dumpSetting(s, p,
-                Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE,
-                SecureSettingsProto.SELECTED_SPELL_CHECKER_SUBTYPE);
-        dumpSetting(s, p,
-                Settings.Secure.SPELL_CHECKER_ENABLED,
-                SecureSettingsProto.SPELL_CHECKER_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
-                SecureSettingsProto.INCALL_POWER_BUTTON_BEHAVIOR);
-        dumpSetting(s, p,
-                Settings.Secure.INCALL_BACK_BUTTON_BEHAVIOR,
-                SecureSettingsProto.INCALL_BACK_BUTTON_BEHAVIOR);
-        dumpSetting(s, p,
-                Settings.Secure.WAKE_GESTURE_ENABLED,
-                SecureSettingsProto.WAKE_GESTURE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.DOZE_ENABLED,
-                SecureSettingsProto.DOZE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.DOZE_ALWAYS_ON,
-                SecureSettingsProto.DOZE_ALWAYS_ON);
-        dumpSetting(s, p,
-                Settings.Secure.DOZE_PULSE_ON_PICK_UP,
-                SecureSettingsProto.DOZE_PULSE_ON_PICK_UP);
-        dumpSetting(s, p,
-                Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
-                SecureSettingsProto.DOZE_PULSE_ON_LONG_PRESS);
-        dumpSetting(s, p,
-                Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
-                SecureSettingsProto.DOZE_PULSE_ON_DOUBLE_TAP);
-        dumpSetting(s, p,
-                Settings.Secure.UI_NIGHT_MODE,
-                SecureSettingsProto.UI_NIGHT_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.SCREENSAVER_ENABLED,
-                SecureSettingsProto.SCREENSAVER_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.SCREENSAVER_COMPONENTS,
-                SecureSettingsProto.SCREENSAVER_COMPONENTS);
-        dumpSetting(s, p,
-                Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
-                SecureSettingsProto.SCREENSAVER_ACTIVATE_ON_DOCK);
-        dumpSetting(s, p,
-                Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
-                SecureSettingsProto.SCREENSAVER_ACTIVATE_ON_SLEEP);
-        dumpSetting(s, p,
-                Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT,
-                SecureSettingsProto.SCREENSAVER_DEFAULT_COMPONENT);
-        dumpSetting(s, p,
-                Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
-                SecureSettingsProto.NFC_PAYMENT_DEFAULT_COMPONENT);
-        dumpSetting(s, p,
-                Settings.Secure.NFC_PAYMENT_FOREGROUND,
-                SecureSettingsProto.NFC_PAYMENT_FOREGROUND);
-        dumpSetting(s, p,
-                Settings.Secure.SMS_DEFAULT_APPLICATION,
-                SecureSettingsProto.SMS_DEFAULT_APPLICATION);
-        dumpSetting(s, p,
-                Settings.Secure.DIALER_DEFAULT_APPLICATION,
-                SecureSettingsProto.DIALER_DEFAULT_APPLICATION);
-        dumpSetting(s, p,
-                Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
-                SecureSettingsProto.EMERGENCY_ASSISTANCE_APPLICATION);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_STRUCTURE_ENABLED,
-                SecureSettingsProto.ASSIST_STRUCTURE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
-                SecureSettingsProto.ASSIST_SCREENSHOT_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
-                SecureSettingsProto.ASSIST_DISCLOSURE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.SHOW_ROTATION_SUGGESTIONS,
-                SecureSettingsProto.SHOW_ROTATION_SUGGESTIONS);
-        dumpSetting(s, p,
-                Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
-                SecureSettingsProto.NUM_ROTATION_SUGGESTIONS_ACCEPTED);
-        dumpSetting(s, p,
-                Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT,
-                SecureSettingsProto.ENABLED_NOTIFICATION_ASSISTANT);
-        dumpSetting(s, p,
-                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-                SecureSettingsProto.ENABLED_NOTIFICATION_LISTENERS);
-        dumpSetting(s, p,
-                Settings.Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES,
-                SecureSettingsProto.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES);
-        dumpSetting(s, p,
-                Settings.Secure.SYNC_PARENT_SOUNDS,
-                SecureSettingsProto.SYNC_PARENT_SOUNDS);
-        dumpSetting(s, p,
-                Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS,
-                SecureSettingsProto.IMMERSIVE_MODE_CONFIRMATIONS);
-        dumpSetting(s, p,
-                Settings.Secure.PRINT_SERVICE_SEARCH_URI,
-                SecureSettingsProto.PRINT_SERVICE_SEARCH_URI);
-        dumpSetting(s, p,
-                Settings.Secure.PAYMENT_SERVICE_SEARCH_URI,
-                SecureSettingsProto.PAYMENT_SERVICE_SEARCH_URI);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
-                SecureSettingsProto.AUTOFILL_SERVICE_SEARCH_URI);
-        dumpSetting(s, p,
-                Settings.Secure.SKIP_FIRST_USE_HINTS,
-                SecureSettingsProto.SKIP_FIRST_USE_HINTS);
-        dumpSetting(s, p,
-                Settings.Secure.UNSAFE_VOLUME_MUSIC_ACTIVE_MS,
-                SecureSettingsProto.UNSAFE_VOLUME_MUSIC_ACTIVE_MS);
-        dumpSetting(s, p,
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                SecureSettingsProto.LOCK_SCREEN_SHOW_NOTIFICATIONS);
-        dumpSetting(s, p,
-                Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
-                SecureSettingsProto.TV_INPUT_HIDDEN_INPUTS);
-        dumpSetting(s, p,
-                Settings.Secure.TV_INPUT_CUSTOM_LABELS,
-                SecureSettingsProto.TV_INPUT_CUSTOM_LABELS);
-        dumpSetting(s, p,
-                Settings.Secure.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED,
-                SecureSettingsProto.USB_AUDIO_AUTOMATIC_ROUTING_DISABLED);
-        dumpSetting(s, p,
-                Settings.Secure.SLEEP_TIMEOUT,
-                SecureSettingsProto.SLEEP_TIMEOUT);
-        dumpSetting(s, p,
-                Settings.Secure.DOUBLE_TAP_TO_WAKE,
-                SecureSettingsProto.DOUBLE_TAP_TO_WAKE);
-        dumpSetting(s, p,
-                Settings.Secure.ASSISTANT,
-                SecureSettingsProto.ASSISTANT);
-        dumpSetting(s, p,
-                Settings.Secure.CAMERA_GESTURE_DISABLED,
-                SecureSettingsProto.CAMERA_GESTURE_DISABLED);
-        dumpSetting(s, p,
-                Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED,
-                SecureSettingsProto.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED);
-        dumpSetting(s, p,
-                Settings.Secure.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED,
-                SecureSettingsProto.CAMERA_DOUBLE_TWIST_TO_FLIP_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED,
-                SecureSettingsProto.CAMERA_LIFT_TRIGGER_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_GESTURE_ENABLED,
-                SecureSettingsProto.ASSIST_GESTURE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_GESTURE_SENSITIVITY,
-                SecureSettingsProto.ASSIST_GESTURE_SENSITIVITY);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
-                SecureSettingsProto.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_GESTURE_WAKE_ENABLED,
-                SecureSettingsProto.ASSIST_GESTURE_WAKE_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.ASSIST_GESTURE_SETUP_COMPLETE,
-                SecureSettingsProto.ASSIST_GESTURE_SETUP_COMPLETE);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
-                SecureSettingsProto.NIGHT_DISPLAY_ACTIVATED);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_AUTO_MODE,
-                SecureSettingsProto.NIGHT_DISPLAY_AUTO_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
-                SecureSettingsProto.NIGHT_DISPLAY_COLOR_TEMPERATURE);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_CUSTOM_START_TIME,
-                SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_START_TIME);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_CUSTOM_END_TIME,
-                SecureSettingsProto.NIGHT_DISPLAY_CUSTOM_END_TIME);
-        dumpSetting(s, p,
-                Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
-                SecureSettingsProto.NIGHT_DISPLAY_LAST_ACTIVATED_TIME);
-        dumpSetting(s, p,
-                Settings.Secure.ENABLED_VR_LISTENERS,
-                SecureSettingsProto.ENABLED_VR_LISTENERS);
-        dumpSetting(s, p,
-                Settings.Secure.VR_DISPLAY_MODE,
-                SecureSettingsProto.VR_DISPLAY_MODE);
-        dumpSetting(s, p,
-                Settings.Secure.CARRIER_APPS_HANDLED,
-                SecureSettingsProto.CARRIER_APPS_HANDLED);
-        dumpSetting(s, p,
-                Settings.Secure.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH,
-                SecureSettingsProto.MANAGED_PROFILE_CONTACT_REMOTE_SEARCH);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
-                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN,
-                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
-                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_LAST_RUN,
-                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_LAST_RUN);
-        dumpSetting(s, p,
-                Settings.Secure.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY,
-                SecureSettingsProto.AUTOMATIC_STORAGE_MANAGER_TURNED_OFF_BY_POLICY);
-        dumpSetting(s, p,
-                Settings.Secure.SYSTEM_NAVIGATION_KEYS_ENABLED,
-                SecureSettingsProto.SYSTEM_NAVIGATION_KEYS_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.QS_TILES,
-                SecureSettingsProto.QS_TILES);
-        dumpSetting(s, p,
-                Settings.Secure.INSTANT_APPS_ENABLED,
-                SecureSettingsProto.INSTANT_APPS_ENABLED);
-        dumpSetting(s, p,
-                Settings.Secure.DEVICE_PAIRED,
-                SecureSettingsProto.DEVICE_PAIRED);
-        dumpSetting(s, p,
-                Settings.Secure.PACKAGE_VERIFIER_STATE,
-                SecureSettingsProto.PACKAGE_VERIFIER_STATE);
-        dumpSetting(s, p,
-                Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG,
-                SecureSettingsProto.CMAS_ADDITIONAL_BROADCAST_PKG);
-        dumpSetting(s, p,
-                Settings.Secure.NOTIFICATION_BADGING,
-                SecureSettingsProto.NOTIFICATION_BADGING);
-        dumpSetting(s, p,
-                Settings.Secure.QS_AUTO_ADDED_TILES,
-                SecureSettingsProto.QS_AUTO_ADDED_TILES);
-        dumpSetting(s, p,
-                Settings.Secure.LOCKDOWN_IN_POWER_MENU,
-                SecureSettingsProto.LOCKDOWN_IN_POWER_MENU);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_MANAGER_CONSTANTS,
-                SecureSettingsProto.BACKUP_MANAGER_CONSTANTS);
-        dumpSetting(s, p,
-                Settings.Secure.BACKUP_LOCAL_TRANSPORT_PARAMETERS,
-                SecureSettingsProto.BACKUP_LOCAL_TRANSPORT_PARAMETERS);
-        dumpSetting(s, p,
-                Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING,
-                SecureSettingsProto.BLUETOOTH_ON_WHILE_DRIVING);
-        dumpSetting(s, p,
-                Settings.Secure.VOLUME_HUSH_GESTURE,
-                SecureSettingsProto.VOLUME_HUSH_GESTURE);
-        // Please insert new settings using the same order as in Settings.Secure.
 
-        p.end(token);
+
+        // Please insert new settings using the same order as in SecureSettingsProto.
     }
 
     private static void dumpProtoSystemSettingsLocked(
@@ -2159,15 +2269,293 @@
 
         s.dumpHistoricalOperations(p, SystemSettingsProto.HISTORICAL_OPERATIONS);
 
-        // This uses the same order as in Settings.System.
+        // This uses the same order as in SystemSettingsProto.
 
-        // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.ADVANCED_SETTINGS,
+                SystemSettingsProto.ADVANCED_SETTINGS);
+
+        final long alarmToken = p.start(SystemSettingsProto.ALARM);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT,
+                SystemSettingsProto.Alarm.DEFAULT_URI);
+        dumpSetting(s, p,
+                Settings.System.ALARM_ALERT_CACHE,
+                SystemSettingsProto.Alarm.ALERT_CACHE);
+        // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
+        p.end(alarmToken);
+
+        final long bluetoothToken = p.start(SystemSettingsProto.BLUETOOTH);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY,
+                SystemSettingsProto.Bluetooth.DISCOVERABILITY);
+        dumpSetting(s, p,
+                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
+                SystemSettingsProto.Bluetooth.DISCOVERABILITY_TIMEOUT_SECS);
+        p.end(bluetoothToken);
+
+        dumpSetting(s, p,
+                Settings.System.DATE_FORMAT,
+                SystemSettingsProto.DATE_FORMAT);
+        dumpSetting(s, p,
+                Settings.System.DISPLAY_COLOR_MODE,
+                SystemSettingsProto.DISPLAY_COLOR_MODE);
+
+        final long devOptionsToken = p.start(SystemSettingsProto.DEVELOPER_OPTIONS);
+        dumpSetting(s, p,
+                Settings.System.SHOW_TOUCHES,
+                SystemSettingsProto.DevOptions.SHOW_TOUCHES);
+        dumpSetting(s, p,
+                Settings.System.POINTER_LOCATION,
+                SystemSettingsProto.DevOptions.POINTER_LOCATION);
+        dumpSetting(s, p,
+                Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
+                SystemSettingsProto.DevOptions.WINDOW_ORIENTATION_LISTENER_LOG);
+        p.end(devOptionsToken);
+
+        final long dtmfToneToken = p.start(SystemSettingsProto.DTMF_TONE);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_WHEN_DIALING,
+                SystemSettingsProto.DtmfTone.PLAY_WHEN_DIALING);
+        dumpSetting(s, p,
+                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
+                SystemSettingsProto.DtmfTone.TYPE_PLAYED_WHEN_DIALING);
+        p.end(dtmfToneToken);
+
+        dumpSetting(s, p,
+                Settings.System.EGG_MODE,
+                SystemSettingsProto.EGG_MODE);
         dumpSetting(s, p,
                 Settings.System.END_BUTTON_BEHAVIOR,
                 SystemSettingsProto.END_BUTTON_BEHAVIOR);
         dumpSetting(s, p,
-                Settings.System.ADVANCED_SETTINGS,
-                SystemSettingsProto.ADVANCED_SETTINGS);
+                Settings.System.FONT_SCALE,
+                SystemSettingsProto.FONT_SCALE);
+
+        final long hapticFeedbackToken = p.start(SystemSettingsProto.HAPTIC_FEEDBACK);
+        dumpSetting(s, p,
+                Settings.System.HAPTIC_FEEDBACK_ENABLED,
+                SystemSettingsProto.HapticFeedback.ENABLED);
+        dumpSetting(s, p,
+                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
+                SystemSettingsProto.HapticFeedback.INTENSITY);
+        p.end(hapticFeedbackToken);
+
+        dumpSetting(s, p,
+                Settings.System.HEARING_AID,
+                SystemSettingsProto.HEARING_AID);
+        dumpSetting(s, p,
+                Settings.System.LOCK_TO_APP_ENABLED,
+                SystemSettingsProto.LOCK_TO_APP_ENABLED);
+
+        final long lockscreenToken = p.start(SystemSettingsProto.LOCKSCREEN);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
+                SystemSettingsProto.Lockscreen.SOUNDS_ENABLED);
+        dumpSetting(s, p,
+                Settings.System.LOCKSCREEN_DISABLED,
+                SystemSettingsProto.Lockscreen.DISABLED);
+        p.end(lockscreenToken);
+
+        dumpSetting(s, p,
+                Settings.System.MEDIA_BUTTON_RECEIVER,
+                SystemSettingsProto.MEDIA_BUTTON_RECEIVER);
+
+        final long notificationToken = p.start(SystemSettingsProto.NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND,
+                SystemSettingsProto.Notification.SOUND);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_SOUND_CACHE,
+                SystemSettingsProto.Notification.SOUND_CACHE);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_LIGHT_PULSE,
+                SystemSettingsProto.Notification.LIGHT_PULSE);
+        dumpSetting(s, p,
+                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
+                SystemSettingsProto.Notification.VIBRATION_INTENSITY);
+        // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
+        p.end(notificationToken);
+
+        dumpSetting(s, p,
+                Settings.System.POINTER_SPEED,
+                SystemSettingsProto.POINTER_SPEED);
+
+        final long ringtoneToken = p.start(SystemSettingsProto.RINGTONE);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE,
+                SystemSettingsProto.Ringtone.DEFAULT_URI);
+        dumpSetting(s, p,
+                Settings.System.RINGTONE_CACHE,
+                SystemSettingsProto.Ringtone.CACHE);
+        p.end(ringtoneToken);
+
+        final long rotationToken = p.start(SystemSettingsProto.ROTATION);
+        dumpSetting(s, p,
+                Settings.System.ACCELEROMETER_ROTATION,
+                SystemSettingsProto.Rotation.ACCELEROMETER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.USER_ROTATION,
+                SystemSettingsProto.Rotation.USER_ROTATION);
+        dumpSetting(s, p,
+                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
+                SystemSettingsProto.Rotation.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
+        p.end(rotationToken);
+
+        dumpSetting(s, p,
+                Settings.System.RTT_CALLING_MODE,
+                SystemSettingsProto.RTT_CALLING_MODE);
+
+        final long screenToken = p.start(SystemSettingsProto.SCREEN);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_OFF_TIMEOUT,
+                SystemSettingsProto.Screen.OFF_TIMEOUT);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS,
+                SystemSettingsProto.Screen.BRIGHTNESS);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
+                SystemSettingsProto.Screen.BRIGHTNESS_FOR_VR);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                SystemSettingsProto.Screen.BRIGHTNESS_MODE);
+        dumpSetting(s, p,
+                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
+                SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ);
+        p.end(screenToken);
+
+        dumpSetting(s, p,
+                Settings.System.SETUP_WIZARD_HAS_RUN,
+                SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
+        dumpSetting(s, p,
+                Settings.System.SHOW_BATTERY_PERCENT,
+                SystemSettingsProto.SHOW_BATTERY_PERCENT);
+        dumpSetting(s, p,
+                Settings.System.SHOW_GTALK_SERVICE_STATUS,
+                SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
+        // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
+
+        final long sipToken = p.start(SystemSettingsProto.SIP);
+        dumpSetting(s, p,
+                Settings.System.SIP_RECEIVE_CALLS,
+                SystemSettingsProto.Sip.RECEIVE_CALLS);
+        dumpSetting(s, p,
+                Settings.System.SIP_CALL_OPTIONS,
+                SystemSettingsProto.Sip.CALL_OPTIONS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ALWAYS,
+                SystemSettingsProto.Sip.ALWAYS);
+        dumpSetting(s, p,
+                Settings.System.SIP_ADDRESS_ONLY,
+                SystemSettingsProto.Sip.ADDRESS_ONLY);
+        // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
+        p.end(sipToken);
+
+        dumpSetting(s, p,
+                Settings.System.SOUND_EFFECTS_ENABLED,
+                SystemSettingsProto.SOUND_EFFECTS_ENABLED);
+        // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
+        // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
+        // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.SYSTEM_LOCALES,
+                SystemSettingsProto.SYSTEM_LOCALES);
+
+        final long textToken = p.start(SystemSettingsProto.TEXT);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_REPLACE,
+                SystemSettingsProto.Text.AUTO_REPLACE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_CAPS,
+                SystemSettingsProto.Text.AUTO_CAPS);
+        dumpSetting(s, p,
+                Settings.System.TEXT_AUTO_PUNCTUATE,
+                SystemSettingsProto.Text.AUTO_PUNCTUATE);
+        dumpSetting(s, p,
+                Settings.System.TEXT_SHOW_PASSWORD,
+                SystemSettingsProto.Text.SHOW_PASSWORD);
+        p.end(textToken);
+
+        // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
+        // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
+        dumpSetting(s, p,
+                Settings.System.TIME_12_24,
+                SystemSettingsProto.TIME_12_24);
+        dumpSetting(s, p,
+                Settings.System.TTY_MODE,
+                SystemSettingsProto.TTY_MODE);
+
+        final long vibrateToken = p.start(SystemSettingsProto.VIBRATE);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_ON,
+                SystemSettingsProto.Vibrate.ON);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_INPUT_DEVICES,
+                SystemSettingsProto.Vibrate.INPUT_DEVICES);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_IN_SILENT,
+                SystemSettingsProto.Vibrate.IN_SILENT);
+        dumpSetting(s, p,
+                Settings.System.VIBRATE_WHEN_RINGING,
+                SystemSettingsProto.Vibrate.WHEN_RINGING);
+        p.end(vibrateToken);
+
+        final long volumeToken = p.start(SystemSettingsProto.VOLUME);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_RING,
+                SystemSettingsProto.Volume.RING);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_SYSTEM,
+                SystemSettingsProto.Volume.SYSTEM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_VOICE,
+                SystemSettingsProto.Volume.VOICE);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MUSIC,
+                SystemSettingsProto.Volume.MUSIC);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_ALARM,
+                SystemSettingsProto.Volume.ALARM);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_NOTIFICATION,
+                SystemSettingsProto.Volume.NOTIFICATION);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_BLUETOOTH_SCO,
+                SystemSettingsProto.Volume.BLUETOOTH_SCO);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_ACCESSIBILITY,
+                SystemSettingsProto.Volume.ACCESSIBILITY);
+        dumpSetting(s, p,
+                Settings.System.VOLUME_MASTER,
+                SystemSettingsProto.Volume.MASTER);
+        dumpSetting(s, p,
+                Settings.System.MASTER_MONO,
+                SystemSettingsProto.Volume.MASTER_MONO);
+        dumpSetting(s, p,
+                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+                SystemSettingsProto.Volume.MODE_RINGER_STREAMS_AFFECTED);
+        dumpSetting(s, p,
+                Settings.System.MUTE_STREAMS_AFFECTED,
+                SystemSettingsProto.Volume.MUTE_STREAMS_AFFECTED);
+        p.end(volumeToken);
+
+        dumpSetting(s, p,
+                Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
+                SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+
+        // Please insert new settings using the same order as in SecureSettingsProto.
+
+        // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
+        // they're deprecated from Settings.System.
+
+        // Settings.System.STAY_ON_WHILE_PLUGGED_IN intentionally excluded since it's deprecated.
         // Settings.System.AIRPLANE_MODE_ON intentionally excluded since it's deprecated.
         // Settings.System.RADIO_BLUETOOTH intentionally excluded since it's just a constant.
         // Settings.System.RADIO_WIFI intentionally excluded since it's just a constant.
@@ -2184,245 +2572,24 @@
         // Settings.System.WIFI_STATIC_NETMASK intentionally excluded since it's deprecated.
         // Settings.System.WIFI_STATIC_DNS1 intentionally excluded since it's deprecated.
         // Settings.System.WIFI_STATIC_DNS2 intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.BLUETOOTH_DISCOVERABILITY,
-                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY);
-        dumpSetting(s, p,
-                Settings.System.BLUETOOTH_DISCOVERABILITY_TIMEOUT,
-                SystemSettingsProto.BLUETOOTH_DISCOVERABILITY_TIMEOUT);
         // Settings.System.LOCK_PATTERN_ENABLED intentionally excluded since it's deprecated.
         // Settings.System.LOCK_PATTERN_VISIBLE intentionally excluded since it's deprecated.
         // Settings.System.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED intentionally excluded since it's deprecated.
-        // Settings.System.NEXT_ALARM_FORMATTED intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.FONT_SCALE,
-                SystemSettingsProto.FONT_SCALE);
-        dumpSetting(s, p,
-                Settings.System.SYSTEM_LOCALES,
-                SystemSettingsProto.SYSTEM_LOCALES);
         // Settings.System.DEBUG_APP intentionally excluded since it's deprecated.
         // Settings.System.WAIT_FOR_DEBUGGER intentionally excluded since it's deprecated.
         // Settings.System.DIM_SCREEN intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.DISPLAY_COLOR_MODE,
-                SystemSettingsProto.DISPLAY_COLOR_MODE);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_OFF_TIMEOUT,
-                SystemSettingsProto.SCREEN_OFF_TIMEOUT);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS,
-                SystemSettingsProto.SCREEN_BRIGHTNESS);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS_FOR_VR,
-                SystemSettingsProto.SCREEN_BRIGHTNESS_FOR_VR);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_BRIGHTNESS_MODE,
-                SystemSettingsProto.SCREEN_BRIGHTNESS_MODE);
-        dumpSetting(s, p,
-                Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
-                SystemSettingsProto.SCREEN_AUTO_BRIGHTNESS_ADJ);
-        // Settings.System.SHOW_PROCESSES intentionally excluded since it's deprecated.
         // Settings.System.ALWAYS_FINISH_ACTIVITIES intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
-                SystemSettingsProto.MODE_RINGER_STREAMS_AFFECTED);
-        dumpSetting(s, p,
-                Settings.System.MUTE_STREAMS_AFFECTED,
-                SystemSettingsProto.MUTE_STREAMS_AFFECTED);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_ON,
-                SystemSettingsProto.VIBRATE_ON);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_INPUT_DEVICES,
-                SystemSettingsProto.VIBRATE_INPUT_DEVICES);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
-                SystemSettingsProto.NOTIFICATION_VIBRATION_INTENSITY);
-        dumpSetting(s, p,
-                Settings.System.HAPTIC_FEEDBACK_INTENSITY,
-                SystemSettingsProto.HAPTIC_FEEDBACK_INTENSITY);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_RING,
-                SystemSettingsProto.VOLUME_RING);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_SYSTEM,
-                SystemSettingsProto.VOLUME_SYSTEM);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_VOICE,
-                SystemSettingsProto.VOLUME_VOICE);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_MUSIC,
-                SystemSettingsProto.VOLUME_MUSIC);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_ALARM,
-                SystemSettingsProto.VOLUME_ALARM);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_NOTIFICATION,
-                SystemSettingsProto.VOLUME_NOTIFICATION);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_BLUETOOTH_SCO,
-                SystemSettingsProto.VOLUME_BLUETOOTH_SCO);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_ACCESSIBILITY,
-                SystemSettingsProto.VOLUME_ACCESSIBILITY);
-        dumpSetting(s, p,
-                Settings.System.VOLUME_MASTER,
-                SystemSettingsProto.VOLUME_MASTER);
-        dumpSetting(s, p,
-                Settings.System.MASTER_MONO,
-                SystemSettingsProto.MASTER_MONO);
-        // Settings.System.NOTIFICATIONS_USE_RING_VOLUME intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_IN_SILENT,
-                SystemSettingsProto.VIBRATE_IN_SILENT);
-        dumpSetting(s, p,
-                Settings.System.APPEND_FOR_LAST_AUDIBLE,
-                SystemSettingsProto.APPEND_FOR_LAST_AUDIBLE);
-        dumpSetting(s, p,
-                Settings.System.RINGTONE,
-                SystemSettingsProto.RINGTONE);
-        dumpSetting(s, p,
-                Settings.System.RINGTONE_CACHE,
-                SystemSettingsProto.RINGTONE_CACHE);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_SOUND,
-                SystemSettingsProto.NOTIFICATION_SOUND);
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_SOUND_CACHE,
-                SystemSettingsProto.NOTIFICATION_SOUND_CACHE);
-        dumpSetting(s, p,
-                Settings.System.ALARM_ALERT,
-                SystemSettingsProto.ALARM_ALERT);
-        dumpSetting(s, p,
-                Settings.System.ALARM_ALERT_CACHE,
-                SystemSettingsProto.ALARM_ALERT_CACHE);
-        dumpSetting(s, p,
-                Settings.System.MEDIA_BUTTON_RECEIVER,
-                SystemSettingsProto.MEDIA_BUTTON_RECEIVER);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_REPLACE,
-                SystemSettingsProto.TEXT_AUTO_REPLACE);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_CAPS,
-                SystemSettingsProto.TEXT_AUTO_CAPS);
-        dumpSetting(s, p,
-                Settings.System.TEXT_AUTO_PUNCTUATE,
-                SystemSettingsProto.TEXT_AUTO_PUNCTUATE);
-        dumpSetting(s, p,
-                Settings.System.TEXT_SHOW_PASSWORD,
-                SystemSettingsProto.TEXT_SHOW_PASSWORD);
-        dumpSetting(s, p,
-                Settings.System.SHOW_GTALK_SERVICE_STATUS,
-                SystemSettingsProto.SHOW_GTALK_SERVICE_STATUS);
+        // Settings.System.APPEND_FOR_LAST_AUDIBLE intentionally excluded since it hasn't been used since API 2.
         // Settings.System.WALLPAPER_ACTIVITY intentionally excluded since it's deprecated.
-        // Settings.System.AUTO_TIME intentionally excluded since it's deprecated.
-        // Settings.System.AUTO_TIME_ZONE intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.TIME_12_24,
-                SystemSettingsProto.TIME_12_24);
-        dumpSetting(s, p,
-                Settings.System.DATE_FORMAT,
-                SystemSettingsProto.DATE_FORMAT);
-        dumpSetting(s, p,
-                Settings.System.SETUP_WIZARD_HAS_RUN,
-                SystemSettingsProto.SETUP_WIZARD_HAS_RUN);
         // Settings.System.WINDOW_ANIMATION_SCALE intentionally excluded since it's deprecated.
         // Settings.System.TRANSITION_ANIMATION_SCALE intentionally excluded since it's deprecated.
         // Settings.System.ANIMATOR_ANIMATION_SCALE intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.ACCELEROMETER_ROTATION,
-                SystemSettingsProto.ACCELEROMETER_ROTATION);
-        dumpSetting(s, p,
-                Settings.System.USER_ROTATION,
-                SystemSettingsProto.USER_ROTATION);
-        dumpSetting(s, p,
-                Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
-                SystemSettingsProto.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY);
-        dumpSetting(s, p,
-                Settings.System.VIBRATE_WHEN_RINGING,
-                SystemSettingsProto.VIBRATE_WHEN_RINGING);
-        dumpSetting(s, p,
-                Settings.System.DTMF_TONE_WHEN_DIALING,
-                SystemSettingsProto.DTMF_TONE_WHEN_DIALING);
-        dumpSetting(s, p,
-                Settings.System.DTMF_TONE_TYPE_WHEN_DIALING,
-                SystemSettingsProto.DTMF_TONE_TYPE_WHEN_DIALING);
-        dumpSetting(s, p,
-                Settings.System.HEARING_AID,
-                SystemSettingsProto.HEARING_AID);
-        dumpSetting(s, p,
-                Settings.System.TTY_MODE,
-                SystemSettingsProto.TTY_MODE);
-        dumpSetting(s, p,
-                Settings.System.RTT_CALLING_MODE,
-                SystemSettingsProto.RTT_CALLING_MODE);
-        dumpSetting(s, p,
-                Settings.System.SOUND_EFFECTS_ENABLED,
-                SystemSettingsProto.SOUND_EFFECTS_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.HAPTIC_FEEDBACK_ENABLED,
-                SystemSettingsProto.HAPTIC_FEEDBACK_ENABLED);
-        // Settings.System.SHOW_WEB_SUGGESTIONS intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.NOTIFICATION_LIGHT_PULSE,
-                SystemSettingsProto.NOTIFICATION_LIGHT_PULSE);
-        dumpSetting(s, p,
-                Settings.System.POINTER_LOCATION,
-                SystemSettingsProto.POINTER_LOCATION);
-        dumpSetting(s, p,
-                Settings.System.SHOW_TOUCHES,
-                SystemSettingsProto.SHOW_TOUCHES);
-        dumpSetting(s, p,
-                Settings.System.WINDOW_ORIENTATION_LISTENER_LOG,
-                SystemSettingsProto.WINDOW_ORIENTATION_LISTENER_LOG);
-        // Settings.System.POWER_SOUNDS_ENABLED intentionally excluded since it's deprecated.
-        // Settings.System.DOCK_SOUNDS_ENABLED intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.LOCKSCREEN_SOUNDS_ENABLED,
-                SystemSettingsProto.LOCKSCREEN_SOUNDS_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.LOCKSCREEN_DISABLED,
-                SystemSettingsProto.LOCKSCREEN_DISABLED);
-        // Settings.System.LOW_BATTERY_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.DESK_DOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.DESK_UNDOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.CAR_DOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.CAR_UNDOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.LOCK_SOUND intentionally excluded since it's deprecated.
-        // Settings.System.UNLOCK_SOUND intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.SIP_RECEIVE_CALLS,
-                SystemSettingsProto.SIP_RECEIVE_CALLS);
-        dumpSetting(s, p,
-                Settings.System.SIP_CALL_OPTIONS,
-                SystemSettingsProto.SIP_CALL_OPTIONS);
-        dumpSetting(s, p,
-                Settings.System.SIP_ALWAYS,
-                SystemSettingsProto.SIP_ALWAYS);
-        dumpSetting(s, p,
-                Settings.System.SIP_ADDRESS_ONLY,
-                SystemSettingsProto.SIP_ADDRESS_ONLY);
-        // Settings.System.SIP_ASK_ME_EACH_TIME intentionally excluded since it's deprecated.
-        dumpSetting(s, p,
-                Settings.System.POINTER_SPEED,
-                SystemSettingsProto.POINTER_SPEED);
-        dumpSetting(s, p,
-                Settings.System.LOCK_TO_APP_ENABLED,
-                SystemSettingsProto.LOCK_TO_APP_ENABLED);
-        dumpSetting(s, p,
-                Settings.System.EGG_MODE,
-                SystemSettingsProto.EGG_MODE);
-        dumpSetting(s, p,
-                Settings.System.SHOW_BATTERY_PERCENT,
-                SystemSettingsProto.SHOW_BATTERY_PERCENT);
-        dumpSetting(s, p,
-                Settings.System.WHEN_TO_MAKE_WIFI_CALLS,
-                SystemSettingsProto.WHEN_TO_MAKE_WIFI_CALLS);
+
         // The rest of the settings were moved to Settings.Secure, and are thus excluded here since
         // they're deprecated from Settings.System.
 
-        // Please insert new settings using the same order as in Settings.System.
-
+        // Please insert new settings using the same order as in SecureSettingsProto.
         p.end(token);
+        // Please insert new settings using the same order as in SecureSettingsProto.
     }
 }
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 68293d9..df21151 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -56,7 +56,8 @@
     SystemUI-tags \
     SystemUI-proto
 
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common \
+    android.car
 
 LOCAL_PACKAGE_NAME := SystemUI
 LOCAL_PRIVATE_PLATFORM_APIS := true
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3d49e5c..3488168 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -205,6 +205,9 @@
     <!-- Listen app op changes -->
     <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" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
index c8bcdaa..5f4cf03 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java
@@ -19,6 +19,7 @@
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.os.Handler;
+import android.os.VibrationEffect;
 import android.util.SparseArray;
 
 import com.android.systemui.plugins.VolumeDialogController.Callbacks;
@@ -44,7 +45,8 @@
     void setRingerMode(int ringerModeNormal, boolean external);
 
     boolean hasVibrator();
-    void vibrate();
+    void vibrate(VibrationEffect effect);
+    void scheduleTouchFeedback();
 
     AudioManager getAudioManager();
 
diff --git a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
index 726d814..c0e0e59 100644
--- a/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_1x_mobiledata.xml
@@ -14,17 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14dp"
-        android:height="24dp"
-        android:viewportWidth="14"
-        android:viewportHeight="24">
+    android:width="14dp"
+    android:height="17dp"
+    android:viewportWidth="14"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M5.62,16.29H4.58V9.06l-1.79,0.8V8.88l2.67-1.17h0.16V16.29z" />
+        android:pathData="M3.77,13.48H2.55V5.05L0.46,5.98V4.84l3.12-1.36h0.19V13.48z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M11.08,11.02l1.61-3.27h1.26l-2.22,4.23l2.27,4.3h-1.27l-1.64-3.33l-1.65,3.33H8.16l2.28-4.3L8.21,7.75h1.25L11.08,11.02z" />
+        android:pathData="M10.14,7.34l1.87-3.81h1.47L10.9,8.46l2.65,5.02h-1.48l-1.91-3.88l-1.92,3.88H6.74L9.4,8.46l-2.6-4.94h1.46L10.14,7.34z" />
     <path
-        android:pathData="M 0 0 H 13.99 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+        android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
index 7a539ff..e4a5bf8 100644
--- a/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_3g_mobiledata.xml
@@ -14,17 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14dp"
-        android:height="24dp"
-        android:viewportWidth="14"
-        android:viewportHeight="24">
+    android:width="14dp"
+    android:height="17dp"
+    android:viewportWidth="14"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M3.83,11.38h0.66c0.43,0,0.75-0.13,0.98-0.39s0.35-0.62,0.35-1.07c0-1-0.39-1.5-1.16-1.5c-0.37,0-0.66,0.13-0.87,0.4 S3.47,9.44,3.47,9.88H2.44c0-0.68,0.21-1.25,0.62-1.69s0.95-0.67,1.6-0.67c0.67,0,1.21,0.21,1.6,0.63s0.59,1.01,0.59,1.78 c0,0.39-0.1,0.76-0.31,1.1s-0.47,0.59-0.8,0.75c0.8,0.3,1.21,0.96,1.21,2c0,0.76-0.21,1.37-0.64,1.82s-0.98,0.68-1.66,0.68 c-0.68,0-1.22-0.21-1.64-0.64s-0.62-1-0.62-1.73h1.04c0,0.45,0.11,0.81,0.33,1.08s0.52,0.4,0.9,0.4c0.39,0,0.69-0.13,0.92-0.39 s0.34-0.66,0.34-1.2c0-1.04-0.49-1.55-1.47-1.55H3.83V11.38z" />
+        android:pathData="M1.9,7.88h0.77c0.5,0,0.88-0.15,1.15-0.46s0.4-0.72,0.4-1.25c0-1.17-0.45-1.75-1.35-1.75c-0.43,0-0.77,0.16-1.02,0.47 S1.49,5.62,1.49,6.13h-1.2c0-0.8,0.24-1.46,0.73-1.97s1.11-0.78,1.86-0.78c0.78,0,1.41,0.25,1.87,0.73S5.43,5.31,5.43,6.2 c0,0.46-0.12,0.89-0.36,1.29S4.52,8.18,4.14,8.37c0.94,0.35,1.41,1.12,1.41,2.33c0,0.89-0.25,1.6-0.74,2.12 c-0.49,0.53-1.14,0.79-1.94,0.79c-0.79,0-1.43-0.25-1.91-0.75c-0.49-0.5-0.73-1.17-0.73-2.01h1.21c0,0.53,0.13,0.95,0.38,1.26 c0.26,0.31,0.6,0.47,1.05,0.47c0.45,0,0.81-0.15,1.08-0.46s0.4-0.77,0.4-1.39c0-1.21-0.57-1.81-1.72-1.81H1.9V7.88z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M14,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.15-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92H14V15.11z" />
+        android:pathData="M13.77,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 C12.47,5.82,12.3,5.33,12,4.98s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.59,6.37,8.58,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H10.7V8.52h3.07V12.24z" />
     <path
-        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
-</vector>
\ No newline at end of file
+        android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
index b2fab0c8..e98560b 100644
--- a/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_mobiledata.xml
@@ -14,17 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14dp"
-        android:height="24dp"
-        android:viewportWidth="14"
-        android:viewportHeight="24">
+    android:width="14dp"
+    android:height="17dp"
+    android:viewportWidth="14"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M6.42,13.3h0.95v0.88H6.42v1.98H5.38v-1.98H2.16v-0.64l3.17-5.91h1.09C6.42,7.63,6.42,13.3,6.42,13.3z M3.31,13.3h2.07 V9.25L3.31,13.3z" />
+        android:pathData="M5.07,10.13h1.11v1.03H5.07v2.31H3.86v-2.31H0.1v-0.75l3.7-6.9h1.27V10.13z M1.44,10.13h2.42V5.4L1.44,10.13z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M13.99,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.62V15.11z" />
+        android:pathData="M13.9,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26h-1.25 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.72,6.37,8.71,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
     <path
-        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 14 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
index bdbb2df..bf39ea2 100644
--- a/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_4g_plus_mobiledata.xml
@@ -14,20 +14,20 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="20dp"
-        android:height="24dp"
-        android:viewportWidth="20"
-        android:viewportHeight="24">
+    android:width="22dp"
+    android:height="17dp"
+    android:viewportWidth="22"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M6.18,13.3h0.95v0.88H6.18v1.98H5.14v-1.98H1.92v-0.64l3.17-5.91h1.09V13.3z M3.07,13.3h2.07V9.25L3.07,13.3z" />
+        android:pathData="M5.32,10.13h1.11v1.03H5.32v2.31H4.11v-2.31H0.35v-0.75l3.7-6.9h1.27V10.13z M1.69,10.13h2.42V5.4L1.69,10.13z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M13.75,15.11l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.8-2.59V11.1 c0-1.2,0.24-2.09,0.72-2.69s1.2-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68s0.71,1.1,0.76,1.94h-1.07 c-0.04-0.53-0.19-0.95-0.44-1.25s-0.63-0.45-1.14-0.45c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74 c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96h-1.56v-0.92h2.63V15.11z" />
+        android:pathData="M14.15,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13s1.39-1.04,2.51-1.04c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H12.9 c-0.05-0.62-0.22-1.1-0.52-1.45s-0.74-0.52-1.34-0.52c-0.72,0-1.24,0.23-1.57,0.7S8.97,6.37,8.96,7.4v2.03 c0,1,0.19,1.77,0.57,2.31c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59h-1.82V8.52h3.07V12.24z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M 20 9.64 L 18 9.64 L 18 7.64 L 17 7.64 L 17 9.64 L 15 9.64 L 15 10.64 L 17 10.64 L 17 12.64 L 18 12.64 L 18 10.64 L 20 10.64 Z" />
+        android:pathData="M 19.3 5.74 L 19.3 3.39 L 18 3.39 L 18 5.74 L 15.65 5.74 L 15.65 7.04 L 18 7.04 L 18 9.39 L 19.3 9.39 L 19.3 7.04 L 21.65 7.04 L 21.65 5.74 Z" />
     <path
-        android:pathData="M 0 0 H 20 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 22 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
index 1a4a2e3..ca601d6 100644
--- a/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_e_mobiledata.xml
@@ -14,14 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="7dp"
-        android:height="24dp"
-        android:viewportWidth="7"
-        android:viewportHeight="24">
+    android:width="6dp"
+    android:height="17dp"
+    android:viewportWidth="6"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M6.5,12.32H3.48v3.02H7v0.92H2.41V7.73h4.53v0.92H3.48v2.75H6.5V12.32z" />
+        android:pathData="M5.1,8.88H1.57v3.53h4.1v1.07H0.32V3.52h5.28V4.6H1.57V7.8H5.1V8.88z" />
     <path
-        android:pathData="M 0 0 H 7 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 6 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
index d6a0488..8ff6d7a 100644
--- a/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_g_mobiledata.xml
@@ -14,14 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8dp"
-        android:height="24dp"
-        android:viewportWidth="8"
-        android:viewportHeight="24">
+    android:width="7dp"
+    android:height="17dp"
+    android:viewportWidth="7"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M8,15.21l-0.19,0.23c-0.54,0.63-1.33,0.94-2.37,0.94c-0.92,0-1.65-0.31-2.17-0.92s-0.79-1.48-0.81-2.59V11.2 c0-1.2,0.24-2.09,0.72-2.69s1.19-0.89,2.15-0.89c0.81,0,1.45,0.23,1.91,0.68S7.95,9.39,8,10.23H6.93C6.88,9.7,6.74,9.28,6.49,8.99 S5.85,8.54,5.34,8.54c-0.61,0-1.06,0.2-1.35,0.6s-0.43,1.04-0.45,1.92v1.74c0,0.86,0.16,1.52,0.49,1.98s0.8,0.69,1.41,0.69 c0.58,0,1.02-0.14,1.32-0.42l0.16-0.15v-1.96H5.37v-0.92H8V15.21z" />
+        android:pathData="M6.73,12.24l-0.22,0.27c-0.63,0.73-1.55,1.1-2.76,1.1c-1.08,0-1.92-0.36-2.53-1.07c-0.61-0.71-0.93-1.72-0.94-3.02V7.56 c0-1.39,0.28-2.44,0.84-3.13S2.5,3.39,3.62,3.39c0.95,0,1.69,0.26,2.23,0.79s0.83,1.28,0.89,2.26H5.48 c-0.05-0.62-0.22-1.1-0.52-1.45S4.22,4.46,3.62,4.46c-0.72,0-1.24,0.23-1.57,0.7S1.54,6.37,1.53,7.4v2.03c0,1,0.19,1.77,0.57,2.31 c0.38,0.54,0.93,0.8,1.65,0.8c0.67,0,1.19-0.16,1.54-0.49l0.18-0.17V9.59H3.66V8.52h3.07V12.24z" />
     <path
-        android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
index be85bbb..68ea58e 100644
--- a/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_mobiledata.xml
@@ -14,14 +14,14 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="8dp"
-        android:height="24dp"
-        android:viewportWidth="8"
-        android:viewportHeight="24">
+    android:width="7dp"
+    android:height="17dp"
+    android:viewportWidth="7"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M8,16.27H6.92v-3.94H3.49v3.94H2.42V7.73h1.07v3.67h3.43V7.73H8V16.27z" />
+        android:pathData="M6.76,13.48H5.5v-4.6H1.49v4.6H0.24V3.52h1.25V7.8H5.5V3.52h1.26V13.48z" />
     <path
-        android:pathData="M 0 0 H 8 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 7 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
index f31f83c..42128002 100644
--- a/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_h_plus_mobiledata.xml
@@ -14,17 +14,17 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="14dp"
-        android:height="24dp"
-        android:viewportWidth="14"
-        android:viewportHeight="24">
+    android:width="15dp"
+    android:height="17dp"
+    android:viewportWidth="15"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M7.64,16.27H6.56v-3.94H3.13v3.94H2.06V7.73h1.07v3.67h3.43V7.73h1.08V16.27z" />
+        android:pathData="M7.01,13.48H5.75v-4.6H1.74v4.6H0.49V3.52h1.25V7.8h4.01V3.52h1.26V13.48z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M 14 9.73 L 12 9.73 L 12 7.73 L 11 7.73 L 11 9.73 L 9 9.73 L 9 10.73 L 11 10.73 L 11 12.73 L 12 12.73 L 12 10.73 L 14 10.73 Z" />
+        android:pathData="M 12.16 5.74 L 12.16 3.39 L 10.86 3.39 L 10.86 5.74 L 8.51 5.74 L 8.51 7.04 L 10.86 7.04 L 10.86 9.39 L 12.16 9.39 L 12.16 7.04 L 14.51 7.04 L 14.51 5.74 Z" />
     <path
-        android:pathData="M 0 0 H 14 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 15 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
index e45b5e0..7536f51 100644
--- a/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_mobiledata.xml
@@ -14,20 +14,20 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="18dp"
-        android:height="24dp"
-        android:viewportWidth="18"
-        android:viewportHeight="24">
+    android:width="18dp"
+    android:height="17dp"
+    android:viewportWidth="18"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M3.79,15.35h3.35v0.92H2.71V7.73h1.08V15.35z" />
+        android:pathData="M1.34,12.4h3.9v1.07H0.08V3.52h1.26V12.4z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12.15,8.65H9.91v7.61H8.84V8.65H6.6V7.73h5.55V8.65z" />
+        android:pathData="M11.1,4.6H8.48v8.88H7.23V4.6H4.62V3.52h6.48V4.6z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M17.5,12.32h-3.02v3.02H18v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+        android:pathData="M17.34,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
     <path
-        android:pathData="M 0 0 H 18 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 18 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
index 553a5bd..302e3bd 100644
--- a/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
+++ b/packages/SystemUI/res/drawable/ic_lte_plus_mobiledata.xml
@@ -14,23 +14,23 @@
      limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:height="24dp"
-        android:viewportWidth="24"
-        android:viewportHeight="24">
+    android:width="26dp"
+    android:height="17dp"
+    android:viewportWidth="26"
+    android:viewportHeight="17">
 
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M3.91,15.35h3.35v0.92H2.84V7.73h1.08V15.35z" />
+        android:pathData="M1.59,12.4h3.9v1.07H0.33V3.52h1.26V12.4z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M12.28,8.65h-2.24v7.61H8.96V8.65H6.73V7.73h5.55V8.65z" />
+        android:pathData="M11.35,4.6H8.73v8.88H7.48V4.6H4.87V3.52h6.48V4.6z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M17.63,12.32h-3.02v3.02h3.52v0.92h-4.59V7.73h4.53v0.92h-3.46v2.75h3.02V12.32z" />
+        android:pathData="M17.59,8.88h-3.52v3.53h4.1v1.07h-5.35V3.52h5.28V4.6h-4.03V7.8h3.52V8.88z" />
     <path
         android:fillColor="#FFFFFFFF"
-        android:pathData="M 24 9.76 L 22 9.76 L 22 7.76 L 21 7.76 L 21 9.76 L 19 9.76 L 19 10.76 L 21 10.76 L 21 12.76 L 22 12.76 L 22 10.76 L 24 10.76 Z" />
+        android:pathData="M 23.32 5.74 L 23.32 3.39 L 22.02 3.39 L 22.02 5.74 L 19.67 5.74 L 19.67 7.04 L 22.02 7.04 L 22.02 9.39 L 23.32 9.39 L 23.32 7.04 L 25.67 7.04 L 25.67 5.74 Z" />
     <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
+        android:pathData="M 0 0 H 26 V 17 H 0 V 0 Z" />
 </vector>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background.xml b/packages/SystemUI/res/drawable/qs_customizer_background.xml
index 12d8016..e15a734 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background.xml
@@ -14,6 +14,6 @@
     limitations under the License.
 -->
 <transition xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/qs_detail_transition" />
-    <item android:drawable="?android:attr/colorPrimary" />
+    <item android:drawable="@drawable/qs_customizer_background_transition" />
+    <item android:drawable="@drawable/qs_customizer_background_primary" />
 </transition>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
new file mode 100644
index 0000000..abe1429
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorPrimary"/>
+        <corners android:radius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
new file mode 100644
index 0000000..ed8f61a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_transition.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="@color/qs_detail_transition"/>
+        <corners android:radius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
new file mode 100644
index 0000000..557cae1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+    <shape>
+        <solid android:color="?android:attr/colorSecondary"/>
+        <corners
+            android:topLeftRadius="?android:attr/dialogCornerRadius"
+            android:topRightRadius="?android:attr/dialogCornerRadius" />
+    </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
index c3e36f2..a4b3c99 100644
--- a/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
+++ b/packages/SystemUI/res/drawable/rounded_full_bg_bottom.xml
@@ -1,4 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
     <solid android:color="?android:attr/colorPrimaryDark" />
diff --git a/packages/SystemUI/res/drawable/rounded_ripple.xml b/packages/SystemUI/res/drawable/rounded_ripple.xml
new file mode 100644
index 0000000..5588eb2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/rounded_ripple.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+        android:color="?android:attr/colorControlHighlight">
+    <item android:id="@android:id/mask">
+        <shape xmlns:android="http://schemas.android.com/apk/res/android">
+            <solid android:color="#FFFFFFFF"/>
+            <corners android:radius="8dp"/>
+        </shape>
+    </item>
+    <item android:id="@android:id/background">
+        <shape android:shape="rectangle">
+            <solid android:color="#FFFFFFFF"/>
+            <corners android:radius="8dp"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
index bd2edf3..0dd9f5a 100644
--- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -1,28 +1,27 @@
 <!--
-Copyright (C) 2014 The Android Open Source Project
+     Copyright (C) 2018 The Android Open Source Project
 
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
 
-         http://www.apache.org/licenses/LICENSE-2.0
+          http://www.apache.org/licenses/LICENSE-2.0
 
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    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.
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
         android:width="@dimen/signal_icon_size"
         android:height="@dimen/signal_icon_size"
-        android:viewportWidth="24"
-        android:viewportHeight="24">
-    <path
-        android:fillColor="#FFFFFFFF"
-        android:pathData="M7.8,7.2L9,10H7L5.87,7.33H4V10H2V2h5c1.13,0,2,0.87,2,2v1.33C9,6.13,8.47,6.87,7.8,7.2z M7,4H4v1.33h3V4z" />
-    <path
-        android:pathData="M 0 0 H 24 V 24 H 0 V 0 Z" />
-    <path
-        android:pathData="M0,0h24v24H0V0z" />
-</vector>
\ No newline at end of file
+        android:viewportWidth="17"
+        android:viewportHeight="17">
+
+<path
+    android:fillColor="#FFFFFFFF"
+    android:pathData="M2.93,4.81H1.81V7.4H1V1h1.79c0.63,0,1.1,0.16,1.42,0.49S4.7,2.29,4.7,2.92c0,0.4-0.09,0.74-0.26,1.04 C4.26,4.25,4.02,4.48,3.7,4.63l1.24,2.72V7.4H4.07L2.93,4.81z M1.81,4.12h0.98c0.34,0,0.61-0.11,0.81-0.33 c0.2-0.22,0.3-0.51,0.3-0.87c0-0.82-0.37-1.23-1.12-1.23H1.81V4.12z" />
+<path
+    android:pathData="M 0 0 H 17 V 17 H 0 V 0 Z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/car_facet_button.xml b/packages/SystemUI/res/layout/car_facet_button.xml
index f432d36..ad86049 100644
--- a/packages/SystemUI/res/layout/car_facet_button.xml
+++ b/packages/SystemUI/res/layout/car_facet_button.xml
@@ -42,6 +42,7 @@
             android:layout_height="wrap_content"
             android:layout_width="match_parent"
             android:animateLayoutChanges="true"
+            android:src="@drawable/car_ic_arrow"
             android:background="@android:color/transparent"
             android:scaleType="fitCenter">
         </com.android.keyguard.AlphaOptimizedImageButton>
diff --git a/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..a65ff16
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_left_navigation_bar_unprovisioned.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+    </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..b0488ae
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_navigation_bar_unprovisioned.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:orientation="horizontal"
+        android:id="@+id/nav_buttons"
+        android:gravity="left"
+        android:paddingLeft="30dp"
+        android:layout_weight="1"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingLeft="30dp"
+            android:paddingRight="30dp"
+        />
+    </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..a65ff16
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_right_navigation_bar_unprovisioned.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:orientation="vertical"
+    android:background="@drawable/system_bar_background">
+
+    <LinearLayout
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:id="@+id/nav_buttons"
+        android:orientation="vertical"
+        android:gravity="top"
+        android:paddingTop="30dp"
+        android:layout_weight="1"
+        android:background="@drawable/system_bar_background"
+        android:animateLayoutChanges="true">
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/home"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;end"
+            android:src="@drawable/car_ic_overview"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+
+        <com.android.systemui.statusbar.car.CarNavigationButton
+            android:id="@+id/hvac"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+            systemui:broadcast="true"
+            android:src="@drawable/car_ic_hvac"
+            android:background="?android:attr/selectableItemBackground"
+            android:paddingTop="30dp"
+            android:paddingBottom="30dp"
+        />
+    </LinearLayout>
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/SystemUI/res/layout/car_status_bar_header.xml b/packages/SystemUI/res/layout/car_status_bar_header.xml
index 158907e..f2ef301 100644
--- a/packages/SystemUI/res/layout/car_status_bar_header.xml
+++ b/packages/SystemUI/res/layout/car_status_bar_header.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- Extends RelativeLayout -->
+<!-- Extends LinearLayout -->
 <com.android.systemui.qs.car.CarStatusBarHeader
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:systemui="http://schemas.android.com/apk/res-auto"
@@ -23,22 +23,21 @@
     android:paddingStart="8dp"
     android:paddingEnd="8dp" >
 
-    <include
-        layout="@layout/system_icons"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_alignParentStart="true"
-        android:layout_centerVertical="true" />
+    <include layout="@layout/system_icons"
+             android:layout_width="wrap_content"
+             android:layout_height="match_parent"
+             android:gravity="center_vertical|end"
+             android:layout_weight="1"
+    />
 
     <com.android.systemui.statusbar.policy.Clock
         android:id="@+id/clock"
         android:textAppearance="@style/TextAppearance.StatusBar.Clock"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
-        android:layout_alignParentEnd="true"
-        android:layout_centerVertical="true"
         android:singleLine="true"
         android:paddingStart="@dimen/status_bar_clock_starting_padding"
         android:paddingEnd="@dimen/status_bar_clock_end_padding"
-        systemui:showDark="false" />
+        android:gravity="center_vertical|end"
+    />
 </com.android.systemui.qs.car.CarStatusBarHeader>
diff --git a/packages/SystemUI/res/layout/car_top_navigation_bar.xml b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
new file mode 100644
index 0000000..e16014b
--- /dev/null
+++ b/packages/SystemUI/res/layout/car_top_navigation_bar.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+** 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.
+*/
+-->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent"
+    android:background="@drawable/system_bar_background">
+
+    <com.android.systemui.statusbar.policy.Clock
+        android:id="@+id/clock"
+        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:singleLine="true"
+        android:paddingStart="@dimen/status_bar_clock_starting_padding"
+        android:paddingEnd="@dimen/status_bar_clock_end_padding"
+        android:gravity="center_vertical"
+    />
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
+
diff --git a/packages/SystemUI/res/layout/car_volume_dialog.xml b/packages/SystemUI/res/layout/car_volume_dialog.xml
index dca50a5..94cc001 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog.xml
@@ -24,7 +24,7 @@
     android:clipChildren="false" >
     <LinearLayout
         android:id="@+id/volume_dialog"
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|top"
         android:orientation="vertical"
@@ -38,7 +38,7 @@
             android:orientation="vertical"
             android:clipChildren="false"
             android:clipToPadding="false"
-            android:elevation="@dimen/volume_panel_elevation" >
+            android:elevation="@dimen/volume_dialog_elevation" >
             <LinearLayout
                 android:id="@+id/car_volume_dialog_rows"
                 android:layout_width="match_parent"
@@ -53,14 +53,15 @@
         android:layout_width="wrap_content"
         android:layout_height="@dimen/car_single_line_list_item_height"
         android:gravity="center"
-        android:layout_marginEnd="@dimen/car_keyline_1">
-        <ImageButton
+        android:layout_marginEnd="@dimen/car_keyline_1"
+        android:layout_gravity="end">
+        <com.android.keyguard.AlphaOptimizedImageButton
             android:id="@+id/expand"
             android:layout_gravity="center"
             android:layout_width="@dimen/car_primary_icon_size"
             android:layout_height="@dimen/car_primary_icon_size"
-            android:layout_marginEnd="@dimen/car_keyline_1"
             android:src="@drawable/car_ic_arrow_drop_up"
+            android:background="?android:attr/selectableItemBackground"
             android:tint="@color/car_tint"
             android:scaleType="fitCenter"
         />
diff --git a/packages/SystemUI/res/layout/car_volume_dialog_row.xml b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
index 14baf49..33cecfa 100644
--- a/packages/SystemUI/res/layout/car_volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/car_volume_dialog_row.xml
@@ -33,13 +33,14 @@
             android:layout_width="@dimen/car_primary_icon_size"
             android:layout_height="@dimen/car_primary_icon_size"
             android:layout_marginStart="@dimen/car_keyline_1"
+            android:background="?android:attr/selectableItemBackground"
             android:tint="@color/car_tint"
             android:scaleType="fitCenter"
             android:soundEffectsEnabled="false" />
         <SeekBar
                 android:id="@+id/volume_row_slider"
                 android:clickable="true"
-                android:layout_marginStart="@dimen/car_keyline_3"
+                android:layout_marginStart="@dimen/car_keyline_1_keyline_3_diff"
                 android:layout_marginEnd="@dimen/car_keyline_3"
                 android:layout_width="match_parent"
                 android:layout_height="@dimen/car_single_line_list_item_height"/>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index cc6e3bf..5ecd380 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -50,7 +50,8 @@
         android:layout_height="wrap_content"
         android:layout_width="wrap_content"
         android:layout_gravity="center_vertical"
-        android:paddingEnd="1dp"
+        android:paddingStart="1dp"
+        android:paddingEnd="2dp"
         android:visibility="gone" />
     <Space
         android:id="@+id/mobile_roaming_space"
diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml
index 71ad85b..51febc7 100644
--- a/packages/SystemUI/res/layout/qs_customize_divider.xml
+++ b/packages/SystemUI/res/layout/qs_customize_divider.xml
@@ -20,9 +20,8 @@
     android:id="@android:id/title"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:gravity="center"
     android:paddingTop="20dp"
-    android:paddingStart="16dp"
-    android:paddingEnd="8dp"
     android:paddingBottom="13dp"
     android:textAppearance="@android:style/TextAppearance.Material.Body2"
     android:textColor="?android:attr/colorAccent"
diff --git a/packages/SystemUI/res/layout/qs_customize_panel.xml b/packages/SystemUI/res/layout/qs_customize_panel.xml
index b3b6a0c..506e6c8 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel.xml
@@ -22,7 +22,6 @@
     android:layout_height="0dp"
     android:elevation="4dp"
     android:orientation="vertical"
-    android:background="@drawable/qs_customizer_background"
     android:gravity="center_horizontal">
 
 </com.android.systemui.qs.customize.QSCustomizer>
diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
index 04d0e65..d70a37a 100644
--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
@@ -15,24 +15,44 @@
      limitations under the License.
 -->
 
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <Toolbar
-        android:id="@*android:id/action_bar"
+<merge xmlns:android="http://schemas.android.com/apk/res/android">->
+    <View
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="28dp"
-        android:navigationContentDescription="@*android:string/action_bar_up_description"
-        style="?android:attr/toolbarStyle" />
+        android:layout_height="@*android:dimen/quick_qs_offset_height"
+        android:background="@android:color/transparent" />
 
-    <android.support.v7.widget.RecyclerView
-        android:id="@android:id/list"
+    <com.android.keyguard.AlphaOptimizedLinearLayout
+        android:id="@+id/customize_container"
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
-        android:scrollIndicators="top"
-        android:scrollbars="vertical"
-        android:importantForAccessibility="no" />
+        android:layout_marginLeft="@dimen/notification_side_paddings"
+        android:layout_marginRight="@dimen/notification_side_paddings"
+        android:orientation="vertical"
+        android:background="@drawable/qs_customizer_background">
+        <Toolbar
+            android:id="@*android:id/action_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@drawable/qs_customizer_toolbar"
+            android:navigationContentDescription="@*android:string/action_bar_up_description"
+            style="?android:attr/toolbarStyle" />
+
+        <android.support.v7.widget.RecyclerView
+            android:id="@android:id/list"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:layout_weight="1"
+            android:paddingTop="28dp"
+            android:paddingLeft="@dimen/qs_tile_layout_margin_side"
+            android:paddingRight="@dimen/qs_tile_layout_margin_side"
+            android:paddingBottom="28dp"
+            android:clipToPadding="false"
+            android:scrollIndicators="top"
+            android:scrollbars="vertical"
+            android:scrollbarStyle="outsideOverlay"
+            android:importantForAccessibility="no" />
+    </com.android.keyguard.AlphaOptimizedLinearLayout>
 
     <View
         android:id="@+id/nav_bar_background"
diff --git a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
index ff55f99..a2250b1 100644
--- a/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
+++ b/packages/SystemUI/res/layout/qs_customize_tile_frame.xml
@@ -17,9 +17,8 @@
 
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
+    android:layout_height="@dimen/qs_tile_height"
     android:layout_width="match_parent"
-    android:paddingStart="8dp"
-    android:paddingEnd="8dp"
-    android:paddingTop="8dp"
+    android:layout_marginTop="@dimen/qs_tile_margin_top_bottom"
+    android:layout_marginBottom="@dimen/qs_tile_margin_top_bottom"
     android:gravity="center" />
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index cd3271f..c03f25c 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -21,7 +21,7 @@
     android:layout_marginTop="8dp"
     android:layout_marginBottom="14dp"
     android:layout_below="@id/quick_status_bar_system_icons"
-    >
+    android:paddingEnd="@dimen/signal_cluster_battery_padding" >
 
     <com.android.systemui.statusbar.phone.StatusIconContainer
         android:id="@+id/statusIcons"
@@ -29,9 +29,4 @@
         android:layout_height="match_parent"
         android:layout_weight="1" />
 
-    <include layout="@layout/signal_cluster_view"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_marginStart="@dimen/signal_cluster_margin_start" />
-
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 7ec437c..54baa4a 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -33,7 +33,7 @@
         android:visibility="invisible" />
 
     <LinearLayout
-        android:id="@+id/next_alarm"
+        android:id="@+id/status_container"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center_horizontal|bottom"
@@ -41,6 +41,7 @@
         android:visibility="invisible">
 
         <ImageView
+            android:id="@+id/next_alarm_icon"
             android:layout_width="@dimen/qs_header_alarm_icon_size"
             android:layout_height="@dimen/qs_header_alarm_icon_size"
             android:src="@drawable/stat_sys_alarm"
@@ -53,6 +54,28 @@
             android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
             android:textAppearance="@style/TextAppearance.QS.TileLabel" />
 
+        <View
+            android:id="@+id/status_separator"
+            android:layout_width="1dp"
+            android:layout_height="match_parent"
+            android:layout_marginStart="10dp"
+            android:layout_marginEnd="10dp"
+            android:background="@android:color/white"
+            android:backgroundTint="?android:attr/textColorPrimary" />
+
+        <ImageView
+            android:id="@+id/ringer_mode_icon"
+            android:layout_width="@dimen/qs_header_alarm_icon_size"
+            android:layout_height="@dimen/qs_header_alarm_icon_size"
+            android:tint="?android:attr/textColorPrimary" />
+
+        <TextView
+            android:id="@+id/ringer_mode_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
+            android:textAppearance="@style/TextAppearance.QS.TileLabel" />
+
     </LinearLayout>
 
 </FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index ca8fcba..ed18dc7 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -22,6 +22,7 @@
     android:layout_width="match_parent"
     android:layout_height="@*android:dimen/quick_qs_total_height"
     android:layout_gravity="@integer/notification_panel_layout_gravity"
+    android:background="@android:color/transparent"
     android:baselineAligned="false"
     android:clickable="false"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
new file mode 100644
index 0000000..d607c8c
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<com.android.systemui.statusbar.StatusBarMobileView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/mobile_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_vertical"
+    android:orientation="horizontal">
+    <FrameLayout
+        android:id="@+id/inout_container"
+        android:layout_height="17dp"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical">
+        <ImageView
+            android:id="@+id/mobile_in"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_down"
+            android:visibility="gone"
+            android:paddingEnd="2dp"
+        />
+        <ImageView
+            android:id="@+id/mobile_out"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_up"
+            android:paddingEnd="2dp"
+            android:visibility="gone"
+        />
+    </FrameLayout>
+    <ImageView
+        android:id="@+id/mobile_type"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_gravity="center_vertical"
+        android:paddingEnd="1dp"
+        android:visibility="gone" />
+    <Space
+        android:id="@+id/mobile_roaming_space"
+        android:layout_height="match_parent"
+        android:layout_width="@dimen/roaming_icon_start_padding"
+        android:visibility="gone"
+    />
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical">
+        <com.android.systemui.statusbar.AnimatedImageView
+            android:id="@+id/mobile_signal"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            systemui:hasOverlappingRendering="false"
+        />
+        <ImageView
+            android:id="@+id/mobile_roaming"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/stat_sys_roaming"
+            android:contentDescription="@string/data_connection_roaming"
+            android:visibility="gone" />
+    </FrameLayout>
+</com.android.systemui.statusbar.StatusBarMobileView>
+
diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
index aa0d4a0..22f1618 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml
@@ -44,7 +44,6 @@
             android:focusable="true"
             android:contentDescription="@string/accessibility_clear_all"
             android:text="@string/clear_all_notifications_text"
-            android:textColor="?attr/wallpaperTextColor"
-            android:textAllCaps="true"/>
+            android:textColor="?attr/wallpaperTextColor"/>
     </FrameLayout>
 </com.android.systemui.statusbar.FooterView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 2e7ab7f..f15ca9e 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -55,19 +55,6 @@
         android:paddingStart="8dp"
         />
 
-    <!-- TODO: remove -->
-    <ImageButton
-        android:id="@+id/helper"
-        android:layout_width="48dp"
-        android:layout_height="@*android:dimen/notification_header_height"
-        android:layout_gravity="top|end"
-        android:layout_marginEnd="6dp"
-        android:src="@drawable/ic_dnd"
-        android:tint="#FF0000"
-        android:background="@drawable/ripple_drawable"
-        android:visibility="visible"
-    />
-
     <ViewStub
         android:layout="@layout/notification_children_container"
         android:id="@+id/child_container_stub"
diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
new file mode 100644
index 0000000..08cef55
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_wifi_group.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** 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.
+*/
+-->
+<com.android.systemui.statusbar.StatusBarWifiView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/wifi_combo"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:paddingStart="4dp"
+    android:gravity="center_vertical"
+    android:orientation="horizontal" >
+
+    <FrameLayout
+            android:id="@+id/inout_container"
+            android:layout_height="17dp"
+            android:layout_width="wrap_content"
+            android:gravity="center_vertical" >
+        <ImageView
+            android:id="@+id/wifi_in"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_down"
+            android:visibility="gone"
+            android:paddingEnd="2dp"
+        />
+        <ImageView
+            android:id="@+id/wifi_out"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:src="@drawable/ic_activity_up"
+            android:paddingEnd="2dp"
+            android:visibility="gone"
+        />
+    </FrameLayout>
+    <FrameLayout
+        android:id="@+id/wifi_combo"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="center_vertical" >
+        <com.android.systemui.statusbar.AlphaOptimizedImageView
+            android:theme="?attr/lightIconTheme"
+            android:id="@+id/wifi_signal"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content" />
+    </FrameLayout>
+
+    <View
+        android:id="@+id/wifi_signal_spacer"
+        android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+        android:layout_height="4dp"
+        android:visibility="gone" />
+
+    <!-- Looks like CarStatusBar uses this... -->
+    <ViewStub
+        android:id="@+id/connected_device_signals_stub"
+        android:layout="@layout/connected_device_signal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <View
+        android:id="@+id/wifi_airplane_spacer"
+        android:layout_width="@dimen/status_bar_airplane_spacer_width"
+        android:layout_height="4dp"
+        android:visibility="gone"
+    />
+</com.android.systemui.statusbar.StatusBarWifiView>
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 1fafb2f..258b82a 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -24,14 +24,10 @@
         android:layout_width="0dp"
         android:layout_weight="1"
         android:layout_height="match_parent"
+        android:paddingEnd="4dp"
         android:gravity="center_vertical"
         android:orientation="horizontal"/>
 
-    <include layout="@layout/signal_cluster_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/signal_cluster_margin_start"/>
-
     <com.android.systemui.BatteryMeterView android:id="@+id/battery"
         android:layout_height="match_parent"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index 0a3f4eb..d4fe1c6 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -17,9 +17,11 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:padding="@dimen/volume_dialog_panel_transparent_padding"
     android:background="@android:color/transparent"
     android:theme="@style/qs_theme"
-    android:clipChildren="false" >
+    android:clipChildren="false"
+    android:clipToPadding="false">
     <!-- right-aligned to be physically near volume button -->
     <LinearLayout
         android:id="@+id/volume_dialog"
@@ -30,22 +32,25 @@
         android:background="@android:color/transparent"
         android:layout_margin="@dimen/volume_dialog_base_margin"
         android:orientation="vertical"
-        android:clipChildren="false" >
+        android:clipChildren="false"
+        android:clipToPadding="false" >
 
         <FrameLayout
             android:id="@+id/ringer"
             android:layout_width="@dimen/volume_dialog_ringer_size"
             android:layout_height="@dimen/volume_dialog_ringer_size"
             android:layout_marginBottom="@dimen/volume_dialog_spacer"
-            android:elevation="@dimen/volume_panel_elevation"
+            android:translationZ="@dimen/volume_dialog_elevation"
             android:layout_gravity="right"
+            android:clipChildren="false"
+            android:clipToPadding="false"
             android:background="@drawable/rounded_bg_full">
             <com.android.keyguard.AlphaOptimizedImageButton
                 android:id="@+id/ringer_icon"
                 style="@style/VolumeButtons"
-                android:background="?android:selectableItemBackgroundBorderless"
-                android:layout_width="@dimen/volume_dialog_tap_target_size"
-                android:layout_height="@dimen/volume_dialog_tap_target_size"
+                android:background="@drawable/rounded_ripple"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
                 android:tint="@color/accent_tint_color_selector"
                 android:layout_gravity="center"
                 android:soundEffectsEnabled="false" />
@@ -59,10 +64,10 @@
             android:minWidth="@dimen/volume_dialog_panel_width"
             android:layout_height="wrap_content"
             android:orientation="vertical"
+            android:translationZ="@dimen/volume_dialog_elevation"
             android:clipChildren="false"
             android:clipToPadding="false"
-            android:background="@drawable/rounded_bg_full"
-            android:elevation="@dimen/volume_panel_elevation" >
+            android:background="@drawable/rounded_bg_full" >
             <LinearLayout
                 android:id="@+id/volume_dialog_rows"
                 android:layout_width="wrap_content"
@@ -84,7 +89,7 @@
                     android:layout_height="@dimen/volume_dialog_tap_target_size"
                     android:layout_gravity="center"
                     android:contentDescription="@string/accessibility_volume_settings"
-                    android:background="?android:selectableItemBackgroundBorderless"
+                    android:background="@drawable/ripple_drawable_20dp"
                     android:tint="?android:attr/colorControlNormal"
                     android:soundEffectsEnabled="false" />
             </FrameLayout>
diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml
index bcc3692..6128da8 100644
--- a/packages/SystemUI/res/layout/volume_dialog_row.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_row.xml
@@ -60,7 +60,7 @@
             style="@style/VolumeButtons"
             android:layout_width="@dimen/volume_dialog_tap_target_size"
             android:layout_height="@dimen/volume_dialog_tap_target_size"
-            android:background="?android:selectableItemBackgroundBorderless"
+            android:background="@drawable/ripple_drawable_20dp"
             android:layout_marginBottom="@dimen/volume_dialog_row_margin_bottom"
             android:tint="@color/accent_tint_color_selector"
             android:soundEffectsEnabled="false" />
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 0076927..ed105ee 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -33,17 +33,18 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"চলিত"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"জাননীসমূহ"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"বেটাৰি কম আছে"</string>
-    <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
-    <skip />
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> বাকী আছে"</string>
     <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
     <skip />
     <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
     <skip />
     <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> বাকী আছে। বেটাৰি সঞ্চয়কাৰী অন হৈ আছে।"</string>
-    <string name="invalid_charger" msgid="4549105996740522523">"ইউএছবি চ্চার্জিং কৰিব পৰা নাযাব।\nআপোনাৰ ফ\'নৰ লগত দিয়া চ্চার্জাৰ ব্যৱহাৰ কৰক।"</string>
-    <string name="invalid_charger_title" msgid="3515740382572798460">"ইউএছবি চ্চার্জিং সমৰ্থিত নহয়।"</string>
-    <string name="invalid_charger_text" msgid="5474997287953892710">"কেৱল যোগান ধৰা চ্চার্জাৰ ব্যৱহাৰ কৰক।"</string>
+    <!-- no translation found for invalid_charger (2741987096648693172) -->
+    <skip />
+    <!-- no translation found for invalid_charger_title (2836102177577255404) -->
+    <skip />
+    <!-- no translation found for invalid_charger_text (6480624964117840005) -->
+    <skip />
     <string name="battery_low_why" msgid="4553600287639198111">"ছেটিংসমূহ"</string>
     <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"বেটাৰি সঞ্চয়কাৰী অন কৰেনে?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"অন কৰক"</string>
@@ -69,8 +70,7 @@
     <string name="usb_debugging_title" msgid="4513918393387141949">"ইউএছবি ডিবাগিংৰ অনুমতি দিবনে?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"এয়া হৈছে কম্পিউটাৰটোৰ RSA কী ফিংগাৰপ্ৰিণ্ট:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"এই কম্পিউটাৰটোৰ পৰা সদায় অনুমতি দিয়ক"</string>
-    <!-- no translation found for usb_debugging_secondary_user_title (6353808721761220421) -->
-    <skip />
+    <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"ইউএছবি ডিবাগিঙৰ অনুমতি নাই"</string>
     <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"এই ডিভাইচটোত বর্তমান ছাইন ইন হৈ থকা ব্যৱহাৰকাৰীজনে ইউএছবি ডিবাগিং অন কৰিব নোৱাৰে। এই সুবিধাটো ব্যৱহাৰ কৰিবলৈ হ\'লে মুখ্য ব্যৱহাৰকাৰী হিচাপে ছাইন ইন কৰক।"</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"স্ক্ৰীণ পূর্ণ কৰিবলৈ জুম কৰক"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"স্ক্ৰীণ পূর্ণ কৰিবলৈ প্ৰসাৰিত কৰক"</string>
@@ -78,15 +78,13 @@
     <skip />
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"স্ক্ৰীণশ্বট ছেভ কৰি থকা হৈছে…"</string>
-    <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
-    <skip />
     <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
     <skip />
     <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
     <skip />
-    <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+    <!-- no translation found for screenshot_failed_title (7612509838919089748) -->
     <skip />
-    <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+    <!-- no translation found for screenshot_failed_to_save_unknown_text (3637758096565605541) -->
     <skip />
     <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
     <skip />
@@ -105,18 +103,15 @@
     <string name="accessibility_search_light" msgid="1103867596330271848">"সন্ধান কৰক"</string>
     <string name="accessibility_camera_button" msgid="8064671582820358152">"কেমেৰা"</string>
     <string name="accessibility_phone_button" msgid="6738112589538563574">"ফ\'ন"</string>
-    <!-- no translation found for accessibility_voice_assist_button (487611083884852965) -->
-    <skip />
+    <string name="accessibility_voice_assist_button" msgid="487611083884852965">"কণ্ঠধ্বনিৰে সহায়"</string>
     <string name="accessibility_unlock_button" msgid="128158454631118828">"আনলক কৰক"</string>
     <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ফিংগাৰপ্ৰিণ্টৰ বাবে ৰৈ থকা হৈছে"</string>
     <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ নকৰাকৈ আনলক কৰক"</string>
     <string name="unlock_label" msgid="8779712358041029439">"আনলক কৰক"</string>
     <string name="phone_label" msgid="2320074140205331708">"ফ\'ন খোলক"</string>
-    <!-- no translation found for voice_assist_label (3956854378310019854) -->
-    <skip />
+    <string name="voice_assist_label" msgid="3956854378310019854">"কণ্ঠধ্বনিৰে সহায় খোলক"</string>
     <string name="camera_label" msgid="7261107956054836961">"কেমেৰা খোলক"</string>
-    <!-- no translation found for recents_caption_resize (3517056471774958200) -->
-    <skip />
+    <string name="recents_caption_resize" msgid="3517056471774958200">"নতুন কাৰ্যৰ চানেকি বাছনি কৰক"</string>
     <string name="cancel" msgid="6442560571259935130">"বাতিল কৰক"</string>
     <!-- no translation found for fingerprint_dialog_touch_sensor (8511557690663181761) -->
     <skip />
@@ -153,10 +148,8 @@
     <string name="accessibility_wimax_two_bars" msgid="9176236858336502288">"WiMAXৰ দুডাল দণ্ড আছে।"</string>
     <string name="accessibility_wimax_three_bars" msgid="6116551636752103927">"WiMAXৰ তিনিডাল দণ্ড আছে।"</string>
     <string name="accessibility_wimax_signal_full" msgid="2768089986795579558">"WiMAXৰ ছিগনেল পূৰা আছে৷"</string>
-    <!-- no translation found for accessibility_ethernet_disconnected (5896059303377589469) -->
-    <skip />
-    <!-- no translation found for accessibility_ethernet_connected (2692130313069182636) -->
-    <skip />
+    <string name="accessibility_ethernet_disconnected" msgid="5896059303377589469">"ইথাৰনেট সংযোগ বিচ্ছিন্ন হৈছে।"</string>
+    <string name="accessibility_ethernet_connected" msgid="2692130313069182636">"ইথাৰনেট সংযোগ হৈছে।"</string>
     <string name="accessibility_no_signal" msgid="7064645320782585167">"কোনো ছিগনেল নাই।"</string>
     <string name="accessibility_not_connected" msgid="6395326276213402883">"সংযোগ হৈ থকা নাই।"</string>
     <string name="accessibility_zero_bars" msgid="3806060224467027887">"এডালো দণ্ড নাই।"</string>
@@ -168,28 +161,41 @@
     <string name="accessibility_desc_off" msgid="6475508157786853157">"অফ।"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"সংযোগ কৰা হ’ল।"</string>
     <string name="accessibility_desc_connecting" msgid="3812924520316280149">"সংযোগ কৰি থকা হৈছে।"</string>
-    <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"জিপিআৰএছ"</string>
-    <string name="accessibility_data_connection_1x" msgid="994133468120244018">"১ X"</string>
-    <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
-    <string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
-    <string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
-    <string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
-    <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
-    <string name="accessibility_data_connection_lte" msgid="5413468808637540658">"এলটিই"</string>
-    <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
-    <string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
-    <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ৰ\'মিং"</string>
-    <string name="accessibility_data_connection_edge" msgid="4477457051631979278">"Edge"</string>
+    <!-- no translation found for data_connection_gprs (7652872568358508452) -->
+    <skip />
+    <!-- no translation found for data_connection_hspa (1499615426569473562) -->
+    <skip />
+    <!-- no translation found for data_connection_3g (503045449315378373) -->
+    <skip />
+    <!-- no translation found for data_connection_3_5g (5218328297191657602) -->
+    <skip />
+    <!-- no translation found for data_connection_3_5g_plus (7570783890290275297) -->
+    <skip />
+    <!-- no translation found for data_connection_4g (9139963475267449144) -->
+    <skip />
+    <!-- no translation found for data_connection_4g_plus (1148687201877800700) -->
+    <skip />
+    <!-- no translation found for data_connection_lte (2694876797724028614) -->
+    <skip />
+    <!-- no translation found for data_connection_lte_plus (3423013208570937424) -->
+    <skip />
+    <!-- no translation found for data_connection_cdma (4677985502159869585) -->
+    <skip />
+    <!-- no translation found for data_connection_roaming (6037232010953697354) -->
+    <skip />
+    <!-- no translation found for data_connection_edge (871835227939216682) -->
+    <skip />
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"ৱাই-ফাই"</string>
     <string name="accessibility_no_sim" msgid="8274017118472455155">"ছিম নাই।"</string>
     <string name="accessibility_cell_data" msgid="5326139158682385073">"ম\'বাইল ডেটা"</string>
     <string name="accessibility_cell_data_on" msgid="5927098403452994422">"ম\'বাইল ডেটা অন অৱস্থাত আছে"</string>
-    <string name="accessibility_cell_data_off" msgid="443267573897409704">"ম\'বাইল ডেটা অফ অৱস্থাত আছে"</string>
+    <!-- no translation found for cell_data_off (5287705247512911922) -->
+    <skip />
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"ব্লুটুথ টেডাৰিং।"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"এয়াৰপ্লেইন ম\'ড।"</string>
     <string name="accessibility_vpn_on" msgid="5993385083262856059">"ভিপিএন অন অৱস্থাত আছে।"</string>
     <string name="accessibility_no_sims" msgid="3957997018324995781">"কোনো ছিম কাৰ্ড নাই"</string>
-    <!-- no translation found for accessibility_carrier_network_change_mode (4017301580441304305) -->
+    <!-- no translation found for carrier_network_change_mode (8149202439957837762) -->
     <skip />
     <string name="accessibility_battery_details" msgid="7645516654955025422">"বেটাৰিৰ বিৱৰণসমূহ খোলক"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"<xliff:g id="NUMBER">%d</xliff:g> শতাংশ বেটাৰি।"</string>
@@ -209,8 +215,7 @@
     <skip />
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g>ক আঁতৰাব।"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> আঁতৰোৱা হৈছে৷"</string>
-    <!-- no translation found for accessibility_recents_all_items_dismissed (4464697366179168836) -->
-    <skip />
+    <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"শেহতীয়া সকলো এপ্লিকেশ্বন অগ্ৰাহ্য কৰা হৈছে।"</string>
     <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> এপ্লিকেশ্বনৰ তথ্য় খোলক।"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> আৰম্ভ কৰা হৈছে।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"জাননী অগ্ৰাহ্য কৰা হৈছে।"</string>
@@ -220,8 +225,7 @@
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ছেটিংসমূহ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"অৱলোকন।"</string>
     <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"কৰ্মস্থানৰ প্ৰ\'ফাইলৰ লক স্ক্ৰীণ"</string>
-    <!-- no translation found for accessibility_desc_close (7479755364962766729) -->
-    <skip />
+    <string name="accessibility_desc_close" msgid="7479755364962766729">"বন্ধ কৰক"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ৱাই-ফাই অফ কৰা হ\'ল।"</string>
     <string name="accessibility_quick_settings_wifi_changed_on" msgid="6440117170789528622">"ৱাই-ফাই অন কৰা হ\'ল।"</string>
@@ -231,19 +235,14 @@
     <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"এয়াৰপ্লেইন ম\'ড অন হৈ আছে৷"</string>
     <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"এয়াৰপ্লেইন ম\'ড অফ কৰা হ\'ল।"</string>
     <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"এয়াৰপ্লেইন ম\'ড অন কৰা হ\'ল।"</string>
-    <!-- no translation found for accessibility_quick_settings_dnd_priority_on (1448402297221249355) -->
+    <!-- no translation found for accessibility_quick_settings_dnd_priority_on (5836205286254617194) -->
     <skip />
-    <!-- no translation found for accessibility_quick_settings_dnd_none_on (6882582132662613537) -->
-    <skip />
-    <!-- no translation found for accessibility_quick_settings_dnd_alarms_on (9152834845587554157) -->
-    <skip />
+    <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"অসুবিধা নিদিব অন হৈ আছে, সম্পূর্ণ নিৰৱতা।"</string>
+    <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"অসুবিধা নিদিব অন হৈ আছে, মাত্ৰ এলাৰ্মসমূহ বাজিব।"</string>
     <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"অসুবিধা নিদিব।"</string>
-    <!-- no translation found for accessibility_quick_settings_dnd_off (2371832603753738581) -->
-    <skip />
-    <!-- no translation found for accessibility_quick_settings_dnd_changed_off (898107593453022935) -->
-    <skip />
-    <!-- no translation found for accessibility_quick_settings_dnd_changed_on (4483780856613561039) -->
-    <skip />
+    <string name="accessibility_quick_settings_dnd_off" msgid="2371832603753738581">"অসুবিধা নিদিব বন্ধ হৈ আছে।"</string>
+    <string name="accessibility_quick_settings_dnd_changed_off" msgid="898107593453022935">"অসুবিধা নিদিব বন্ধ কৰা হ\'ল।"</string>
+    <string name="accessibility_quick_settings_dnd_changed_on" msgid="4483780856613561039">"অসুবিধা নিদিব অন কৰা হৈছে।"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="6341675755803320038">"ব্লুটুথ।"</string>
     <string name="accessibility_quick_settings_bluetooth_off" msgid="2133631372372064339">"ব্লুটুথ অফ হৈ আছে।"</string>
     <string name="accessibility_quick_settings_bluetooth_on" msgid="7681999166216621838">"ব্লুটুথ অন হৈ আছে।"</string>
@@ -277,16 +276,12 @@
     <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ডেটা সঞ্চয়কাৰী সুবিধা অন কৰা হ\'ল।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ডিছপ্লেৰ উজ্জ্বলতা"</string>
     <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"চ্চার্জ কৰি থকা হৈছে"</string>
-    <!-- no translation found for data_usage_disabled_dialog_3g_title (5281770593459841889) -->
-    <skip />
-    <!-- no translation found for data_usage_disabled_dialog_4g_title (1601769736881078016) -->
-    <skip />
+    <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ডেটা পজ কৰা হৈছে"</string>
+    <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ডেটা পজ কৰা হৈছে"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"ম\'বাইল ডেটা পজ কৰা হৈছে"</string>
-    <!-- no translation found for data_usage_disabled_dialog_title (3932437232199671967) -->
-    <skip />
+    <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ডেটা পজ কৰা হৈছে"</string>
     <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"আপুনি নির্ধাৰণ কৰা ডেটাৰ সীমা শেষ হৈছে। আপুনি এতিয়া ম\'বাইল ডেটা ব্যৱহাৰ কৰিব নোৱাৰে। \n\nযদিহে আপুনি আকৌ ম\'বাইল ডেটা ব্যৱহাৰ কৰে তেন্তে ডেটাৰ ব্যৱহাৰৰ বাবে মাচুল ভৰিবলগীয়া হ\'ব পাৰে।"</string>
-    <!-- no translation found for data_usage_disabled_dialog_enable (1412395410306390593) -->
-    <skip />
+    <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"পুনৰ আৰম্ভ কৰক"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"জিপিএছ সন্ধান কৰি থকা হৈছে"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"জিপিএছএ অৱস্থান ছেট কৰিছে"</string>
     <string name="accessibility_location_active" msgid="2427290146138169014">"অৱস্থানৰ অনুৰোধ সক্ৰিয় হৈ আছে"</string>
@@ -307,16 +302,12 @@
     <string name="dessert_case" msgid="1295161776223959221">"মিষ্টান্ন ভাণ্ডাৰ"</string>
     <string name="start_dreams" msgid="5640361424498338327">"স্ক্ৰীণ ছেভাৰ"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"ইথাৰনেট"</string>
-    <!-- no translation found for quick_settings_header_onboarding_text (7872508260264044734) -->
+    <!-- no translation found for quick_settings_header_onboarding_text (8030309023792936283) -->
     <skip />
-    <!-- no translation found for quick_settings_dnd_label (8735855737575028208) -->
-    <skip />
-    <!-- no translation found for quick_settings_dnd_priority_label (483232950670692036) -->
-    <skip />
-    <!-- no translation found for quick_settings_dnd_alarms_label (2559229444312445858) -->
-    <skip />
-    <!-- no translation found for quick_settings_dnd_none_label (5025477807123029478) -->
-    <skip />
+    <string name="quick_settings_dnd_label" msgid="8735855737575028208">"অসুবিধা নিদিব"</string>
+    <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"কেৱল গুৰুত্বপূৰ্ণ"</string>
+    <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"কেৱল এলাৰ্মসমূহ"</string>
+    <string name="quick_settings_dnd_none_label" msgid="5025477807123029478">"সম্পূর্ণ নিৰৱতা"</string>
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"ব্লুটুথ"</string>
     <string name="quick_settings_bluetooth_multiple_devices_label" msgid="3912245565613684735">"ব্লুটুথ (<xliff:g id="NUMBER">%d</xliff:g>টা ডিভাইচ)"</string>
     <string name="quick_settings_bluetooth_off_label" msgid="8159652146149219937">"ব্লুটুথ বন্ধ অৱস্থাত আছে"</string>
@@ -354,12 +345,10 @@
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"নেটৱৰ্ক নাই"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"ৱাই-ফাই অফ"</string>
     <string name="quick_settings_wifi_on_label" msgid="7607810331387031235">"ৱাই-ফাই অন হৈ আছে"</string>
-    <!-- no translation found for quick_settings_wifi_detail_empty_text (269990350383909226) -->
-    <skip />
+    <string name="quick_settings_wifi_detail_empty_text" msgid="269990350383909226">"কোনো ৱাই-ফাই নেটৱৰ্ক নাই"</string>
     <!-- no translation found for quick_settings_wifi_secondary_label_transient (7748206246119760554) -->
     <skip />
-    <!-- no translation found for quick_settings_cast_title (7709016546426454729) -->
-    <skip />
+    <string name="quick_settings_cast_title" msgid="7709016546426454729">"কাষ্ট"</string>
     <string name="quick_settings_casting" msgid="6601710681033353316">"কাষ্টিং"</string>
     <string name="quick_settings_cast_device_default_name" msgid="5367253104742382945">"নাম নথকা ডিভাইচ"</string>
     <string name="quick_settings_cast_device_default_description" msgid="2484573682378634413">"কাষ্টৰ বাবে সাজু"</string>
@@ -377,6 +366,8 @@
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"হটস্পট"</string>
     <!-- no translation found for quick_settings_hotspot_secondary_label_transient (8010579363691405477) -->
     <skip />
+    <!-- no translation found for quick_settings_hotspot_secondary_label_data_saver_enabled (5672131949987422420) -->
+    <skip />
     <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"জাননীসমূহ"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ফ্লাশ্বলাইট"</string>
@@ -396,7 +387,7 @@
     <skip />
     <!-- no translation found for quick_settings_night_secondary_label_on_at (6256314040368487637) -->
     <skip />
-    <!-- no translation found for quick_settings_night_secondary_label_until (8664820079774824618) -->
+    <!-- no translation found for quick_settings_secondary_label_until (2749196569462600150) -->
     <skip />
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC নিষ্ক্ৰিয় হৈ আছে"</string>
@@ -410,12 +401,9 @@
     <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g>টো সুৰক্ষিত ম\'ডত অক্ষম কৰা হ\'ল।"</string>
     <string name="recents_stack_action_button_label" msgid="6593727103310426253">"সকলো মচক"</string>
     <string name="recents_drag_hint_message" msgid="2649739267073203985">"বিভাজিত স্ক্ৰীণ ব্য়ৱহাৰ কৰিবলৈ ইয়ালৈ টানি আনি এৰক"</string>
-    <!-- no translation found for recents_multistack_add_stack_dialog_split_horizontal (8848514474543427332) -->
-    <skip />
-    <!-- no translation found for recents_multistack_add_stack_dialog_split_vertical (9075292233696180813) -->
-    <skip />
-    <!-- no translation found for recents_multistack_add_stack_dialog_split_custom (4177837597513701943) -->
-    <skip />
+    <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"আনুভূমিকভাৱে বিভাজিত কৰক"</string>
+    <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"উলম্বভাৱে বিভাজিত কৰক"</string>
+    <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"উপযোগিতা অনুসৰি বিভাজিত কৰক"</string>
     <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"স্ক্ৰীণখনক ওপৰফাললৈ ভাগ কৰক"</string>
     <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"স্ক্ৰীণখনক বাওঁফাললৈ ভাগ কৰক"</string>
     <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"স্ক্ৰীণখনক সোঁফাললৈ ভাগ কৰক"</string>
@@ -429,8 +417,7 @@
     <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>ৰ বাবে বাওঁফাললৈ শ্লাইড কৰক।"</string>
     <string name="zen_priority_introduction" msgid="1149025108714420281">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্ম, ৰিমাইণ্ডাৰ, ইভেন্ট আৰু কল কৰোঁতাৰ বাহিৰে আন কোনো শব্দৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
     <string name="zen_alarms_introduction" msgid="4934328096749380201">"আপুনি নিৰ্দিষ্ট কৰা এলাৰ্মৰ বাহিৰে আন কোনো ধ্বনি আৰু কম্পনৰ পৰা আপুনি অসুবিধা নাপাব। কিন্তু, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি আপুনি প্লে কৰিব খোজা যিকোনো বস্তু তথাপি শুনিব পাৰিব।"</string>
-    <!-- no translation found for zen_priority_customize_button (7948043278226955063) -->
-    <skip />
+    <string name="zen_priority_customize_button" msgid="7948043278226955063">"নিজৰ উপযোগিতা অনুসৰি"</string>
     <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"এই কার্যই এলার্ম, সংগীত, ভিডিঅ\' আৰু খেলসমূহকে ধৰি সকলোৰে বাবে ধ্বনি আৰু কম্পন অৱৰোধ কৰিব। আপুনি ফ\'ন কল তথাপি কৰিবলৈ সক্ষম হ\'ব।"</string>
     <string name="zen_silence_introduction" msgid="3137882381093271568">"এই কার্যই এলার্ম, মিউজিক, ভিডিঅ\' আৰু গেইমকে ধৰি সকলোৰে ধ্বনি আৰু কম্পন অৱৰোধ কৰে।"</string>
     <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
@@ -439,25 +426,16 @@
     <string name="keyguard_unlock" msgid="8043466894212841998">"আনলক কৰিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string>
     <string name="do_disclosure_generic" msgid="5615898451805157556">"আপোনাৰ প্ৰতিষ্ঠানে এই ডিভাইচটো পৰিচালনা কৰে"</string>
     <string name="do_disclosure_with_name" msgid="5640615509915445501">"এই ডিভাইচটো <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>ৰ দ্বাৰা পৰিচালিত।"</string>
-    <!-- no translation found for phone_hint (4872890986869209950) -->
-    <skip />
-    <!-- no translation found for voice_hint (8939888732119726665) -->
-    <skip />
-    <!-- no translation found for camera_hint (7939688436797157483) -->
-    <skip />
+    <string name="phone_hint" msgid="4872890986869209950">"ফ\'নৰ বাবে আইকনৰপৰা ছোৱাইপ কৰক"</string>
+    <string name="voice_hint" msgid="8939888732119726665">"কণ্ঠধ্বনিৰে সহায়ৰ বাবে আইকনৰ পৰা ছোৱাইপ কৰক"</string>
+    <string name="camera_hint" msgid="7939688436797157483">"কেমেৰা খুলিবলৈ আইকনৰপৰা ছোৱাইপ কৰক"</string>
     <string name="interruption_level_none_with_warning" msgid="5114872171614161084">"সম্পূর্ণ নিৰৱতা। এই কার্যই স্ক্ৰীণ ৰীডাৰসমূহকো নিৰৱ কৰিব।"</string>
-    <!-- no translation found for interruption_level_none (6000083681244492992) -->
-    <skip />
-    <!-- no translation found for interruption_level_priority (6426766465363855505) -->
-    <skip />
-    <!-- no translation found for interruption_level_alarms (5226306993448328896) -->
-    <skip />
-    <!-- no translation found for interruption_level_none_twoline (3957581548190765889) -->
-    <skip />
-    <!-- no translation found for interruption_level_priority_twoline (1564715335217164124) -->
-    <skip />
-    <!-- no translation found for interruption_level_alarms_twoline (3266909566410106146) -->
-    <skip />
+    <string name="interruption_level_none" msgid="6000083681244492992">"সম্পূর্ণ নিৰৱতা"</string>
+    <string name="interruption_level_priority" msgid="6426766465363855505">"কেৱল গুৰুত্বপূৰ্ণ"</string>
+    <string name="interruption_level_alarms" msgid="5226306993448328896">"কেৱল এলাৰ্মসমূহ"</string>
+    <string name="interruption_level_none_twoline" msgid="3957581548190765889">"সম্পূর্ণ \n নিৰৱতা"</string>
+    <string name="interruption_level_priority_twoline" msgid="1564715335217164124">"কেৱল\nগুৰুত্বপূৰ্ণ"</string>
+    <string name="interruption_level_alarms_twoline" msgid="3266909566410106146">"কেৱল\nএলাৰ্মসমূহ"</string>
     <!-- no translation found for keyguard_indication_charging_time (2056340799276374421) -->
     <skip />
     <!-- no translation found for keyguard_indication_charging_time_fast (7767562163577492332) -->
@@ -480,11 +458,9 @@
     <string name="guest_wipe_session_message" msgid="8476238178270112811">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string>
     <string name="guest_wipe_session_wipe" msgid="5065558566939858884">"আকৌ আৰম্ভ কৰক"</string>
     <string name="guest_wipe_session_dontwipe" msgid="1401113462524894716">"হয়, অব্যাহত ৰাখক"</string>
-    <!-- no translation found for guest_notification_title (1585278533840603063) -->
-    <skip />
+    <string name="guest_notification_title" msgid="1585278533840603063">"অতিথি ব্য়ৱহাৰকাৰী"</string>
     <string name="guest_notification_text" msgid="335747957734796689">"এপসমূহ আৰু ডেটা মচিবলৈ অতিথি ব্যৱহাৰকাৰীক আঁতৰাওক"</string>
-    <!-- no translation found for guest_notification_remove_action (8820670703892101990) -->
-    <skip />
+    <string name="guest_notification_remove_action" msgid="8820670703892101990">"অতিথি আঁতৰাওক"</string>
     <string name="user_logout_notification_title" msgid="1453960926437240727">"ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
     <string name="user_logout_notification_text" msgid="3350262809611876284">"বর্তমানৰ ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
     <string name="user_logout_notification_action" msgid="1195428991423425062">"ব্যৱহাৰকাৰীক লগ আউট কৰক"</string>
@@ -499,6 +475,10 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"আপোনাৰ স্ক্ৰীণত প্ৰদৰ্শন হোৱা সকলো <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> কেপশ্বাৰ কৰা আৰম্ভ কৰিব।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"পুনৰাই নেদেখুৱাব"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"সকলো মচক"</string>
+    <!-- no translation found for manage_notifications_text (8035284146227267681) -->
+    <skip />
+    <!-- no translation found for dnd_suppressing_shade_text (7986451830430707907) -->
+    <skip />
     <string name="media_projection_action_text" msgid="8470872969457985954">"এতিয়াই আৰম্ভ কৰক"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"কোনো জাননী নাই"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"প্ৰ\'ফাইল নিৰীক্ষণ কৰা হ\'ব পাৰে"</string>
@@ -549,11 +529,9 @@
     <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
     <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"বিশ্বাসী পৰিচয়-পত্ৰসমূহ খোলক"</string>
     <string name="monitoring_description_network_logging" msgid="7223505523384076027">"আপোনাৰ প্ৰশাসকে নেটৱৰ্ক লগিং অন কৰিছে, যিয়ে আপোনাৰ ডিভাইচটোত নেটৱৰ্ক ট্ৰেফিক পৰ্যবেক্ষণ কৰে।\n\nএই সম্পৰ্কে অধিক জানিবলৈ আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।"</string>
-    <!-- no translation found for monitoring_description_vpn (4445150119515393526) -->
-    <skip />
+    <string name="monitoring_description_vpn" msgid="4445150119515393526">"আপুনি এটা এপক ভিপিএন সংযোগ ছেট আপ কৰিবলৈ অনুমতি দিছে। \n\n এই এপটোৱে ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি আপোনাৰ নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
     <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"<xliff:g id="ORGANIZATION">%1$s</xliff:g>য়ে আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল পৰিচালনা কৰে।\n\nআপোনাৰ প্ৰশাসকে ইমেইল, এপসমূহ আৰু আপুনি চোৱা ৱেবছাইটকে ধৰি আপোনাৰ নেটৱৰ্কৰ সকলো কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে। \n\nঅধিক তথ্যৰ বাবে আপোনাৰ প্ৰশাসকৰ সৈতে যোগাযোগ কৰক।\n\nইয়াৰ উপৰি, আপুনি এটা ভিপিএনৰ সৈতে সংযুক্ত হৈ আছে, যিয়ে আপোনাৰ নেটৱৰ্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
-    <!-- no translation found for legacy_vpn_name (6604123105765737830) -->
-    <skip />
+    <string name="legacy_vpn_name" msgid="6604123105765737830">"ভিপিএন"</string>
     <string name="monitoring_description_app" msgid="1828472472674709532">"আপুনি <xliff:g id="APPLICATION">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে যিয়ে আপোনাৰ ইমেইল, এপ্ আৰু ৱেবছাইটকে ধৰি নেটৱর্কৰ কাৰ্যকলাপ পৰ্যবেক্ষণ কৰিব পাৰে।"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"আপুনি <xliff:g id="APPLICATION">%1$s</xliff:g>ৰে সংযুক্ত হৈ আছে, যি ইমেইল, এপ্ আৰু ৱেবছাইটসমূহকে ধৰি আপোনাৰ ব্যক্তিগত নেটৱর্কৰ কাৰ্যকলাপ নিৰীক্ষণ কৰিব পাৰে।"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"আপুনি <xliff:g id="APPLICATION">%1$s</xliff:g>ৰ সৈতে সংযুক্ত হৈ আছে, যিয়ে ইমেইল, এপ্ আৰু ৱেবছাইটসমূহকে সামৰি আপোনাৰ ব্যক্তিগত নেটৱর্কৰ কার্যকলাপ নিৰীক্ষণ কৰিব পাৰে।"</string>
@@ -568,10 +546,10 @@
     <string name="hidden_notifications_setup" msgid="41079514801976810">"ছেট আপ কৰক"</string>
     <string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
     <string name="volume_zen_end_now" msgid="6930243045593601084">"এতিয়া অফ কৰক"</string>
-    <!-- no translation found for accessibility_volume_expand (5946812790999244205) -->
+    <!-- no translation found for accessibility_volume_settings (4915364006817819212) -->
     <skip />
-    <!-- no translation found for accessibility_volume_collapse (3609549593031810875) -->
-    <skip />
+    <string name="accessibility_volume_expand" msgid="5946812790999244205">"সম্প্ৰসাৰণ কৰক"</string>
+    <string name="accessibility_volume_collapse" msgid="3609549593031810875">"সংকুচিত কৰক"</string>
     <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
     <skip />
     <string name="screen_pinning_title" msgid="3273740381976175811">"স্ক্ৰীণ পিন কৰা হ\'ল"</string>
@@ -615,10 +593,6 @@
     <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। মিউট কৰিবলৈ টিপক।"</string>
     <!-- no translation found for volume_dialog_title (7272969888820035876) -->
     <skip />
-    <!-- no translation found for volume_dialog_ringer_guidance_vibrate (8902050240801159042) -->
-    <skip />
-    <!-- no translation found for volume_dialog_ringer_guidance_silent (2128975224280276122) -->
-    <skip />
     <!-- no translation found for volume_dialog_ringer_guidance_ring (6144469689490528338) -->
     <skip />
     <string name="output_title" msgid="5355078100792942802">"মিডিয়া আউটপুট"</string>
@@ -629,44 +603,26 @@
     <string name="output_service_wifi" msgid="3749735218931825054">"ৱাই-ফাই"</string>
     <string name="output_service_bt_wifi" msgid="4486837869988770896">"ব্লুটুথ আৰু ৱাই-ফাই"</string>
     <string name="system_ui_tuner" msgid="708224127392452018">"System UI Tuner"</string>
-    <!-- no translation found for show_battery_percentage (5444136600512968798) -->
-    <skip />
-    <!-- no translation found for show_battery_percentage_summary (3215025775576786037) -->
-    <skip />
-    <!-- no translation found for quick_settings (10042998191725428) -->
-    <skip />
-    <!-- no translation found for status_bar (4877645476959324760) -->
-    <skip />
+    <string name="show_battery_percentage" msgid="5444136600512968798">"সংযুক্ত বেটাৰিৰ কিমান শতাংশ বাকী আছে দেখুওৱাক"</string>
+    <string name="show_battery_percentage_summary" msgid="3215025775576786037">"চাৰ্জ হৈ নথকা অৱস্থাত বেটাৰি কিমান শতাংশ বাকী স্থিতি দণ্ডৰ ভিতৰত দেখুৱাওক"</string>
+    <string name="quick_settings" msgid="10042998191725428">"ক্ষিপ্ৰ ছেটিংসমূহ"</string>
+    <string name="status_bar" msgid="4877645476959324760">"স্থিতি দণ্ড"</string>
     <string name="overview" msgid="4018602013895926956">"অৱলোকন"</string>
     <string name="demo_mode" msgid="2532177350215638026">"ছিষ্টেমৰ UI প্ৰদৰ্শন ম\'ড"</string>
-    <!-- no translation found for enable_demo_mode (4844205668718636518) -->
-    <skip />
-    <!-- no translation found for show_demo_mode (2018336697782464029) -->
-    <skip />
-    <!-- no translation found for status_bar_ethernet (5044290963549500128) -->
-    <skip />
-    <!-- no translation found for status_bar_alarm (8536256753575881818) -->
-    <skip />
-    <!-- no translation found for status_bar_work (6022553324802866373) -->
-    <skip />
-    <!-- no translation found for status_bar_airplane (7057575501472249002) -->
-    <skip />
-    <!-- no translation found for add_tile (2995389510240786221) -->
-    <skip />
-    <!-- no translation found for broadcast_tile (3894036511763289383) -->
-    <skip />
-    <!-- no translation found for zen_alarm_warning_indef (3482966345578319605) -->
-    <skip />
-    <!-- no translation found for zen_alarm_warning (444533119582244293) -->
-    <skip />
-    <!-- no translation found for alarm_template (3980063409350522735) -->
-    <skip />
-    <!-- no translation found for alarm_template_far (4242179982586714810) -->
-    <skip />
-    <!-- no translation found for accessibility_quick_settings_detail (2579369091672902101) -->
-    <skip />
-    <!-- no translation found for accessibility_status_bar_hotspot (4099381329956402865) -->
-    <skip />
+    <string name="enable_demo_mode" msgid="4844205668718636518">"ডেম\' ম\'ড সক্ষম কৰক"</string>
+    <string name="show_demo_mode" msgid="2018336697782464029">"ডেম\' ম\'ড দেখুৱাওক"</string>
+    <string name="status_bar_ethernet" msgid="5044290963549500128">"ইথাৰনেট"</string>
+    <string name="status_bar_alarm" msgid="8536256753575881818">"এলাৰ্ম"</string>
+    <string name="status_bar_work" msgid="6022553324802866373">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
+    <string name="status_bar_airplane" msgid="7057575501472249002">"এয়াৰপ্লেইন ম\'ড"</string>
+    <string name="add_tile" msgid="2995389510240786221">"টাইল যোগ দিয়ক"</string>
+    <string name="broadcast_tile" msgid="3894036511763289383">"সম্প্ৰচাৰ টাইল"</string>
+    <string name="zen_alarm_warning_indef" msgid="3482966345578319605">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব যদিহে তাৰ আগতে আপুনি এইটো অফ নকৰে"</string>
+    <string name="zen_alarm_warning" msgid="444533119582244293">"আপুনি আপোনাৰ পিছৰটো এলাৰ্ম <xliff:g id="WHEN">%1$s</xliff:g> বজাত শুনা নাপাব"</string>
+    <string name="alarm_template" msgid="3980063409350522735">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
+    <string name="alarm_template_far" msgid="4242179982586714810">"<xliff:g id="WHEN">%1$s</xliff:g> বজাত"</string>
+    <string name="accessibility_quick_settings_detail" msgid="2579369091672902101">"ক্ষিপ্ৰ ছেটিংসমূহ, <xliff:g id="TITLE">%s</xliff:g>।"</string>
+    <string name="accessibility_status_bar_hotspot" msgid="4099381329956402865">"হটস্পট"</string>
     <string name="accessibility_managed_profile" msgid="6613641363112584120">"কৰ্মস্থানৰ প্ৰ\'ফাইল"</string>
     <string name="tuner_warning_title" msgid="7094689930793031682">"কিছুমানৰ বাবে আমোদজনক হয় কিন্তু সকলোৰে বাবে নহয়"</string>
     <string name="tuner_warning" msgid="8730648121973575701">"System UI Tunerএ আপোনাক Android ব্যৱহাৰকাৰী ইণ্টাৰফেইচ সলনি কৰিবলৈ আৰু নিজৰ উপযোগিতা অনুসৰি ব্যৱহাৰ কৰিবলৈ অতিৰিক্ত সুবিধা প্ৰদান কৰে। এই পৰীক্ষামূলক সুবিধাসমূহ সলনি হ\'ব পাৰে, সেইবোৰে কাম নকৰিব পাৰে বা আগন্তুক সংস্কৰণসমূহত সেইবোৰ অন্তৰ্ভুক্ত কৰা নহ\'ব পাৰে। সাৱধানেৰে আগবাঢ়ক।"</string>
@@ -695,6 +651,8 @@
     <string name="notification_header_default_channel" msgid="7506845022070889909">"জাননীসমূহ"</string>
     <!-- no translation found for notification_channel_disabled (344536703863700565) -->
     <skip />
+    <!-- no translation found for notification_channel_minimized (1664411570378910931) -->
+    <skip />
     <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
     <skip />
     <!-- no translation found for inline_keep_showing (8945102997083836858) -->
@@ -703,10 +661,24 @@
     <skip />
     <!-- no translation found for inline_keep_button (6665940297019018232) -->
     <skip />
+    <!-- no translation found for inline_minimize_button (966233327974702195) -->
+    <skip />
     <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
     <skip />
     <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
     <skip />
+    <!-- no translation found for notification_appops_camera_active (730959943016785931) -->
+    <skip />
+    <!-- no translation found for notification_appops_microphone_active (1546319728924580686) -->
+    <skip />
+    <!-- no translation found for notification_appops_overlay_active (633813008357934729) -->
+    <skip />
+    <!-- no translation found for notification_appops (1258122060887196817) -->
+    <!-- no translation found for notification_using (2211008461429037973) -->
+    <!-- no translation found for notification_appops_settings (1028328314935908050) -->
+    <skip />
+    <!-- no translation found for notification_appops_ok (602562195588819631) -->
+    <skip />
     <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ খোলা অৱস্থাত আছে"</string>
     <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ জাননী নিয়ন্ত্ৰণসমূহ বন্ধ অৱস্থাত আছে"</string>
     <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"এই চ্চেনেলৰ পৰা জাননী দিবলৈ অনুমতি দিয়ক"</string>
@@ -719,6 +691,8 @@
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"জাননীৰ নিয়ন্ত্ৰণসমূহ"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"জাননীক স্নুজ কৰাৰ বিকল্পসমূহ"</string>
+    <!-- no translation found for notification_menu_snooze_action (1112254519029621372) -->
+    <skip />
     <string name="snooze_undo" msgid="6074877317002985129">"আনডু কৰক"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>ৰ বাবে স্নুজ কৰক"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
@@ -811,8 +785,11 @@
     <string name="right_keycode" msgid="708447961000848163">"সোঁ কীক\'ড"</string>
     <string name="left_icon" msgid="3096287125959387541">"বাওঁ আইকন"</string>
     <string name="right_icon" msgid="3952104823293824311">"সোঁ আইকন"</string>
-    <string name="drag_to_add_tiles" msgid="7058945779098711293">"টাইল যোগ কৰিবৰ বাবে টানি আনি এৰক"</string>
+    <!-- no translation found for drag_to_add_tiles (230586591689084925) -->
+    <skip />
     <string name="drag_to_remove_tiles" msgid="3361212377437088062">"আঁতৰাবৰ বাবে টানি আনি ইয়াত এৰি দিয়ক"</string>
+    <!-- no translation found for drag_to_remove_disabled (2390968976638993382) -->
+    <skip />
     <string name="qs_edit" msgid="2232596095725105230">"সম্পাদনা কৰক"</string>
     <string name="tuner_time" msgid="6572217313285536011">"সময়"</string>
   <string-array name="clock_options">
@@ -856,7 +833,8 @@
     <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"ক্ষিপ্ৰ ছেটিংসমূহ বন্ধ কৰক।"</string>
     <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"এলার্ম ছেট কৰা হ\'ল।"</string>
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> হিচাপে ছাইন ইন হ\'ল"</string>
-    <string name="accessibility_quick_settings_no_internet" msgid="31890692343084075">"ইণ্টাৰনেট সংযোগ নাই।"</string>
+    <!-- no translation found for data_connection_no_internet (4503302451650972989) -->
+    <skip />
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"বিৱৰণসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিংসমূহ খোলক।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ছেটিংসমূহৰ ক্ৰম সম্পাদনা কৰক।"</string>
@@ -899,11 +877,15 @@
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"স্ক্ৰীণশ্বটসমূহ"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"সাধাৰণ বার্তাসমূহ"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"সঞ্চয়াগাৰ"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
+    <skip />
     <string name="instant_apps" msgid="6647570248119804907">"তাৎক্ষণিক এপসমূহ"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"তাৎক্ষণিক এপসমূহক ইনষ্টল কৰাৰ প্ৰয়োজন নাই।"</string>
     <string name="app_info" msgid="6856026610594615344">"এপ্ সম্পৰ্কীয় তথ্য"</string>
     <string name="go_to_web" msgid="2650669128861626071">"ব্ৰাউজাৰলৈ যাওক"</string>
     <string name="mobile_data" msgid="7094582042819250762">"ম\'বাইল ডেটা"</string>
+    <!-- no translation found for mobile_data_text_format (3526214522670876454) -->
+    <skip />
     <string name="wifi_is_off" msgid="1838559392210456893">"ৱাই-ফাই অফ অৱস্থাত আছে"</string>
     <string name="bt_is_off" msgid="2640685272289706392">"ব্লুটুথ অফ অৱস্থাত আছে"</string>
     <string name="dnd_is_off" msgid="6167780215212497572">"অসুবিধা নিদিব অফ অৱস্থাত আছে"</string>
@@ -915,7 +897,12 @@
     <string name="qs_dnd_replace" msgid="8019520786644276623">"সলনি কৰক"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"নেপথ্যত চলি থকা এপসমূহ"</string>
     <string name="running_foreground_services_msg" msgid="6326247670075574355">"বেটাৰি আৰু ডেটাৰ ব্যৱহাৰৰ বিষয়ে বিশদভাৱে জানিবলৈ টিপক"</string>
-    <string name="data_usage_disable_mobile" msgid="5116269981510015864">"ম’বাইল ডেটা অফ কৰিবনে?"</string>
+    <!-- no translation found for mobile_data_disable_title (1068272097382942231) -->
+    <skip />
+    <!-- no translation found for mobile_data_disable_message (4756541658791493506) -->
+    <skip />
+    <!-- no translation found for mobile_data_disable_message_default_carrier (6078110473451946831) -->
+    <skip />
     <string name="touch_filtered_warning" msgid="8671693809204767551">"এটা এপে অনুমতি বিচাৰি কৰা অনুৰোধ এটা ঢাকি ধৰা বাবে ছেটিংসমূহে আপোনাৰ উত্তৰ সত্যাপন কৰিব পৰা নাই।"</string>
     <!-- no translation found for slice_permission_title (7465009437851044444) -->
     <skip />
@@ -929,4 +916,18 @@
     <skip />
     <!-- no translation found for slice_permission_deny (7683681514008048807) -->
     <skip />
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 5649557..36aa511 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -33,7 +33,6 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ongoing"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"Battery is low"</string>
-    <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Battery is low. Turn on Battery Saver"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left based on your usage"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> remaining, about <xliff:g id="TIME">%s</xliff:g> left"</string>
@@ -73,11 +72,12 @@
     <string name="global_action_screenshot" msgid="8329831278085426283">"Screenshot"</string>
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Saving screenshot…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Saving screenshot…"</string>
-    <string name="screenshot_saving_text" msgid="2545047868936087248">"Screenshot is being saved"</string>
     <string name="screenshot_saved_title" msgid="5637073968117370753">"Screenshot saved"</string>
     <string name="screenshot_saved_text" msgid="7574667448002050363">"Tap to view your screenshot"</string>
-    <string name="screenshot_failed_title" msgid="9096484883063264803">"Couldn\'t capture screenshot"</string>
-    <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problem encountered while saving screenshot"</string>
+    <!-- no translation found for screenshot_failed_title (7612509838919089748) -->
+    <skip />
+    <!-- no translation found for screenshot_failed_to_save_unknown_text (3637758096565605541) -->
+    <skip />
     <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Can\'t save screenshot due to limited storage space"</string>
     <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Taking screenshots isn\'t allowed by the app or your organisation"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"USB file transfer options"</string>
@@ -348,7 +348,8 @@
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"On at sunset"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Until sunrise"</string>
     <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"On at <xliff:g id="TIME">%s</xliff:g>"</string>
-    <string name="quick_settings_night_secondary_label_until" msgid="8664820079774824618">"Until <xliff:g id="TIME">%s</xliff:g>"</string>
+    <!-- no translation found for quick_settings_secondary_label_until (2749196569462600150) -->
+    <skip />
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC is disabled"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC is enabled"</string>
@@ -432,6 +433,8 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> will start capturing everything that\'s displayed on your screen."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Don\'t show again"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Clear all"</string>
+    <!-- no translation found for manage_notifications_text (8035284146227267681) -->
+    <skip />
     <string name="dnd_suppressing_shade_text" msgid="7986451830430707907">"Do not disturb is hiding notifications"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Start now"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"No notifications"</string>
@@ -625,6 +628,8 @@
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"notification controls"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"notification snooze options"</string>
+    <!-- no translation found for notification_menu_snooze_action (1112254519029621372) -->
+    <skip />
     <string name="snooze_undo" msgid="6074877317002985129">"UNDO"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"Snoozed for <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
@@ -811,6 +816,8 @@
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"Screenshots"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"General Messages"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"Storage"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
+    <skip />
     <string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Instant apps don\'t require installation."</string>
     <string name="app_info" msgid="6856026610594615344">"App info"</string>
@@ -838,4 +845,18 @@
     <string name="slice_permission_checkbox" msgid="7986504458640562900">"Allow <xliff:g id="APP">%1$s</xliff:g> to show slices from any app"</string>
     <string name="slice_permission_allow" msgid="2340244901366722709">"Allow"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Deny"</string>
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 783160c..cfb9033 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -33,7 +33,6 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‎‏‏‎‎‏‎‏‏‏‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‏‎‎Ongoing‎‏‎‎‏‎"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎Notifications‎‏‎‎‏‎"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‏‎‎‏‎‎‏‏‎‎‏‎‏‎Battery is low‎‏‎‎‏‎"</string>
-    <string name="battery_low_title_hybrid" msgid="6268991275887381595">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‏‎‏‏‎‏‏‎Battery is low. Turn on Battery Saver‎‏‎‎‏‎"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ remaining‎‏‎‎‏‎"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ remaining, about ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎ left based on your usage‎‏‎‎‏‎"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‏‎‏‎‎‎‏‏‎‎‎‎‎‏‏‎‏‎‎‏‏‎‎‎‎‏‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="PERCENTAGE">%s</xliff:g>‎‏‎‎‏‏‏‎ remaining, about ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎ left‎‏‎‎‏‎"</string>
@@ -73,11 +72,10 @@
     <string name="global_action_screenshot" msgid="8329831278085426283">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎‏‎‏‏‎Screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‏‎‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎Saving screenshot…‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‏‎Saving screenshot…‎‏‎‎‏‎"</string>
-    <string name="screenshot_saving_text" msgid="2545047868936087248">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‏‏‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎Screenshot is being saved‎‏‎‎‏‎"</string>
     <string name="screenshot_saved_title" msgid="5637073968117370753">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‏‏‏‎‏‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‎‏‎Screenshot saved‎‏‎‎‏‎"</string>
     <string name="screenshot_saved_text" msgid="7574667448002050363">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‏‏‏‎‎‏‎‎‏‏‏‎‏‏‎Tap to view your screenshot‎‏‎‎‏‎"</string>
-    <string name="screenshot_failed_title" msgid="9096484883063264803">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‎Couldn\'t capture screenshot‎‏‎‎‏‎"</string>
-    <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎Problem encountered while saving screenshot‎‏‎‎‏‎"</string>
+    <string name="screenshot_failed_title" msgid="7612509838919089748">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‏‎‎‎Couldn\'t save screenshot‎‏‎‎‏‎"</string>
+    <string name="screenshot_failed_to_save_unknown_text" msgid="3637758096565605541">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‏‎Try taking screenshot again‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‏‏‏‎‏‏‏‎‎Can\'t save screenshot due to limited storage space‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‏‏‏‎‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‏‏‏‎‎Taking screenshots isn\'t allowed by the app or your organization‎‏‎‎‏‎"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎USB file transfer options‎‏‎‎‏‎"</string>
@@ -348,7 +346,7 @@
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‎On at sunset‎‏‎‎‏‎"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‎‎‏‎‎Until sunrise‎‏‎‎‏‎"</string>
     <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‎‎‏‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎On at ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
-    <string name="quick_settings_night_secondary_label_until" msgid="8664820079774824618">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‏‎‎‏‏‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
+    <string name="quick_settings_secondary_label_until" msgid="2749196569462600150">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‎‏‎‏‏‎‎Until ‎‏‎‎‏‏‎<xliff:g id="TIME">%s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‎‎‏‏‎‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎NFC‎‏‎‎‏‎"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‎NFC is disabled‎‏‎‎‏‎"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‎NFC is enabled‎‏‎‎‏‎"</string>
@@ -432,6 +430,7 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‎‏‎‎‏‏‏‎ will start capturing everything that\'s displayed on your screen.‎‏‎‎‏‎"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‎‎‎Don\'t show again‎‏‎‎‏‎"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‏‎‏‎‏‎‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎Clear all‎‏‎‎‏‎"</string>
+    <string name="manage_notifications_text" msgid="8035284146227267681">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‎‎‎‎‏‏‎‎‎‎‏‎Manage notifications‎‏‎‎‏‎"</string>
     <string name="dnd_suppressing_shade_text" msgid="7986451830430707907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎Do Not disturb is hiding notifications‎‏‎‎‏‎"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎Start now‎‏‎‎‏‎"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‏‎‏‎‎‏‏‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‏‎‎No notifications‎‏‎‎‏‎"</string>
@@ -625,6 +624,7 @@
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‏‎‎‏‎‎‎‎‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ ‎‏‎‎‏‏‎<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‎‏‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‎‎notification controls‎‏‎‎‏‎"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‎‏‎‏‏‎‎‏‏‎‎‏‏‏‎‏‎notification snooze options‎‏‎‎‏‎"</string>
+    <string name="notification_menu_snooze_action" msgid="1112254519029621372">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‎‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‎Snooze‎‏‎‎‏‎"</string>
     <string name="snooze_undo" msgid="6074877317002985129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎UNDO‎‏‎‎‏‎"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‎Snoozed for ‎‏‎‎‏‏‎<xliff:g id="TIME_AMOUNT">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
@@ -811,6 +811,8 @@
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‏‏‎‏‎‎‎‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎Screenshots‎‏‎‎‏‎"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎General Messages‎‏‎‎‏‎"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‎‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‎‏‎Storage‎‏‎‎‏‎"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
+    <skip />
     <string name="instant_apps" msgid="6647570248119804907">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‏‎‏‏‎Instant Apps‎‏‎‎‏‎"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎‏‏‎‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‎‎‎Instant apps don\'t require installation.‎‏‎‎‏‎"</string>
     <string name="app_info" msgid="6856026610594615344">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‎‎‎‎App info‎‏‎‎‏‎"</string>
@@ -838,4 +840,18 @@
     <string name="slice_permission_checkbox" msgid="7986504458640562900">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‎Allow ‎‏‎‎‏‏‎<xliff:g id="APP">%1$s</xliff:g>‎‏‎‎‏‏‏‎ to show slices from any app‎‏‎‎‏‎"</string>
     <string name="slice_permission_allow" msgid="2340244901366722709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‏‏‏‏‎‎‎‎‏‎‎‏‎‏‎‏‎Allow‎‏‎‎‏‎"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎Deny‎‏‎‎‏‎"</string>
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 1a10610..287e0b4 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -33,7 +33,6 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"En cours"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notifications"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"Batterie faible"</string>
-    <string name="battery_low_title_hybrid" msgid="6268991275887381595">"Batterie faible : activez l\'économiseur de batterie"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restants"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant en fonction de votre utilisation : environ <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> – Temps restant : environ <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -73,11 +72,12 @@
     <string name="global_action_screenshot" msgid="8329831278085426283">"Capture d\'écran"</string>
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Enregistrement capture écran…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Enregistrement de la capture d\'écran…"</string>
-    <string name="screenshot_saving_text" msgid="2545047868936087248">"Enregistrement de la capture d\'écran…"</string>
     <string name="screenshot_saved_title" msgid="5637073968117370753">"Capture d\'écran enregistrée"</string>
     <string name="screenshot_saved_text" msgid="7574667448002050363">"Appuyez pour afficher votre capture d\'écran"</string>
-    <string name="screenshot_failed_title" msgid="9096484883063264803">"Impossible d\'effectuer une capture d\'écran"</string>
-    <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Erreur lors de l\'enregistrement de la capture d\'écran"</string>
+    <!-- no translation found for screenshot_failed_title (7612509838919089748) -->
+    <skip />
+    <!-- no translation found for screenshot_failed_to_save_unknown_text (3637758096565605541) -->
+    <skip />
     <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string>
     <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"Options transfert fichiers USB"</string>
@@ -275,8 +275,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"Vitrine des desserts"</string>
     <string name="start_dreams" msgid="5640361424498338327">"Économiseur d\'écran"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
-    <!-- no translation found for quick_settings_header_onboarding_text (8030309023792936283) -->
-    <skip />
+    <string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"Appuyez de manière prolongée sur les icônes pour accéder à d\'autres options"</string>
     <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Ne pas déranger"</string>
     <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Prioritaires uniquement"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Alarmes uniquement"</string>
@@ -351,7 +350,8 @@
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Activé au crépuscule"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Jusqu\'à l\'aube"</string>
     <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"Activé à <xliff:g id="TIME">%s</xliff:g>"</string>
-    <string name="quick_settings_night_secondary_label_until" msgid="8664820079774824618">"Jusqu\'à <xliff:g id="TIME">%s</xliff:g>"</string>
+    <!-- no translation found for quick_settings_secondary_label_until (2749196569462600150) -->
+    <skip />
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"La technologie NFC est désactivée"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"La technologie NFC est activée"</string>
@@ -435,6 +435,8 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va commencer à capturer tous les contenus affichés à l\'écran."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Ne plus afficher"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Tout effacer"</string>
+    <!-- no translation found for manage_notifications_text (8035284146227267681) -->
+    <skip />
     <string name="dnd_suppressing_shade_text" msgid="7986451830430707907">"Le mode Ne pas déranger masque les notifications"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Commencer"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"Aucune notification"</string>
@@ -628,6 +630,8 @@
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> : <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"paramètres des notifications"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"options de répétition des notifications"</string>
+    <!-- no translation found for notification_menu_snooze_action (1112254519029621372) -->
+    <skip />
     <string name="snooze_undo" msgid="6074877317002985129">"ANNULER"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"Répétée après <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
@@ -725,11 +729,9 @@
     <string name="right_keycode" msgid="708447961000848163">"Code de touche droit"</string>
     <string name="left_icon" msgid="3096287125959387541">"Icône gauche"</string>
     <string name="right_icon" msgid="3952104823293824311">"Icône droite"</string>
-    <!-- no translation found for drag_to_add_tiles (230586591689084925) -->
-    <skip />
+    <string name="drag_to_add_tiles" msgid="230586591689084925">"Sélectionnez et faites glisser les tuiles pour les ajouter"</string>
     <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Faites glisser les tuiles ici pour les supprimer."</string>
-    <!-- no translation found for drag_to_remove_disabled (2390968976638993382) -->
-    <skip />
+    <string name="drag_to_remove_disabled" msgid="2390968976638993382">"Au minimum six tuiles sont nécessaires"</string>
     <string name="qs_edit" msgid="2232596095725105230">"Modifier"</string>
     <string name="tuner_time" msgid="6572217313285536011">"Heure"</string>
   <string-array name="clock_options">
@@ -816,6 +818,8 @@
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"Captures d\'écran"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"Nouveaux messages"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"Espace de stockage"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
+    <skip />
     <string name="instant_apps" msgid="6647570248119804907">"Applis instantanées"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Les applis instantanées ne nécessitent pas d\'installation."</string>
     <string name="app_info" msgid="6856026610594615344">"Infos sur l\'appli"</string>
@@ -833,12 +837,9 @@
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Remplacer"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"Applications en cours d\'exécution en arrière-plan"</string>
     <string name="running_foreground_services_msg" msgid="6326247670075574355">"Appuyer pour obtenir des informations sur l\'utilisation de la batterie et des données"</string>
-    <!-- no translation found for mobile_data_disable_title (1068272097382942231) -->
-    <skip />
-    <!-- no translation found for mobile_data_disable_message (4756541658791493506) -->
-    <skip />
-    <!-- no translation found for mobile_data_disable_message_default_carrier (6078110473451946831) -->
-    <skip />
+    <string name="mobile_data_disable_title" msgid="1068272097382942231">"Désactiver les données mobiles ?"</string>
+    <string name="mobile_data_disable_message" msgid="4756541658791493506">"Vous n\'accéderez pas aux données mobiles ni à Internet via <xliff:g id="CARRIER">%s</xliff:g>. Internet ne sera disponible qu\'avec une connexion Wi-Fi."</string>
+    <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"votre opérateur"</string>
     <string name="touch_filtered_warning" msgid="8671693809204767551">"L\'application Paramètres ne peut pas valider votre réponse, car une application masque la demande d\'autorisation."</string>
     <string name="slice_permission_title" msgid="7465009437851044444">"Autoriser <xliff:g id="APP_0">%1$s</xliff:g> à afficher des éléments de <xliff:g id="APP_2">%2$s</xliff:g> ?"</string>
     <string name="slice_permission_text_1" msgid="3514586565609596523">"- Accès aux informations de <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -846,4 +847,18 @@
     <string name="slice_permission_checkbox" msgid="7986504458640562900">"Autoriser <xliff:g id="APP">%1$s</xliff:g> à afficher des éléments de n\'importe quelle application"</string>
     <string name="slice_permission_allow" msgid="2340244901366722709">"Autoriser"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Refuser"</string>
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index e37ca1c..c59492f 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -24,6 +24,8 @@
 
     <dimen name="brightness_mirror_height">96dp</dimen>
 
+    <!-- Width for the spacer, used between QS tiles. -->
+    <dimen name="qs_quick_tile_space_width">38dp</dimen>
     <dimen name="qs_tile_margin_top">2dp</dimen>
     <dimen name="qs_header_tooltip_height">24dp</dimen>
 
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 30ec2d5..a1e41f3 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -33,24 +33,22 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"ଚାଲୁଅଛି"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"ବିଜ୍ଞପ୍ତି"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"ବ୍ୟାଟେରୀ କମ୍‍ ଅଛି"</string>
-    <!-- no translation found for battery_low_title_hybrid (6268991275887381595) -->
-    <skip />
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> ବାକି ଅଛି"</string>
     <!-- no translation found for battery_low_percent_format_hybrid (6838677459286775617) -->
     <skip />
     <!-- no translation found for battery_low_percent_format_hybrid_short (9025795469949145586) -->
     <skip />
-    <!-- no translation found for battery_low_percent_format_saver_started (7879389868952879166) -->
+    <string name="battery_low_percent_format_saver_started" msgid="7879389868952879166">"<xliff:g id="PERCENTAGE">%s</xliff:g> ବାକି ଅଛି। ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅନ୍‍ ଅଛି।"</string>
+    <!-- no translation found for invalid_charger (2741987096648693172) -->
     <skip />
-    <string name="invalid_charger" msgid="4549105996740522523">"USB ଚାର୍ଜିଙ୍ଗ ସପୋର୍ଟ କରୁନାହିଁ।\nକେବଳ ଦିଆଯାଇଥିବା ଚାର୍ଜର୍‌ ହିଁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
-    <string name="invalid_charger_title" msgid="3515740382572798460">"USB ଚାର୍ଜିଙ୍ଗ ସପୋର୍ଟ କରୁନାହିଁ।"</string>
-    <string name="invalid_charger_text" msgid="5474997287953892710">"କେବଳ ଦିଆଯାଇଥିବା ଚାର୍ଜର୍‌ ହିଁ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
+    <!-- no translation found for invalid_charger_title (2836102177577255404) -->
+    <skip />
+    <!-- no translation found for invalid_charger_text (6480624964117840005) -->
+    <skip />
     <string name="battery_low_why" msgid="4553600287639198111">"ସେଟିଙ୍ଗ"</string>
-    <!-- no translation found for battery_saver_confirmation_title (2052100465684817154) -->
-    <skip />
+    <string name="battery_saver_confirmation_title" msgid="2052100465684817154">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅନ୍ କରିବେ?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7507968430447930257">"ଅନ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for battery_saver_start_action (8187820911065797519) -->
-    <skip />
+    <string name="battery_saver_start_action" msgid="8187820911065797519">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅନ୍ କରନ୍ତୁ"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"ସେଟିଙ୍ଗ"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"ୱାଇ-ଫାଇ"</string>
     <string name="status_bar_settings_auto_rotation" msgid="3790482541357798421">"ଅଟୋ-ରୋଟେଟ୍‌ ସ୍କ୍ରୀନ୍‍"</string>
@@ -60,47 +58,37 @@
     <string name="bluetooth_tethered" msgid="7094101612161133267">"ବ୍ଲୁ-ଟୁଥ୍‍ ଟିଥରିଙ୍ଗ୍"</string>
     <string name="status_bar_input_method_settings_configure_input_methods" msgid="3504292471512317827">"ଇନପୁଟ୍‍ ପଦ୍ଧତି ସେଟ୍‍ କରନ୍ତୁ"</string>
     <string name="status_bar_use_physical_keyboard" msgid="7551903084416057810">"ଫିଜିକଲ୍ କୀ’ବୋର୍ଡ୍"</string>
-    <!-- no translation found for usb_device_permission_prompt (1825685909587559679) -->
-    <skip />
-    <!-- no translation found for usb_accessory_permission_prompt (2465531696941369047) -->
-    <skip />
-    <!-- no translation found for usb_device_confirm_prompt (7440562274256843905) -->
-    <skip />
-    <!-- no translation found for usb_accessory_confirm_prompt (4333670517539993561) -->
-    <skip />
+    <string name="usb_device_permission_prompt" msgid="1825685909587559679">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ଆକ୍ସେସ୍‍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="usb_accessory_permission_prompt" msgid="2465531696941369047">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ଆକ୍ସେସ୍‍ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g>କୁ ଅନୁମତି ଦେବେ?"</string>
+    <string name="usb_device_confirm_prompt" msgid="7440562274256843905">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string>
+    <string name="usb_accessory_confirm_prompt" msgid="4333670517539993561">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> ନିୟନ୍ତ୍ରଣ କରିବାକୁ <xliff:g id="APPLICATION">%1$s</xliff:g> ଖୋଲିବେ?"</string>
     <string name="usb_accessory_uri_prompt" msgid="513450621413733343">"ଇନଷ୍ଟଲ୍‍ ହୋଇଥିବା କୌଣସି ଆପ୍‍ ଏହି USB ଆକ୍ସେସୋରୀରେ କାମ କରେନାହିଁ। ଏହି ଆକ୍ସେସୋରୀ ବିଷୟରେ <xliff:g id="URL">%1$s</xliff:g>ରେ ଅଧିକ ଜାଣନ୍ତୁ"</string>
     <string name="title_usb_accessory" msgid="4966265263465181372">"USB ଆକ୍ସେସରୀ"</string>
     <string name="label_view" msgid="6304565553218192990">"ଦେଖନ୍ତୁ"</string>
-    <!-- no translation found for always_use_device (4015357883336738417) -->
-    <skip />
-    <!-- no translation found for always_use_accessory (3257892669444535154) -->
-    <skip />
+    <string name="always_use_device" msgid="4015357883336738417">"<xliff:g id="USB_DEVICE">%2$s</xliff:g> କନେକ୍ଟ ଥିବାବେଳେ <xliff:g id="APPLICATION">%1$s</xliff:g> ସର୍ବଦା ଖୋଲନ୍ତୁ"</string>
+    <string name="always_use_accessory" msgid="3257892669444535154">"<xliff:g id="USB_ACCESSORY">%2$s</xliff:g> କନେକ୍ଟ ଥିବାବେଳେ <xliff:g id="APPLICATION">%1$s</xliff:g> ସର୍ବଦା ଖୋଲନ୍ତୁ"</string>
     <string name="usb_debugging_title" msgid="4513918393387141949">"USB ଡିବଗିଙ୍ଗ କରିବେ?"</string>
     <string name="usb_debugging_message" msgid="2220143855912376496">"କମ୍ପ୍ୟୁଟର୍‌ର RSA କୀ\' ଆଙ୍ଗୁଠି ଚିହ୍ନ ହେଉଛି:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="303335496705863070">"ସବୁବେଳେ ଏହି କମ୍ପ୍ୟୁଟର୍‌ରୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="usb_debugging_secondary_user_title" msgid="6353808721761220421">"USBରେ ଡିବଗ୍‍ କରାଯାଇପାରିବ ନାହିଁ"</string>
-    <!-- no translation found for usb_debugging_secondary_user_message (6067122453571699801) -->
-    <skip />
+    <string name="usb_debugging_secondary_user_message" msgid="6067122453571699801">"ସମ୍ପ୍ରତି ସାଇନ୍‍-ଇନ୍‍ କରିଥିବା ୟୁଜର୍‍ ଜଣକ ଏହି ଡିଭାଇସରେ USB ଡିବଗିଙ୍ଗ ଅନ୍‍ କରିପାରିବେ ନାହିଁ। ଏହି ବୈଶିଷ୍ଟ୍ୟ ବ୍ୟବହାର କରିବାକୁ, ପ୍ରାଥମିକ ୟୁଜର୍‍ରେ ସାଇନ୍‍-ଇନ୍‍ କରନ୍ତୁ।"</string>
     <string name="compat_mode_on" msgid="6623839244840638213">"ସ୍କ୍ରୀନ ଭରିବା ପାଇଁ ଜୁମ୍ କରନ୍ତୁ"</string>
     <string name="compat_mode_off" msgid="4434467572461327898">"ସ୍କ୍ରୀନ୍‌କୁ ଭରିବା ପାଇଁ ଟାଣନ୍ତୁ"</string>
     <!-- no translation found for global_action_screenshot (8329831278085426283) -->
     <skip />
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
-    <!-- no translation found for screenshot_saving_text (2545047868936087248) -->
-    <skip />
     <!-- no translation found for screenshot_saved_title (5637073968117370753) -->
     <skip />
     <!-- no translation found for screenshot_saved_text (7574667448002050363) -->
     <skip />
-    <!-- no translation found for screenshot_failed_title (9096484883063264803) -->
+    <!-- no translation found for screenshot_failed_title (7612509838919089748) -->
     <skip />
-    <!-- no translation found for screenshot_failed_to_save_unknown_text (8844781948876286488) -->
+    <!-- no translation found for screenshot_failed_to_save_unknown_text (3637758096565605541) -->
     <skip />
     <!-- no translation found for screenshot_failed_to_save_text (3041612585107107310) -->
     <skip />
-    <!-- no translation found for screenshot_failed_to_capture_text (173674476457581486) -->
-    <skip />
+    <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"ଆପ୍‍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍‍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"USB ଫାଇଲ୍‌ ଟ୍ରାନ୍ସଫର୍‌ର ବିକଳ୍ପ"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"ଏକ ମିଡିଆ ପ୍ଲେୟାର୍‍ (MTP) ଭାବରେ ଭର୍ତ୍ତି କରନ୍ତୁ"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"ଏକ କ୍ୟାମେରା (PTP) ଭାବରେ ଭର୍ତ୍ତି କରନ୍ତୁ"</string>
@@ -108,8 +96,7 @@
     <string name="accessibility_back" msgid="567011538994429120">"ଫେରନ୍ତୁ"</string>
     <string name="accessibility_home" msgid="8217216074895377641">"ହୋମ୍"</string>
     <string name="accessibility_menu" msgid="316839303324695949">"ମେନୁ"</string>
-    <!-- no translation found for accessibility_accessibility_button (7601252764577607915) -->
-    <skip />
+    <string name="accessibility_accessibility_button" msgid="7601252764577607915">"ଆକ୍ସେସିବିଲିଟୀ"</string>
     <!-- no translation found for accessibility_rotate_button (7402949513740253006) -->
     <skip />
     <string name="accessibility_recent" msgid="5208608566793607626">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀ"</string>
@@ -118,8 +105,7 @@
     <string name="accessibility_phone_button" msgid="6738112589538563574">"ଫୋନ୍‍"</string>
     <string name="accessibility_voice_assist_button" msgid="487611083884852965">"ଭଏସ୍‌ ସହାୟକ"</string>
     <string name="accessibility_unlock_button" msgid="128158454631118828">"ଅନଲକ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_waiting_for_fingerprint (4808860050517462885) -->
-    <skip />
+    <string name="accessibility_waiting_for_fingerprint" msgid="4808860050517462885">"ଆଙ୍ଗୁଠି ଚିହ୍ନ ପାଇଁ ଅପେକ୍ଷା କରିଛି"</string>
     <string name="accessibility_unlock_without_fingerprint" msgid="7541705575183694446">"ଆଙ୍ଗୁଠିଚିହ୍ନ ବ୍ୟବହାର ନକରି ଅନଲକ୍‍ କରନ୍ତୁ"</string>
     <string name="unlock_label" msgid="8779712358041029439">"ଅନଲକ୍‌"</string>
     <string name="phone_label" msgid="2320074140205331708">"ଫୋନ୍‌ ଖୋଲନ୍ତୁ"</string>
@@ -175,40 +161,48 @@
     <string name="accessibility_desc_off" msgid="6475508157786853157">"ଅଫ୍।"</string>
     <string name="accessibility_desc_connected" msgid="8366256693719499665">"ସଂଯୁକ୍ତ।"</string>
     <string name="accessibility_desc_connecting" msgid="3812924520316280149">"ସଂଯୋଗ କରୁଛି।"</string>
-    <string name="accessibility_data_connection_gprs" msgid="1606477224486747751">"GPRS"</string>
-    <string name="accessibility_data_connection_1x" msgid="994133468120244018">"1 X"</string>
-    <string name="accessibility_data_connection_hspa" msgid="2032328855462645198">"HSPA"</string>
-    <string name="accessibility_data_connection_3g" msgid="8628562305003568260">"3G"</string>
-    <string name="accessibility_data_connection_3.5g" msgid="8664845609981692001">"3.5G"</string>
-    <string name="accessibility_data_connection_4g" msgid="7741000750630089612">"4G"</string>
-    <string name="accessibility_data_connection_4g_plus" msgid="3032226872470658661">"4G+"</string>
-    <string name="accessibility_data_connection_lte" msgid="5413468808637540658">"LTE"</string>
-    <string name="accessibility_data_connection_lte_plus" msgid="361876866906946007">"LTE+"</string>
-    <string name="accessibility_data_connection_cdma" msgid="6132648193978823023">"CDMA"</string>
-    <string name="accessibility_data_connection_roaming" msgid="5977362333466556094">"ରୋମିଙ୍ଗ"</string>
-    <string name="accessibility_data_connection_edge" msgid="4477457051631979278">"ଏଜ୍‌"</string>
+    <!-- no translation found for data_connection_gprs (7652872568358508452) -->
+    <skip />
+    <!-- no translation found for data_connection_hspa (1499615426569473562) -->
+    <skip />
+    <!-- no translation found for data_connection_3g (503045449315378373) -->
+    <skip />
+    <!-- no translation found for data_connection_3_5g (5218328297191657602) -->
+    <skip />
+    <!-- no translation found for data_connection_3_5g_plus (7570783890290275297) -->
+    <skip />
+    <!-- no translation found for data_connection_4g (9139963475267449144) -->
+    <skip />
+    <!-- no translation found for data_connection_4g_plus (1148687201877800700) -->
+    <skip />
+    <!-- no translation found for data_connection_lte (2694876797724028614) -->
+    <skip />
+    <!-- no translation found for data_connection_lte_plus (3423013208570937424) -->
+    <skip />
+    <!-- no translation found for data_connection_cdma (4677985502159869585) -->
+    <skip />
+    <!-- no translation found for data_connection_roaming (6037232010953697354) -->
+    <skip />
+    <!-- no translation found for data_connection_edge (871835227939216682) -->
+    <skip />
     <string name="accessibility_data_connection_wifi" msgid="2324496756590645221">"ୱାଇ-ଫାଇ"</string>
     <string name="accessibility_no_sim" msgid="8274017118472455155">"କୌଣସି SIM ନାହିଁ।"</string>
-    <!-- no translation found for accessibility_cell_data (5326139158682385073) -->
-    <skip />
-    <!-- no translation found for accessibility_cell_data_on (5927098403452994422) -->
-    <skip />
-    <!-- no translation found for accessibility_cell_data_off (443267573897409704) -->
+    <string name="accessibility_cell_data" msgid="5326139158682385073">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
+    <string name="accessibility_cell_data_on" msgid="5927098403452994422">"ମୋବାଇଲ୍‌ ଡାଟା ଅନ୍‍"</string>
+    <!-- no translation found for cell_data_off (5287705247512911922) -->
     <skip />
     <string name="accessibility_bluetooth_tether" msgid="4102784498140271969">"ବ୍ଲୁ-ଟୁଥ୍‍ ଟିଥରିଙ୍ଗ"</string>
     <string name="accessibility_airplane_mode" msgid="834748999790763092">"ଏରୋପ୍ଲେନ୍‍ ମୋଡ୍‌।"</string>
-    <!-- no translation found for accessibility_vpn_on (5993385083262856059) -->
-    <skip />
+    <string name="accessibility_vpn_on" msgid="5993385083262856059">"VPN ଅନ୍‍।"</string>
     <string name="accessibility_no_sims" msgid="3957997018324995781">"କୌଣସି SIM କାର୍ଡ ନାହିଁ।"</string>
-    <string name="accessibility_carrier_network_change_mode" msgid="4017301580441304305">"କେରିଅର୍‍ ନେଟ୍‌ୱର୍କ ବଦଳାଯାଉଛି।"</string>
+    <!-- no translation found for carrier_network_change_mode (8149202439957837762) -->
+    <skip />
     <string name="accessibility_battery_details" msgid="7645516654955025422">"ବ୍ୟାଟେରୀ ବିବରଣୀ ଖୋଲନ୍ତୁ"</string>
     <string name="accessibility_battery_level" msgid="7451474187113371965">"ବ୍ୟାଟେରୀ <xliff:g id="NUMBER">%d</xliff:g> ଶତକଡ଼ା ଅଛି।"</string>
-    <!-- no translation found for accessibility_battery_level_charging (1147587904439319646) -->
-    <skip />
+    <string name="accessibility_battery_level_charging" msgid="1147587904439319646">"ବ୍ୟାଟେରୀ ଚାର୍ଜ ହେଉଛି, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> ଶତକଡ଼ା।"</string>
     <string name="accessibility_settings_button" msgid="799583911231893380">"ସିଷ୍ଟମ୍‍ ସେଟିଙ୍ଗ।"</string>
     <string name="accessibility_notifications_button" msgid="4498000369779421892">"ବିଜ୍ଞପ୍ତି"</string>
-    <!-- no translation found for accessibility_overflow_action (5681882033274783311) -->
-    <skip />
+    <string name="accessibility_overflow_action" msgid="5681882033274783311">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ଦେଖନ୍ତୁ"</string>
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"ବିଜ୍ଞପ୍ତି ଖାଲି କରନ୍ତୁ।"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"GPS ସକ୍ଷମ।"</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"GPS ପ୍ରାପ୍ତ କରୁଛି।"</string>
@@ -222,8 +216,7 @@
     <string name="accessibility_recents_item_will_be_dismissed" msgid="395770242498031481">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ।"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> ଖାରଜ କରିଦିଆଗଲା।"</string>
     <string name="accessibility_recents_all_items_dismissed" msgid="4464697366179168836">"ସମସ୍ତ ସମ୍ପ୍ରତି ଆପ୍ଲିକେଶନଗୁଡ଼ିକ ଖାରଜ କରାଯାଇଛି।"</string>
-    <!-- no translation found for accessibility_recents_item_open_app_info (5107479759905883540) -->
-    <skip />
+    <string name="accessibility_recents_item_open_app_info" msgid="5107479759905883540">"<xliff:g id="APP">%s</xliff:g> ଆପ୍ଲିକେଶନ୍‍ ସୂଚନା ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_recents_item_launched" msgid="7616039892382525203">"<xliff:g id="APP">%s</xliff:g> ଆରମ୍ଭ ହେଉଛି।"</string>
     <string name="accessibility_notification_dismissed" msgid="854211387186306927">"ବିଜ୍ଞପ୍ତି ଖାରଜ କରାଗଲା।"</string>
     <string name="accessibility_desc_notification_shade" msgid="4690274844447504208">"ବିଜ୍ଞପ୍ତି ଶେଡ୍‍।"</string>
@@ -231,8 +224,7 @@
     <string name="accessibility_desc_lock_screen" msgid="5625143713611759164">"ଲକ୍‌ ସ୍କ୍ରୀନ୍‌।"</string>
     <string name="accessibility_desc_settings" msgid="3417884241751434521">"ସେଟିଙ୍ଗ"</string>
     <string name="accessibility_desc_recent_apps" msgid="4876900986661819788">"ସଂକ୍ଷିପ୍ତ ବିବରଣୀ"</string>
-    <!-- no translation found for accessibility_desc_work_lock (4288774420752813383) -->
-    <skip />
+    <string name="accessibility_desc_work_lock" msgid="4288774420752813383">"ୱର୍କ ଲକ୍‍ ସ୍କ୍ରୀନ୍‍"</string>
     <string name="accessibility_desc_close" msgid="7479755364962766729">"ବନ୍ଦ କରନ୍ତୁ"</string>
     <string name="accessibility_quick_settings_wifi" msgid="5518210213118181692">"<xliff:g id="SIGNAL">%1$s</xliff:g>।"</string>
     <string name="accessibility_quick_settings_wifi_changed_off" msgid="8716484460897819400">"ୱାଇ-ଫାଇ ବନ୍ଦ ଅଛି।"</string>
@@ -243,7 +235,8 @@
     <string name="accessibility_quick_settings_airplane_on" msgid="6406141469157599296">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌ ଅନ୍ ଅଛି।"</string>
     <string name="accessibility_quick_settings_airplane_changed_off" msgid="66846307818850664">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
     <string name="accessibility_quick_settings_airplane_changed_on" msgid="8983005603505087728">"ଏୟାର୍‌ପ୍ଲେନ୍‌ ମୋଡ୍‌କୁ ଚାଲୁ କରାଯାଇଛି।"</string>
-    <string name="accessibility_quick_settings_dnd_priority_on" msgid="1448402297221249355">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍‍ ଅଛି, କେବଳ ଗୁରୁତ୍ୱର୍ଣ୍ଣ।"</string>
+    <!-- no translation found for accessibility_quick_settings_dnd_priority_on (5836205286254617194) -->
+    <skip />
     <string name="accessibility_quick_settings_dnd_none_on" msgid="6882582132662613537">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ ଅଛି, ସମ୍ପୂର୍ଣ୍ଣ ନୀରବ।"</string>
     <string name="accessibility_quick_settings_dnd_alarms_on" msgid="9152834845587554157">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍ ଅଛି, କେବଳ ଆଲାର୍ମ।"</string>
     <string name="accessibility_quick_settings_dnd" msgid="6607873236717185815">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ।"</string>
@@ -266,8 +259,7 @@
     <string name="accessibility_quick_settings_more_time" msgid="3659274935356197708">"ଅଧିକ ସମୟ।"</string>
     <string name="accessibility_quick_settings_less_time" msgid="2404728746293515623">"କମ୍ ସମୟ।"</string>
     <string name="accessibility_quick_settings_flashlight_off" msgid="4936432000069786988">"ଫ୍ଲାଶ୍‌ଲାଇଟ୍ ଅଫ୍ ଅଛି।"</string>
-    <!-- no translation found for accessibility_quick_settings_flashlight_unavailable (8012811023312280810) -->
-    <skip />
+    <string name="accessibility_quick_settings_flashlight_unavailable" msgid="8012811023312280810">"ଟର୍ଚ୍ଚ ଲାଇଟ୍‍ ଅନୁପଲବ୍ଧ।"</string>
     <string name="accessibility_quick_settings_flashlight_on" msgid="2003479320007841077">"ଫ୍ଲାଶ୍‌ଲାଇଟ୍ ଅନ୍ ଅଛି।"</string>
     <string name="accessibility_quick_settings_flashlight_changed_off" msgid="3303701786768224304">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ବନ୍ଦ ଅଛି।"</string>
     <string name="accessibility_quick_settings_flashlight_changed_on" msgid="6531793301533894686">"ଟର୍ଚ୍ଚ ଲାଇଟ୍ ଅନ୍ ଅଛି।"</string>
@@ -280,27 +272,21 @@
     <string name="accessibility_quick_settings_work_mode_on" msgid="7650588553988014341">"ୱର୍କ ମୋଡ୍‍ ଅନ୍‍।"</string>
     <string name="accessibility_quick_settings_work_mode_changed_off" msgid="5605534876107300711">"ୱର୍କ ମୋଡ୍‌କୁ ଅଫ୍‍ କରାଯାଇଛି।"</string>
     <string name="accessibility_quick_settings_work_mode_changed_on" msgid="249840330756998612">"ୱର୍କ ମୋଡ୍‌କୁ ଅନ୍‍ କରାଯାଇଛି।"</string>
-    <!-- no translation found for accessibility_quick_settings_data_saver_changed_off (650231949881093289) -->
-    <skip />
-    <!-- no translation found for accessibility_quick_settings_data_saver_changed_on (4218725402373934151) -->
-    <skip />
+    <string name="accessibility_quick_settings_data_saver_changed_off" msgid="650231949881093289">"ଡାଟା ସେଭର୍‌ ଅଫ୍‍ କରାଗଲା।"</string>
+    <string name="accessibility_quick_settings_data_saver_changed_on" msgid="4218725402373934151">"ଡାଟା ସେଭର୍‌ ଅନ୍‍ କରାଗଲା।"</string>
     <string name="accessibility_brightness" msgid="8003681285547803095">"ଡିସ୍‌ପ୍ଲେ ଉଜ୍ଜ୍ୱଳତା"</string>
-    <!-- no translation found for accessibility_ambient_display_charging (9084521679384069087) -->
-    <skip />
+    <string name="accessibility_ambient_display_charging" msgid="9084521679384069087">"ଚାର୍ଜ କରାଯାଉଛି"</string>
     <string name="data_usage_disabled_dialog_3g_title" msgid="5281770593459841889">"2G-3G ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
-    <!-- no translation found for data_usage_disabled_dialog_mobile_title (6801382439018099779) -->
-    <skip />
+    <string name="data_usage_disabled_dialog_mobile_title" msgid="6801382439018099779">"ମୋବାଇଲ୍‍ ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ଡାଟା ପଜ୍‍ କରାଯାଇଛି"</string>
-    <!-- no translation found for data_usage_disabled_dialog (4919541636934603816) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="4919541636934603816">"ଆପଣ ସେଟ୍‍ କରିଥିବା ଡାଟାର ସୀମାରେ ପହଞ୍ଚିଲେ। ଆପଣ ଆଉ ମୋବାଇଲ୍‍ ଡାଟା ବ୍ୟବହାର କରୁନାହାଁନ୍ତି।\n\nଯଦି ଆପଣ ପୁଣି ଆରମ୍ଭ କରନ୍ତି, ଡାଟା ବ୍ୟବହାର ପାଇଁ ଆପଣଙ୍କୁ ଦେୟ ଲାଗୁ କରାଯାଇ ପାରେ।"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
     <string name="gps_notification_searching_text" msgid="8574247005642736060">"GPS ଖୋଜାଯାଉଛି"</string>
     <string name="gps_notification_found_text" msgid="4619274244146446464">"GPS ଦ୍ୱାରା ଲୋକେଶନ୍ ସେଟ୍ କରାଯାଇଛି"</string>
     <string name="accessibility_location_active" msgid="2427290146138169014">"ଲୋକେଶନ୍‍ ଅନୁରୋଧ ସକ୍ରିୟ ଅଛି"</string>
     <string name="accessibility_clear_all" msgid="5235938559247164925">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ଖାଲି କରନ୍ତୁ।"</string>
-    <!-- no translation found for notification_group_overflow_indicator (1863231301642314183) -->
-    <skip />
+    <string name="notification_group_overflow_indicator" msgid="1863231301642314183">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
     <plurals name="notification_group_overflow_description" formatted="false" msgid="4579313201268495404">
       <item quantity="other">ଭିତରେ ଆଉ <xliff:g id="NUMBER_1">%s</xliff:g>ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ରହିଛି।</item>
       <item quantity="one">ଭିତରେ ଆଉ <xliff:g id="NUMBER_0">%s</xliff:g>ଟି ଅଧିକ ବିଜ୍ଞପ୍ତି ରହିଛି।</item>
@@ -316,7 +302,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"ଡେଜର୍ଟ କେସ୍‌"</string>
     <string name="start_dreams" msgid="5640361424498338327">"ସ୍କ୍ରୀନ୍‌ ସେଭର୍‌"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"ଇଥରନେଟ୍‌"</string>
-    <!-- no translation found for quick_settings_header_onboarding_text (7872508260264044734) -->
+    <!-- no translation found for quick_settings_header_onboarding_text (8030309023792936283) -->
     <skip />
     <string name="quick_settings_dnd_label" msgid="8735855737575028208">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\""</string>
     <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"କେବଳ ପ୍ରାଥମିକତା"</string>
@@ -339,8 +325,7 @@
     <string name="quick_settings_brightness_label" msgid="6968372297018755815">"ଉଜ୍ଜ୍ୱଳତା"</string>
     <string name="quick_settings_rotation_unlocked_label" msgid="7305323031808150099">"ସ୍ୱତଃ-ଘୂର୍ଣ୍ଣନ"</string>
     <string name="accessibility_quick_settings_rotation" msgid="4231661040698488779">"ସ୍କ୍ରୀନ୍‍କୁ ଅଟୋ-ରୋଟେଟ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_quick_settings_rotation_value (8187398200140760213) -->
-    <skip />
+    <string name="accessibility_quick_settings_rotation_value" msgid="8187398200140760213">"<xliff:g id="ID_1">%s</xliff:g> ମୋଡ୍‍"</string>
     <string name="quick_settings_rotation_locked_label" msgid="6359205706154282377">"ଘୂର୍ଣ୍ଣନ ଲକ୍‍ ହୋଇଛି"</string>
     <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"ପୋର୍ଟ୍ରେଟ୍"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ଲ୍ୟାଣ୍ଡସ୍କେପ୍"</string>
@@ -375,18 +360,18 @@
     <string name="quick_settings_more_settings" msgid="326112621462813682">"ଅଧିକ ସେଟିଙ୍ଗ"</string>
     <string name="quick_settings_done" msgid="3402999958839153376">"ହୋଇଗଲା"</string>
     <string name="quick_settings_connected" msgid="1722253542984847487">"ସଂଯୁକ୍ତ"</string>
-    <!-- no translation found for quick_settings_connected_battery_level (4136051440381328892) -->
-    <skip />
+    <string name="quick_settings_connected_battery_level" msgid="4136051440381328892">"କନେକ୍ଟ ରହିଛି, ବ୍ୟାଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="47623027419264404">"ସଂଯୋଗ କରୁଛି..."</string>
     <string name="quick_settings_tethering_label" msgid="7153452060448575549">"ଟିଥରିଙ୍ଗ"</string>
     <string name="quick_settings_hotspot_label" msgid="6046917934974004879">"ହଟସ୍ପଟ୍‌"</string>
     <!-- no translation found for quick_settings_hotspot_secondary_label_transient (8010579363691405477) -->
     <skip />
+    <!-- no translation found for quick_settings_hotspot_secondary_label_data_saver_enabled (5672131949987422420) -->
+    <skip />
     <!-- no translation found for quick_settings_hotspot_secondary_label_num_devices (2324635800672199428) -->
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"ବିଜ୍ଞପ୍ତି"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"ଫ୍ଲାଶ୍‍ଲାଇଟ"</string>
-    <!-- no translation found for quick_settings_cellular_detail_title (3661194685666477347) -->
-    <skip />
+    <string name="quick_settings_cellular_detail_title" msgid="3661194685666477347">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
     <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"ଡାଟାର ବ୍ୟବହାର"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"ଅବଶିଷ୍ଟ ଡାଟା"</string>
     <string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"ସୀମାଠାରୁ ଅଧିକ"</string>
@@ -402,37 +387,26 @@
     <skip />
     <!-- no translation found for quick_settings_night_secondary_label_on_at (6256314040368487637) -->
     <skip />
-    <!-- no translation found for quick_settings_night_secondary_label_until (8664820079774824618) -->
+    <!-- no translation found for quick_settings_secondary_label_until (2749196569462600150) -->
     <skip />
-    <!-- no translation found for quick_settings_nfc_label (9012153754816969325) -->
-    <skip />
-    <!-- no translation found for quick_settings_nfc_off (6883274004315134333) -->
-    <skip />
-    <!-- no translation found for quick_settings_nfc_on (6680317193676884311) -->
-    <skip />
-    <!-- no translation found for recents_empty_message (808480104164008572) -->
-    <skip />
-    <!-- no translation found for recents_empty_message_dismissed_all (2791312568666558651) -->
-    <skip />
+    <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
+    <string name="quick_settings_nfc_off" msgid="6883274004315134333">"NFC ଅକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="quick_settings_nfc_on" msgid="6680317193676884311">"NFC ସକ୍ଷମ କରାଯାଇଛି"</string>
+    <string name="recents_empty_message" msgid="808480104164008572">"କୌଣସି ସାମ୍ପ୍ରତିକ ଆଇଟମ୍ ନାହିଁ"</string>
+    <string name="recents_empty_message_dismissed_all" msgid="2791312568666558651">"ଆପଣ ସୁବୁକିଛି ଖାଲି କରିଦେଇଛନ୍ତି"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ଆପ୍ଲିକେଶନ୍‍ ସୂଚନା"</string>
     <string name="recents_lock_to_app_button_label" msgid="6942899049072506044">"ସ୍କ୍ରୀନ୍‌ ଲକ୍‌"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ସର୍ଚ୍ଚ କରନ୍ତୁ"</string>
     <string name="recents_launch_error_message" msgid="2969287838120550506">"<xliff:g id="APP">%s</xliff:g> କୁ ଆରମ୍ଭ କରାଯାଇପାରିଲା ନାହିଁ।"</string>
-    <!-- no translation found for recents_launch_disabled_message (1624523193008871793) -->
-    <skip />
-    <!-- no translation found for recents_stack_action_button_label (6593727103310426253) -->
-    <skip />
-    <!-- no translation found for recents_drag_hint_message (2649739267073203985) -->
-    <skip />
+    <string name="recents_launch_disabled_message" msgid="1624523193008871793">"<xliff:g id="APP">%s</xliff:g> ସୁରକ୍ଷିତ-ମୋଡ୍‌ରେ ଅକ୍ଷମ ଅଟେ।"</string>
+    <string name="recents_stack_action_button_label" msgid="6593727103310426253">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
+    <string name="recents_drag_hint_message" msgid="2649739267073203985">"ସ୍ପ୍ଲିଟ୍‍ ସ୍କ୍ରୀନ୍‍ ବ୍ୟବହାର କରିବା ପାଇଁ ଏଠାକୁ ଡ୍ରାଗ୍‌ କରନ୍ତୁ"</string>
     <string name="recents_multistack_add_stack_dialog_split_horizontal" msgid="8848514474543427332">"ଭୂସମାନ୍ତର ଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
     <string name="recents_multistack_add_stack_dialog_split_vertical" msgid="9075292233696180813">"ଭୂଲମ୍ବ ଭାବରେ ଭାଗ କରନ୍ତୁ"</string>
     <string name="recents_multistack_add_stack_dialog_split_custom" msgid="4177837597513701943">"କଷ୍ଟମ୍‍ କରି ଭାଗ କରନ୍ତୁ"</string>
-    <!-- no translation found for recents_accessibility_split_screen_top (9056056469282256287) -->
-    <skip />
-    <!-- no translation found for recents_accessibility_split_screen_left (8987144699630620019) -->
-    <skip />
-    <!-- no translation found for recents_accessibility_split_screen_right (275069779299592867) -->
-    <skip />
+    <string name="recents_accessibility_split_screen_top" msgid="9056056469282256287">"ସ୍କ୍ରୀନ୍‌କୁ ଉପର ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
+    <string name="recents_accessibility_split_screen_left" msgid="8987144699630620019">"ସ୍କ୍ରୀନ୍‌କୁ ବାମ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
+    <string name="recents_accessibility_split_screen_right" msgid="275069779299592867">"ସ୍କ୍ରୀନ୍‌କୁ ଡାହାଣ ଆଡ଼କୁ ଭାଗ କରନ୍ତୁ"</string>
     <string name="expanded_header_battery_charged" msgid="5945855970267657951">"ଚାର୍ଜ ହୋଇଗଲା"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"ଚାର୍ଜ କରାଯାଉଛି"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"ପୂର୍ଣ୍ଣ ଚାର୍ଜ ହେବାକୁ ଆଉ <xliff:g id="CHARGING_TIME">%s</xliff:g> ଅଛି"</string>
@@ -441,20 +415,16 @@
     <string name="description_target_search" msgid="3091587249776033139">"ସର୍ଚ୍ଚ"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ଉପରକୁ ସ୍ଲାଇଡ୍‍ କରନ୍ତୁ।"</string>
     <string name="description_direction_left" msgid="7207478719805562165">"<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ପାଇଁ ବାମକୁ ସ୍ଲାଇଡ୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for zen_priority_introduction (1149025108714420281) -->
-    <skip />
-    <!-- no translation found for zen_alarms_introduction (4934328096749380201) -->
-    <skip />
+    <string name="zen_priority_introduction" msgid="1149025108714420281">"ଆଲାର୍ମ, ରିମାଇଣ୍ଡର୍‌, ଇଭେଣ୍ଟ ଏବଂ ଆପଣ ନିର୍ଦ୍ଦିଷ୍ଟ କରିଥିବା କଲର୍‌ଙ୍କ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
+    <string name="zen_alarms_introduction" msgid="4934328096749380201">"ଆଲାର୍ମ ବ୍ୟତୀତ ଆପଣଙ୍କ ଧ୍ୟାନ ଅନ୍ୟ କୌଣସି ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ରେ ଆକର୍ଷଣ କରାଯିବନାହିଁ। ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ନିଜେ ଚଲାଇବାକୁ ବାଛିଥିବା ଅନ୍ୟ ସବୁକିଛି ଆପଣ ଶୁଣିପାରିବେ।"</string>
     <string name="zen_priority_customize_button" msgid="7948043278226955063">"କଷ୍ଟମାଇଜ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for zen_silence_introduction_voice (3948778066295728085) -->
-    <skip />
+    <string name="zen_silence_introduction_voice" msgid="3948778066295728085">"ଆଲାର୍ମ, ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଏବଂ ଗେମ୍‌ ସମେତ ଏହା ଦ୍ୱାରା ସମସ୍ତ ଧ୍ୱନୀ ଏବଂ ଭାଇବ୍ରେଶନ୍‌ ଅବରୋଧ ହୁଏ। ଆପଣ ତଥାପି ଫୋନ୍‍ କଲ୍‍ କରିପାରିବେ।"</string>
     <string name="zen_silence_introduction" msgid="3137882381093271568">"ଆଲାର୍ମ, ମ୍ୟୁଜିକ୍‍, ଭିଡିଓ ଓ ଗେମ୍ସ ସମେତ ଏହାଦ୍ୱାରା ସମସ୍ତ ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଶନ୍‍ ଅବରୋଧ ହୁଏ।"</string>
     <string name="keyguard_more_overflow_text" msgid="9195222469041601365">"+<xliff:g id="NUMBER_OF_NOTIFICATIONS">%d</xliff:g>"</string>
     <string name="speed_bump_explanation" msgid="1288875699658819755">"ନିମ୍ନରେ କମ୍‍ ଜରୁରୀ ବିଜ୍ଞପ୍ତି"</string>
     <string name="notification_tap_again" msgid="7590196980943943842">"ଖୋଲିବା ପାଇଁ ପୁଣି ଟାପ୍‍ କରନ୍ତୁ"</string>
     <string name="keyguard_unlock" msgid="8043466894212841998">"ଅନଲକ୍‌ କରିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍‌ କରନ୍ତୁ"</string>
-    <!-- no translation found for do_disclosure_generic (5615898451805157556) -->
-    <skip />
+    <string name="do_disclosure_generic" msgid="5615898451805157556">"ଏହି ଡିଭାଇସ୍‌ ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳିତ।"</string>
     <string name="do_disclosure_with_name" msgid="5640615509915445501">"ଏହି ଡିଭାଇସ୍‌ <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ହେଉଛି"</string>
     <string name="phone_hint" msgid="4872890986869209950">"ଫୋନ୍‍ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
     <string name="voice_hint" msgid="8939888732119726665">"ଭଏସ୍‍ ସହାୟକ ପାଇଁ ଆଇକନରୁ ସ୍ୱାଇପ୍‍ କରନ୍ତୁ"</string>
@@ -499,135 +469,94 @@
     <string name="user_remove_user_title" msgid="4681256956076895559">"ୟୁଜରଙ୍କୁ ବାହାର କରିବେ?"</string>
     <string name="user_remove_user_message" msgid="1453218013959498039">"ଏହି ୟୁଜରଙ୍କ ସମସ୍ତ ଆପ୍‍ ଓ ଡାଟା ଡିଲିଟ୍‍ ହେବ।"</string>
     <string name="user_remove_user_remove" msgid="7479275741742178297">"ବାହାର କରନ୍ତୁ"</string>
-    <!-- no translation found for battery_saver_notification_title (8614079794522291840) -->
-    <skip />
+    <string name="battery_saver_notification_title" msgid="8614079794522291840">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅନ୍‌ ଅଛି"</string>
     <string name="battery_saver_notification_text" msgid="820318788126672692">"କାର୍ଯ୍ୟ ସମ୍ପାଦନ ଓ ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡ ଡାଟା କମ୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for battery_saver_notification_action_text (132118784269455533) -->
-    <skip />
+    <string name="battery_saver_notification_action_text" msgid="132118784269455533">"ବ୍ୟାଟେରୀ ସେଭର୍‌ ଅଫ୍‍ କରନ୍ତୁ"</string>
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ଆପଣଙ୍କ ସ୍କ୍ରୀନ୍‌ରେ ପ୍ରଦର୍ଶିତ ହେଉଥିବା ସମସ୍ତ ବସ୍ତୁକୁ କ୍ୟାପଚର୍ କରିବା ଆରମ୍ଭ ହୋଇଯିବ।"</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"ସମସ୍ତ ଖାଲି କରନ୍ତୁ"</string>
+    <!-- no translation found for manage_notifications_text (8035284146227267681) -->
+    <skip />
+    <!-- no translation found for dnd_suppressing_shade_text (7986451830430707907) -->
+    <skip />
     <string name="media_projection_action_text" msgid="8470872969457985954">"ବର୍ତ୍ତମାନ ଆରମ୍ଭ କରନ୍ତୁ"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"କୌଣସି ବିଜ୍ଞପ୍ତି ନାହିଁ"</string>
     <string name="profile_owned_footer" msgid="8021888108553696069">"ପ୍ରୋଫାଇଲ୍ ନିରୀକ୍ଷଣ କରାଯାଇପାରେ।"</string>
     <string name="vpn_footer" msgid="2388611096129106812">"ନେଟ୍‌ୱର୍କ ନୀରିକ୍ଷଣ କରାଯାଇପାରେ"</string>
     <string name="branded_vpn_footer" msgid="2168111859226496230">"ନେଟ୍‌ୱର୍କକୁ ନିରୀକ୍ଷଣ କରାଯାଇପାରେ"</string>
-    <!-- no translation found for quick_settings_disclosure_management_monitoring (6645176135063957394) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_management_monitoring (370622174777570853) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_management_named_vpn (1085137869053332307) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_management_named_vpn (6290456493852584017) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_management (3294967280853150271) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_management (1059403025094542908) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_management_vpns (3698767349925266482) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_management_vpns (7777821385318891527) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_managed_profile_monitoring (5125463987558278215) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_managed_profile_monitoring (8973606847896650284) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_monitoring (679658227269205728) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_vpns (8170318392053156330) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_managed_profile_named_vpn (3494535754792751741) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_personal_profile_named_vpn (4467456202486569906) -->
-    <skip />
-    <!-- no translation found for quick_settings_disclosure_named_vpn (6943724064780847080) -->
-    <skip />
-    <!-- no translation found for monitoring_title_device_owned (1652495295941959815) -->
-    <skip />
+    <string name="quick_settings_disclosure_management_monitoring" msgid="6645176135063957394">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରନ୍ତି ଏବଂ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରିପାରନ୍ତି"</string>
+    <string name="quick_settings_disclosure_named_management_monitoring" msgid="370622174777570853">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରନ୍ତି ଏବଂ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରନ୍ତି"</string>
+    <string name="quick_settings_disclosure_management_named_vpn" msgid="1085137869053332307">"ଡିଭାଇସ୍‌ ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳିତ ଏବଂ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_named_management_named_vpn" msgid="6290456493852584017">"ଡିଭାଇସ୍‌ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ଏବଂ <xliff:g id="VPN_APP">%2$s</xliff:g>ରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_management" msgid="3294967280853150271">"ଡିଭାଇସ୍‌ ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳିତ"</string>
+    <string name="quick_settings_disclosure_named_management" msgid="1059403025094542908">"ଡିଭାଇସ୍‍ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ହେଉଛି"</string>
+    <string name="quick_settings_disclosure_management_vpns" msgid="3698767349925266482">"ଡିଭାଇସ୍‌ ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳିତ ଏବଂ VPNଗୁଡ଼ିକରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_named_management_vpns" msgid="7777821385318891527">"ଡିଭାଇସ୍‌ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ଏବଂ VPNଗୁଡ଼ିକରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="5125463987558278215">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରିପାରନ୍ତି"</string>
+    <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8973606847896650284">"<xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରିପାରନ୍ତି"</string>
+    <string name="quick_settings_disclosure_monitoring" msgid="679658227269205728">"ନେଟୱର୍କ ନୀରିକ୍ଷଣ କରାଯାଇପାରେ"</string>
+    <string name="quick_settings_disclosure_vpns" msgid="8170318392053156330">"ଡିଭାଇସ୍‍ VPNଗୁଡ଼ିକରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="3494535754792751741">"ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_personal_profile_named_vpn" msgid="4467456202486569906">"ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ"</string>
+    <string name="quick_settings_disclosure_named_vpn" msgid="6943724064780847080">"ଡିଭାଇସ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ"</string>
+    <string name="monitoring_title_device_owned" msgid="1652495295941959815">"ଡିଭାଇସ୍‌ ପରିଚାଳନା"</string>
     <string name="monitoring_title_profile_owned" msgid="6790109874733501487">"ପ୍ରୋଫାଇଲ୍ ନୀରିକ୍ଷଣ"</string>
     <string name="monitoring_title" msgid="169206259253048106">"ନେଟ୍‌ୱର୍କ ନୀରିକ୍ଷଣ"</string>
-    <!-- no translation found for monitoring_subtitle_vpn (876537538087857300) -->
-    <skip />
-    <!-- no translation found for monitoring_subtitle_network_logging (3341264304793193386) -->
-    <skip />
-    <!-- no translation found for monitoring_subtitle_ca_certificate (3874151893894355988) -->
-    <skip />
+    <string name="monitoring_subtitle_vpn" msgid="876537538087857300">"VPN"</string>
+    <string name="monitoring_subtitle_network_logging" msgid="3341264304793193386">"ନେଟୱର୍କ ଲଗିଙ୍ଗ"</string>
+    <string name="monitoring_subtitle_ca_certificate" msgid="3874151893894355988">"CA ସର୍ଟିଫିକେଟ୍‌"</string>
     <string name="disable_vpn" msgid="4435534311510272506">"VPN ଅକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="disconnect_vpn" msgid="1324915059568548655">"VPN ବିଛିନ୍ନ କରନ୍ତୁ"</string>
-    <!-- no translation found for monitoring_button_view_policies (100913612638514424) -->
-    <skip />
-    <!-- no translation found for monitoring_description_named_management (5281789135578986303) -->
-    <skip />
-    <!-- no translation found for monitoring_description_management (4573721970278370790) -->
-    <skip />
-    <!-- no translation found for monitoring_description_management_ca_certificate (5202023784131001751) -->
-    <skip />
-    <!-- no translation found for monitoring_description_managed_profile_ca_certificate (4683248196789897964) -->
-    <skip />
-    <!-- no translation found for monitoring_description_ca_certificate (7886985418413598352) -->
-    <skip />
-    <!-- no translation found for monitoring_description_management_network_logging (7184005419733060736) -->
-    <skip />
-    <!-- no translation found for monitoring_description_named_vpn (7403457334088909254) -->
-    <skip />
-    <!-- no translation found for monitoring_description_two_named_vpns (4198511413729213802) -->
-    <skip />
-    <!-- no translation found for monitoring_description_managed_profile_named_vpn (1427905889862420559) -->
-    <skip />
-    <!-- no translation found for monitoring_description_personal_profile_named_vpn (3133980926929069283) -->
-    <skip />
+    <string name="monitoring_button_view_policies" msgid="100913612638514424">"ପଲିସୀ ଦେଖନ୍ତୁ"</string>
+    <string name="monitoring_description_named_management" msgid="5281789135578986303">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳନା କରାଯାଉଛି।.\n\nଆପଣଙ୍କ ଆଡମିନ୍‍ ସେଟିଙ୍ଗ, କର୍ପୋରେଟ୍‍ ଆକ୍ସେସ୍‍, ଆପ୍‍, ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ସମ୍ବନ୍ଧୀୟ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେଶନ୍‍ ନିରୀକ୍ଷଣ ଓ ପରିଚାଳନା କରିପାରନ୍ତି।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ନିଜ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
+    <string name="monitoring_description_management" msgid="4573721970278370790">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ଆପଣଙ୍କ ସଂସ୍ଥା ଦ୍ୱାରା ପରିଚାଳନା କରାଯାଉଛି।\n\nଆପଣଙ୍କ ଆଡମିନ୍‍ ସେଟିଙ୍ଗ, କର୍ପୋରେଟ୍‍ ଆକ୍ସେସ୍‍, ଆପ୍‍, ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ସମ୍ବନ୍ଧୀୟ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେଶନ୍‍ ନିରୀକ୍ଷଣ ଓ ପରିଚାଳନା କରିପାରନ୍ତି।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ନିଜ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
+    <string name="monitoring_description_management_ca_certificate" msgid="5202023784131001751">"ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
+    <string name="monitoring_description_managed_profile_ca_certificate" msgid="4683248196789897964">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲରେ ଆପଣଙ୍କ ସଂସ୍ଥା ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରିଛନ୍ତି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
+    <string name="monitoring_description_ca_certificate" msgid="7886985418413598352">"ଏହି ଡିଭାଇସରେ ଏକ ସର୍ଟିଫିକେଟ୍‍ ଅଥରିଟି ଇନଷ୍ଟଲ୍‍ କରାଯାଇଛି। ଆପଣଙ୍କ ସୁରକ୍ଷିତ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କିମ୍ବା ସଂଶୋଧନ କରାଯାଇ ପାରେ।"</string>
+    <string name="monitoring_description_management_network_logging" msgid="7184005419733060736">"ଆପଣଙ୍କ ଆଡମିନ୍‍ ନେଟୱର୍କ ଲଗଇନ୍‍ କରିବା ଅନ୍‍ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରେ।"</string>
+    <string name="monitoring_description_named_vpn" msgid="7403457334088909254">"ଆପଣ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
+    <string name="monitoring_description_two_named_vpns" msgid="4198511413729213802">"ଆପଣ <xliff:g id="VPN_APP_0">%1$s</xliff:g> ଏବଂ <xliff:g id="VPN_APP_1">%2$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
+    <string name="monitoring_description_managed_profile_named_vpn" msgid="1427905889862420559">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
+    <string name="monitoring_description_personal_profile_named_vpn" msgid="3133980926929069283">"ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
     <string name="monitoring_description_do_header_generic" msgid="96588491028288691">"ଆପଣଙ୍କ ଡିଭାଇସ୍‌ <xliff:g id="DEVICE_OWNER_APP">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳିତ ହେଉଛି।"</string>
     <string name="monitoring_description_do_header_with_name" msgid="5511133708978206460">"ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ପରିଚାଳନା କରିବାକୁ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_OWNER_APP">%2$s</xliff:g> ବ୍ୟବହାର କରନ୍ତି।"</string>
-    <!-- no translation found for monitoring_description_do_body (3639594537660975895) -->
-    <skip />
+    <string name="monitoring_description_do_body" msgid="3639594537660975895">"ଆପଣଙ୍କ ଆଡମିନ୍‍ ସେଟିଙ୍ଗ, କର୍ପୋରେଟ୍‍ ଆକ୍ସେସ୍‍, ଆପ୍‍, ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ସମ୍ବନ୍ଧୀୟ ଡାଟା ଏବଂ ଆପଣଙ୍କ ଡିଭାଇସର ଲୋକେଶନ୍‍ ନିରୀକ୍ଷଣ ଓ ପରିଚାଳନା କରିପାରନ୍ତି।"</string>
     <string name="monitoring_description_do_learn_more_separator" msgid="3785251953067436862">" "</string>
     <string name="monitoring_description_do_learn_more" msgid="1849514470437907421">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
     <string name="monitoring_description_do_body_vpn" msgid="8255218762488901796">"ଆପଣ <xliff:g id="VPN_APP">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ ଅଛନ୍ତି, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟ୍‌ୱର୍କର ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
-    <!-- no translation found for monitoring_description_vpn_settings_separator (1933186756733474388) -->
-    <skip />
-    <!-- no translation found for monitoring_description_vpn_settings (6434859242636063861) -->
-    <skip />
-    <!-- no translation found for monitoring_description_ca_cert_settings_separator (4987350385906393626) -->
-    <skip />
-    <!-- no translation found for monitoring_description_ca_cert_settings (5489969458872997092) -->
-    <skip />
-    <!-- no translation found for monitoring_description_network_logging (7223505523384076027) -->
-    <skip />
+    <string name="monitoring_description_vpn_settings_separator" msgid="1933186756733474388">" "</string>
+    <string name="monitoring_description_vpn_settings" msgid="6434859242636063861">"VPN ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ"</string>
+    <string name="monitoring_description_ca_cert_settings_separator" msgid="4987350385906393626">" "</string>
+    <string name="monitoring_description_ca_cert_settings" msgid="5489969458872997092">"ମୁକ୍ତ ବିଶ୍ୱସ୍ତ କ୍ରୀଡେନଶିଆଲ୍‍"</string>
+    <string name="monitoring_description_network_logging" msgid="7223505523384076027">"ଆପଣଙ୍କ ଆଡମିନ୍‍ ନେଟ୍‌ୱର୍କ ଲଗଇନ୍‍ କରିବା ଅନ୍‍ କରିଛନ୍ତି, ଯାହା ଆପଣଙ୍କ ଡିଭାଇସରେ ଟ୍ରାଫିକ୍‍ ନିରୀକ୍ଷଣ କରେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ନିଜ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
     <string name="monitoring_description_vpn" msgid="4445150119515393526">"ଏକ VPN ସଂଯୋଗ ସେଟ୍‍ ଅପ୍‍ କରିବା ପାଇଁ ଆପଣ ଗୋଟିଏ ଆପକୁ ଅନୁମତି ଦେଲେ।\n\nଏହି ଆପ୍‍ ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ଡିଭାଇସ୍‍ ଓ ନେଟ୍‌ୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
-    <!-- no translation found for monitoring_description_vpn_profile_owned (2958019119161161530) -->
-    <skip />
+    <string name="monitoring_description_vpn_profile_owned" msgid="2958019119161161530">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳନା କରାଯାଉଛି।\n\nଆପଣଙ୍କ ଆଡମିନ୍‍ ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରିବେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ନିଜ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।.\n\nଆପଣ ଏକ VPNରେ ମଧ୍ୟ ସଂଯୁକ୍ତ, ଯାହା ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
     <string name="legacy_vpn_name" msgid="6604123105765737830">"VPN"</string>
-    <!-- no translation found for monitoring_description_app (1828472472674709532) -->
-    <skip />
+    <string name="monitoring_description_app" msgid="1828472472674709532">"ଆପଣ <xliff:g id="APPLICATION">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
     <string name="monitoring_description_app_personal" msgid="484599052118316268">"ଆପଣ <xliff:g id="APPLICATION">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟ୍‌ୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
     <string name="branded_monitoring_description_app_personal" msgid="2669518213949202599">"ଆପଣ <xliff:g id="APPLICATION">%1$s</xliff:g>ରେ ସଂଯୁକ୍ତ ଅଛନ୍ତି, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟ୍‌ୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
-    <!-- no translation found for monitoring_description_app_work (4612997849787922906) -->
-    <skip />
-    <!-- no translation found for monitoring_description_app_personal_work (5664165460056859391) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_trust_granted (4985003749105182372) -->
-    <skip />
-    <!-- no translation found for keyguard_indication_trust_managed (8319646760022357585) -->
-    <skip />
+    <string name="monitoring_description_app_work" msgid="4612997849787922906">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳନା କରାଯାଉଛି। ପ୍ରୋଫାଇଲଟି <xliff:g id="APPLICATION">%2$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।\n\nଅଧିକ ସୂଚନା ପାଇଁ, ନିଜ ଆଡମିନଙ୍କ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ।"</string>
+    <string name="monitoring_description_app_personal_work" msgid="5664165460056859391">"ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ <xliff:g id="ORGANIZATION">%1$s</xliff:g> ଦ୍ୱାରା ପରିଚାଳନା କରାଯାଉଛି। ପ୍ରୋଫାଇଲଟି <xliff:g id="APPLICATION_WORK">%2$s</xliff:g> ସହ ସଂଯୁକ୍ତ ଅଛି, ଯାହା ଇମେଲ୍‍, ଆପ୍‌ ଓ ୱେବସାଇଟ୍‍ ସମେତ ଆପଣଙ୍କ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।\n\nଆପଣ <xliff:g id="APPLICATION_PERSONAL">%3$s</xliff:g>ରେ ମଧ୍ୟ ସଂଯୁକ୍ତ, ଯାହା ଆପଣଙ୍କ ବ୍ୟକ୍ତିଗତ ନେଟୱର୍କ ଗତିବିଧିକୁ ନିରୀକ୍ଷଣ କରିପାରେ।"</string>
+    <string name="keyguard_indication_trust_granted" msgid="4985003749105182372">"<xliff:g id="USER_NAME">%1$s</xliff:g>ଙ୍କ ପାଇଁ ଅନଲକ୍‍ କରାଯାଇଛି"</string>
+    <string name="keyguard_indication_trust_managed" msgid="8319646760022357585">"<xliff:g id="TRUST_AGENT">%1$s</xliff:g> ଚାଲୁଛି"</string>
     <string name="keyguard_indication_trust_disabled" msgid="7412534203633528135">"ଯେତେବେଳ ପର୍ଯ୍ୟନ୍ତ ଆପଣ ମାନୁଆଲୀ ଅନଲକ୍‌ କରିନାହାନ୍ତି, ସେତେବେଳ ପର୍ଯ୍ୟନ୍ତ ଡିଭାଇସ୍‌ ଲକ୍‌ ରହିବ"</string>
     <string name="hidden_notifications_title" msgid="7139628534207443290">"ବିଜ୍ଞପ୍ତିକୁ ଶୀଘ୍ର ପ୍ରାପ୍ତ କରନ୍ତୁ"</string>
     <string name="hidden_notifications_text" msgid="2326409389088668981">"ଅନଲକ୍‌ କରିବା ପୁର୍ବରୁ ସେମାନଙ୍କୁ ଦେଖନ୍ତୁ"</string>
     <string name="hidden_notifications_cancel" msgid="3690709735122344913">"ନାହିଁ, ଥାଉ"</string>
     <string name="hidden_notifications_setup" msgid="41079514801976810">"ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
     <string name="zen_mode_and_condition" msgid="4462471036429759903">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
-    <!-- no translation found for volume_zen_end_now (6930243045593601084) -->
+    <string name="volume_zen_end_now" msgid="6930243045593601084">"ବର୍ତ୍ତମାନ ଅଫ୍‍ କରନ୍ତୁ"</string>
+    <!-- no translation found for accessibility_volume_settings (4915364006817819212) -->
     <skip />
     <string name="accessibility_volume_expand" msgid="5946812790999244205">"ବଢ଼ାନ୍ତୁ"</string>
     <string name="accessibility_volume_collapse" msgid="3609549593031810875">"ଛୋଟ କରନ୍ତୁ"</string>
     <!-- no translation found for accessibility_output_chooser (8185317493017988680) -->
     <skip />
     <string name="screen_pinning_title" msgid="3273740381976175811">"ସ୍କ୍ରିନକୁ ପିନ୍‌ କରାଯାଇଛି"</string>
-    <!-- no translation found for screen_pinning_description (8909878447196419623) -->
-    <skip />
+    <string name="screen_pinning_description" msgid="8909878447196419623">"ଆପଣ ଅନପିନ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଦେଖାଉଥିବ। ଅନପିନ୍‍ କରିବାକୁ ସ୍ପର୍ଶ କରି ଧରିରଖନ୍ତୁ ଓ ଦେଖନ୍ତୁ।"</string>
     <!-- no translation found for screen_pinning_description_recents_invisible (8281145542163727971) -->
     <skip />
-    <!-- no translation found for screen_pinning_description_accessible (426190689254018656) -->
-    <skip />
+    <string name="screen_pinning_description_accessible" msgid="426190689254018656">"ଆପଣ ଅନପିନ୍‍ ନକରିବା ପର୍ଯ୍ୟନ୍ତ ଏହା ଦେଖାଉଥିବ। ଅନପିନ୍‍ କରିବାକୁ ସ୍ପର୍ଶ କରନ୍ତୁ ଏବଂ ଓଭରଭ୍ୟୁକୁ ଧରିରଖନ୍ତୁ।"</string>
     <!-- no translation found for screen_pinning_description_recents_invisible_accessible (6134833683151189507) -->
     <skip />
     <!-- no translation found for screen_pinning_toast (2266705122951934150) -->
@@ -646,67 +575,40 @@
     <string name="managed_profile_foreground_toast" msgid="5421487114739245972">"ଆପଣ ନିଜ ୱର୍କ ପ୍ରୋଫାଇଲ୍‌ ବ୍ୟବହାର କରୁଛନ୍ତି"</string>
     <string name="stream_voice_call" msgid="4410002696470423714">"କଲ୍ କରନ୍ତୁ"</string>
     <string name="stream_system" msgid="7493299064422163147">"ସିଷ୍ଟମ୍‌"</string>
-    <!-- no translation found for stream_ring (8213049469184048338) -->
-    <skip />
-    <!-- no translation found for stream_music (9086982948697544342) -->
-    <skip />
+    <string name="stream_ring" msgid="8213049469184048338">"ରିଙ୍ଗ"</string>
+    <string name="stream_music" msgid="9086982948697544342">"ମିଡିଆ"</string>
     <string name="stream_alarm" msgid="5209444229227197703">"ଆଲାର୍ମ"</string>
-    <!-- no translation found for stream_notification (2563720670905665031) -->
-    <skip />
-    <!-- no translation found for stream_bluetooth_sco (2055645746402746292) -->
-    <skip />
-    <!-- no translation found for stream_dtmf (2447177903892477915) -->
-    <skip />
-    <!-- no translation found for stream_accessibility (301136219144385106) -->
-    <skip />
-    <!-- no translation found for ring_toggle_title (3281244519428819576) -->
-    <skip />
-    <!-- no translation found for volume_ringer_status_normal (4273142424125855384) -->
-    <skip />
-    <!-- no translation found for volume_ringer_status_vibrate (1825615171021346557) -->
-    <skip />
-    <!-- no translation found for volume_ringer_status_silent (6896394161022916369) -->
-    <skip />
-    <!-- no translation found for volume_stream_content_description_unmute (4436631538779230857) -->
-    <skip />
-    <!-- no translation found for volume_stream_content_description_vibrate (1187944970457807498) -->
-    <skip />
-    <!-- no translation found for volume_stream_content_description_mute (3625049841390467354) -->
-    <skip />
-    <!-- no translation found for volume_stream_content_description_vibrate_a11y (6427727603978431301) -->
-    <skip />
-    <!-- no translation found for volume_stream_content_description_mute_a11y (8995013018414535494) -->
-    <skip />
+    <string name="stream_notification" msgid="2563720670905665031">"ବିଜ୍ଞପ୍ତି"</string>
+    <string name="stream_bluetooth_sco" msgid="2055645746402746292">"ବ୍ଲୁ-ଟୁଥ୍‌"</string>
+    <string name="stream_dtmf" msgid="2447177903892477915">"ଡୁଆଲ୍‍ ମଲ୍ଟି ଟୋନ୍‍ ଫ୍ରିକ୍ୱେନ୍ସୀ"</string>
+    <string name="stream_accessibility" msgid="301136219144385106">"ଆକ୍ସେସିବିଲିଟୀ"</string>
+    <string name="ring_toggle_title" msgid="3281244519428819576">"କଲ୍‌"</string>
+    <string name="volume_ringer_status_normal" msgid="4273142424125855384">"ରିଙ୍ଗ"</string>
+    <string name="volume_ringer_status_vibrate" msgid="1825615171021346557">"ଭାଇବ୍ରେଟ୍‌"</string>
+    <string name="volume_ringer_status_silent" msgid="6896394161022916369">"ମ୍ୟୁଟ୍"</string>
+    <string name="volume_stream_content_description_unmute" msgid="4436631538779230857">"%1$s। ଅନମ୍ୟୁଟ୍‍ କରିବା ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="volume_stream_content_description_vibrate" msgid="1187944970457807498">"%1$s। ଭାଇବ୍ରେଟ୍‍ ସେଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
+    <string name="volume_stream_content_description_mute" msgid="3625049841390467354">"%1$s। ମ୍ୟୁଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ। ଆକ୍ସେସିବିଲିଟୀ ସର୍ଭିସ୍‌ ମ୍ୟୁଟ୍‍ କରାଯାଇପାରେ।"</string>
+    <string name="volume_stream_content_description_vibrate_a11y" msgid="6427727603978431301">"%1$s। ଭାଇବ୍ରେଟରେ ସେଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="volume_stream_content_description_mute_a11y" msgid="8995013018414535494">"%1$s। ମ୍ୟୁଟ୍‍ କରିବାକୁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
     <!-- no translation found for volume_dialog_title (7272969888820035876) -->
     <skip />
-    <!-- no translation found for volume_dialog_ringer_guidance_vibrate (8902050240801159042) -->
-    <skip />
-    <!-- no translation found for volume_dialog_ringer_guidance_silent (2128975224280276122) -->
-    <skip />
     <!-- no translation found for volume_dialog_ringer_guidance_ring (6144469689490528338) -->
     <skip />
-    <!-- no translation found for output_title (5355078100792942802) -->
-    <skip />
-    <!-- no translation found for output_calls_title (8717692905017206161) -->
-    <skip />
-    <!-- no translation found for output_none_found (5544982839808921091) -->
-    <skip />
-    <!-- no translation found for output_none_found_service_off (8631969668659757069) -->
-    <skip />
-    <!-- no translation found for output_service_bt (6224213415445509542) -->
-    <skip />
-    <!-- no translation found for output_service_wifi (3749735218931825054) -->
-    <skip />
-    <!-- no translation found for output_service_bt_wifi (4486837869988770896) -->
-    <skip />
+    <string name="output_title" msgid="5355078100792942802">"ମିଡିଆ ଆଉଟପୁଟ୍‍"</string>
+    <string name="output_calls_title" msgid="8717692905017206161">"ଫୋନ୍‍ କଲ୍‍ ଆଉଟପୁଟ୍‍"</string>
+    <string name="output_none_found" msgid="5544982839808921091">"କୌଣସି ଡିଭାଇସ୍ ମିଳିଲା ନାହିଁ"</string>
+    <string name="output_none_found_service_off" msgid="8631969668659757069">"କୌଣସି ଡିଭାଇସ୍ ମିଳିଲା ନାହିଁ। <xliff:g id="SERVICE">%1$s</xliff:g> ଅନ୍‍ କରି ଦେଖନ୍ତୁ"</string>
+    <string name="output_service_bt" msgid="6224213415445509542">"ବ୍ଲୁ-ଟୁଥ୍‌"</string>
+    <string name="output_service_wifi" msgid="3749735218931825054">"ୱାଇ-ଫାଇ"</string>
+    <string name="output_service_bt_wifi" msgid="4486837869988770896">"ବ୍ଲୁ-ଟୁଥ୍‌ ଓ ୱାଇ-ଫାଇ"</string>
     <string name="system_ui_tuner" msgid="708224127392452018">"ସିଷ୍ଟମ୍ UI ଟ୍ୟୁନର୍‍"</string>
     <string name="show_battery_percentage" msgid="5444136600512968798">"ଏମ୍ବେଡ୍‍ ହୋଇଥିବା ବ୍ୟାଟେରୀ ଶତକଡ଼ା ଦେଖାନ୍ତୁ"</string>
     <string name="show_battery_percentage_summary" msgid="3215025775576786037">"ଚାର୍ଜ ହେଉନଥିବାବେଳେ ଷ୍ଟାଟସ୍‍ ବାର୍‍ ଆଇକନ୍‍ ଭିତରେ ବ୍ୟାଟେରୀ ସ୍ତର ଶତକଡ଼ା ଦେଖାନ୍ତୁ"</string>
     <string name="quick_settings" msgid="10042998191725428">"ଦ୍ରୁତ ସେଟିଙ୍ଗ"</string>
     <string name="status_bar" msgid="4877645476959324760">"ଷ୍ଟାଟସ୍‍ ବାର୍‍"</string>
     <string name="overview" msgid="4018602013895926956">"ଅବଲୋକନ"</string>
-    <!-- no translation found for demo_mode (2532177350215638026) -->
-    <skip />
+    <string name="demo_mode" msgid="2532177350215638026">"ସିଷ୍ଟମ୍‌ UI ଡେମୋ ମୋଡ୍‌"</string>
     <string name="enable_demo_mode" msgid="4844205668718636518">"ଡେମୋ ମୋଡ୍ ସକ୍ଷମ କରନ୍ତୁ"</string>
     <string name="show_demo_mode" msgid="2018336697782464029">"ଡେମୋ ମୋଡ୍‍ ଦେଖାନ୍ତୁ"</string>
     <string name="status_bar_ethernet" msgid="5044290963549500128">"ଇଥରନେଟ୍‌"</string>
@@ -738,26 +640,19 @@
     <string name="enable_bluetooth_title" msgid="5027037706500635269">"ବ୍ଲୁ-ଟୁଥ୍‍ ଅନ୍‍ କରିବେ?"</string>
     <string name="enable_bluetooth_message" msgid="9106595990708985385">"ଆପଣଙ୍କ ଟାବଲେଟ୍‌ରେ କୀ’ବୋର୍ଡ ସଂଯୋଗ କରିବା ପାଇଁ ଆପଣଙ୍କୁ ପ୍ରଥମେ ବ୍ଲୁ-ଟୁଥ୍‍ ଅନ୍‍ କରିବାକୁ ହେବ।"</string>
     <string name="enable_bluetooth_confirmation_ok" msgid="6258074250948309715">"ଅନ୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for show_silently (6841966539811264192) -->
-    <skip />
-    <!-- no translation found for block (2734508760962682611) -->
-    <skip />
-    <!-- no translation found for do_not_silence (6878060322594892441) -->
-    <skip />
-    <!-- no translation found for do_not_silence_block (4070647971382232311) -->
-    <skip />
-    <!-- no translation found for tuner_full_importance_settings (3207312268609236827) -->
-    <skip />
-    <!-- no translation found for tuner_full_importance_settings_on (7545060756610299966) -->
-    <skip />
-    <!-- no translation found for tuner_full_importance_settings_off (8208165412614935229) -->
-    <skip />
-    <!-- no translation found for power_notification_controls_description (4372459941671353358) -->
-    <skip />
-    <!-- no translation found for notification_header_default_channel (7506845022070889909) -->
-    <skip />
+    <string name="show_silently" msgid="6841966539811264192">"ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ନିରବରେ ଦେଖାନ୍ତୁ"</string>
+    <string name="block" msgid="2734508760962682611">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ବ୍ଲକ୍‌ କରନ୍ତୁ"</string>
+    <string name="do_not_silence" msgid="6878060322594892441">"ନିରବ କରନ୍ତୁ ନାହିଁ"</string>
+    <string name="do_not_silence_block" msgid="4070647971382232311">"ନିରବ କିମ୍ବା ବ୍ଲକ୍‌ କରନ୍ତୁ ନାହିଁ"</string>
+    <string name="tuner_full_importance_settings" msgid="3207312268609236827">"ପାୱାର୍‍ ବିଜ୍ଞପ୍ତି କଣ୍ଟ୍ରୋଲ୍‌"</string>
+    <string name="tuner_full_importance_settings_on" msgid="7545060756610299966">"ଅନ୍"</string>
+    <string name="tuner_full_importance_settings_off" msgid="8208165412614935229">"ଅଫ୍"</string>
+    <string name="power_notification_controls_description" msgid="4372459941671353358">"ପାୱାର୍‍ ବିଜ୍ଞପ୍ତି କଣ୍ଟ୍ରୋଲ୍‌ରେ, ଆପଣ ଏକ ଆପ୍‍ ବିଜ୍ଞପ୍ତି ପାଇଁ 0 ରୁ 5 ଗୁରୁତ୍ୱ ସ୍ତର ସେଟ୍‍ କରିହେବେ। \n\n"<b>"ସ୍ତର 5"</b>" \n- ବିଜ୍ଞପ୍ତି ତାଲିକାର ଶୀର୍ଷରେ ଦେଖାନ୍ତୁ \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବାକୁ ଅନୁମତି ଦିଅନ୍ତୁ \n- ସର୍ବଦା ପିକ୍‍ କରନ୍ତୁ \n\n"<b>"ସ୍ତର 4"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- ସର୍ବଦା ପିକ୍‍ କରନ୍ତୁ \n\n"<b>"ସ୍ତର 3"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n\n"<b>"ସ୍ତର 2"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n- କଦାପି ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଟ୍‍ କରନ୍ତୁ ନାହିଁ \n\n"<b>"ସ୍ତର 1"</b>" \n- ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନରେ ବାଧା ଦେବା ବ୍ଲକ୍‌ କରନ୍ତୁ \n- କଦାପି ପିକ୍‍ କରନ୍ତୁ ନାହିଁ \n- କଦାପି ସାଉଣ୍ଡ ଓ ଭାଇବ୍ରେଟ୍‍ କରନ୍ତୁ ନାହିଁ \n- ଲକ୍‍ ସ୍କ୍ରୀନ୍‍ ଓ ଷ୍ଟାଟସ୍‍ ବାର୍‌ରୁ ଲୁଚାନ୍ତୁ \n- ବିଜ୍ଞପ୍ତି ତାଲିକାର ନିମ୍ନରେ ଦେଖାନ୍ତୁ \n\n"<b>"ସ୍ତର 0"</b>" \n- ଆପରୁ ସମସ୍ତ ବିଜ୍ଞପ୍ତି ବ୍ଲକ୍‌ କରନ୍ତୁ"</string>
+    <string name="notification_header_default_channel" msgid="7506845022070889909">"ବିଜ୍ଞପ୍ତି"</string>
     <!-- no translation found for notification_channel_disabled (344536703863700565) -->
     <skip />
+    <!-- no translation found for notification_channel_minimized (1664411570378910931) -->
+    <skip />
     <!-- no translation found for inline_blocking_helper (3055064577771478591) -->
     <skip />
     <!-- no translation found for inline_keep_showing (8945102997083836858) -->
@@ -766,124 +661,97 @@
     <skip />
     <!-- no translation found for inline_keep_button (6665940297019018232) -->
     <skip />
+    <!-- no translation found for inline_minimize_button (966233327974702195) -->
+    <skip />
     <!-- no translation found for inline_keep_showing_app (1723113469580031041) -->
     <skip />
     <!-- no translation found for notification_unblockable_desc (1037434112919403708) -->
     <skip />
-    <!-- no translation found for notification_channel_controls_opened_accessibility (6553950422055908113) -->
+    <!-- no translation found for notification_appops_camera_active (730959943016785931) -->
     <skip />
-    <!-- no translation found for notification_channel_controls_closed_accessibility (7521619812603693144) -->
+    <!-- no translation found for notification_appops_microphone_active (1546319728924580686) -->
     <skip />
-    <!-- no translation found for notification_channel_switch_accessibility (3420796005601900717) -->
+    <!-- no translation found for notification_appops_overlay_active (633813008357934729) -->
     <skip />
+    <!-- no translation found for notification_appops (1258122060887196817) -->
+    <!-- no translation found for notification_using (2211008461429037973) -->
+    <!-- no translation found for notification_appops_settings (1028328314935908050) -->
+    <skip />
+    <!-- no translation found for notification_appops_ok (602562195588819631) -->
+    <skip />
+    <string name="notification_channel_controls_opened_accessibility" msgid="6553950422055908113">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ଖୋଲା ଯାଇଛି"</string>
+    <string name="notification_channel_controls_closed_accessibility" msgid="7521619812603693144">"<xliff:g id="APP_NAME">%1$s</xliff:g> ପାଇଁ ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ ବନ୍ଦ ହୋଇଛି"</string>
+    <string name="notification_channel_switch_accessibility" msgid="3420796005601900717">"ଏହି ଚ୍ୟାନେଲରୁ ବିଜ୍ଞପ୍ତିକୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string>
     <string name="notification_more_settings" msgid="816306283396553571">"ଅଧିକ ସେଟିଙ୍ଗ"</string>
     <!-- no translation found for notification_app_settings (420348114670768449) -->
     <skip />
     <string name="notification_done" msgid="5279426047273930175">"ହୋଇଗଲା"</string>
     <!-- no translation found for inline_undo (558916737624706010) -->
     <skip />
-    <!-- no translation found for notification_menu_accessibility (2046162834248888553) -->
+    <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
+    <string name="notification_menu_gear_description" msgid="2204480013726775108">"ବିଜ୍ଞପ୍ତି ନିୟନ୍ତ୍ରଣ"</string>
+    <string name="notification_menu_snooze_description" msgid="3653669438131034525">"ବିଜ୍ଞପ୍ତି ସ୍ନୁଜ୍‍ ବିକଳ୍ପ"</string>
+    <!-- no translation found for notification_menu_snooze_action (1112254519029621372) -->
     <skip />
-    <!-- no translation found for notification_menu_gear_description (2204480013726775108) -->
-    <skip />
-    <!-- no translation found for notification_menu_snooze_description (3653669438131034525) -->
-    <skip />
-    <!-- no translation found for snooze_undo (6074877317002985129) -->
-    <skip />
-    <!-- no translation found for snoozed_for_time (2390718332980204462) -->
-    <skip />
-    <!-- no translation found for snoozeHourOptions (2124335842674413030) -->
-    <!-- no translation found for snoozeMinuteOptions (4127251700591510196) -->
-    <!-- no translation found for battery_panel_title (7944156115535366613) -->
-    <skip />
+    <string name="snooze_undo" msgid="6074877317002985129">"ପୂର୍ବାବସ୍ଥାକୁ ଫେରାଇ ଆଣନ୍ତୁ"</string>
+    <string name="snoozed_for_time" msgid="2390718332980204462">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> ପାଇଁ ସ୍ନୁଜ୍‍ କରାଗଲା"</string>
+    <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
+      <item quantity="other">%d ଘଣ୍ଟା</item>
+      <item quantity="one">%d ଘଣ୍ଟା</item>
+    </plurals>
+    <plurals name="snoozeMinuteOptions" formatted="false" msgid="4127251700591510196">
+      <item quantity="other">%d ମିନିଟ୍‍</item>
+      <item quantity="one">%d ମିନିଟ୍‍</item>
+    </plurals>
+    <string name="battery_panel_title" msgid="7944156115535366613">"ବ୍ୟାଟେରୀ ବ୍ୟବହାର"</string>
     <string name="battery_detail_charging_summary" msgid="1279095653533044008">"ଚାର୍ଜ କରାଯିବାବେଳେ ବ୍ୟାଟେରୀ ସେଭର୍‍ ଉପଲବ୍ଧ ନଥାଏ"</string>
     <string name="battery_detail_switch_title" msgid="6285872470260795421">"ବ୍ୟାଟେରୀ ସେଭର୍"</string>
     <string name="battery_detail_switch_summary" msgid="9049111149407626804">"କାର୍ଯ୍ୟଦକ୍ଷତା ଓ ବ୍ୟାକ୍‌ଗ୍ରାଉଣ୍ଡ ଡାଟା କମ୍ କରେ"</string>
-    <!-- no translation found for keyboard_key_button_template (6230056639734377300) -->
-    <skip />
-    <!-- no translation found for keyboard_key_home (2243500072071305073) -->
-    <skip />
-    <!-- no translation found for keyboard_key_back (2337450286042721351) -->
-    <skip />
-    <!-- no translation found for keyboard_key_dpad_up (5584144111755734686) -->
-    <skip />
-    <!-- no translation found for keyboard_key_dpad_down (7331518671788337815) -->
-    <skip />
-    <!-- no translation found for keyboard_key_dpad_left (1346446024676962251) -->
-    <skip />
-    <!-- no translation found for keyboard_key_dpad_right (3317323247127515341) -->
-    <skip />
-    <!-- no translation found for keyboard_key_dpad_center (2566737770049304658) -->
-    <skip />
-    <!-- no translation found for keyboard_key_tab (3871485650463164476) -->
-    <skip />
-    <!-- no translation found for keyboard_key_space (2499861316311153293) -->
-    <skip />
-    <!-- no translation found for keyboard_key_enter (5739632123216118137) -->
-    <skip />
-    <!-- no translation found for keyboard_key_backspace (1559580097512385854) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_play_pause (3861975717393887428) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_stop (2859963958595908962) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_next (1894394911630345607) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_previous (4256072387192967261) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_rewind (2654808213360820186) -->
-    <skip />
-    <!-- no translation found for keyboard_key_media_fast_forward (3849417047738200605) -->
-    <skip />
-    <!-- no translation found for keyboard_key_page_up (5654098530106845603) -->
-    <skip />
-    <!-- no translation found for keyboard_key_page_down (8720502083731906136) -->
-    <skip />
-    <!-- no translation found for keyboard_key_forward_del (1391451334716490176) -->
-    <skip />
-    <!-- no translation found for keyboard_key_move_home (2765693292069487486) -->
-    <skip />
-    <!-- no translation found for keyboard_key_move_end (5901174332047975247) -->
-    <skip />
-    <!-- no translation found for keyboard_key_insert (8530501581636082614) -->
-    <skip />
-    <!-- no translation found for keyboard_key_num_lock (5052537581246772117) -->
-    <skip />
-    <!-- no translation found for keyboard_key_numpad_template (8729216555174634026) -->
-    <skip />
+    <string name="keyboard_key_button_template" msgid="6230056639734377300">"ବଟନ୍‍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="keyboard_key_home" msgid="2243500072071305073">"ହୋମ୍‌"</string>
+    <string name="keyboard_key_back" msgid="2337450286042721351">"ଫେରନ୍ତୁ"</string>
+    <string name="keyboard_key_dpad_up" msgid="5584144111755734686">"ଉପର"</string>
+    <string name="keyboard_key_dpad_down" msgid="7331518671788337815">"ତଳ"</string>
+    <string name="keyboard_key_dpad_left" msgid="1346446024676962251">"ବାମ"</string>
+    <string name="keyboard_key_dpad_right" msgid="3317323247127515341">"ଡାହାଣ"</string>
+    <string name="keyboard_key_dpad_center" msgid="2566737770049304658">"କେନ୍ଦ୍ର"</string>
+    <string name="keyboard_key_tab" msgid="3871485650463164476">"ଟ୍ୟାବ୍"</string>
+    <string name="keyboard_key_space" msgid="2499861316311153293">"ସ୍ପେସ୍‍"</string>
+    <string name="keyboard_key_enter" msgid="5739632123216118137">"ଏଣ୍ଟର୍"</string>
+    <string name="keyboard_key_backspace" msgid="1559580097512385854">"ବ୍ୟାକସ୍ପେସ୍‍"</string>
+    <string name="keyboard_key_media_play_pause" msgid="3861975717393887428">"ପ୍ଲେ କରନ୍ତୁ/ପଜ୍‍ କରନ୍ତୁ"</string>
+    <string name="keyboard_key_media_stop" msgid="2859963958595908962">"ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="keyboard_key_media_next" msgid="1894394911630345607">"ପରବର୍ତ୍ତୀ"</string>
+    <string name="keyboard_key_media_previous" msgid="4256072387192967261">"ପୂର୍ବବର୍ତ୍ତୀ"</string>
+    <string name="keyboard_key_media_rewind" msgid="2654808213360820186">"ରିୱାଇଣ୍ଡ"</string>
+    <string name="keyboard_key_media_fast_forward" msgid="3849417047738200605">"ଫାଷ୍ଟ ଫର୍‌ୱାର୍ଡ"</string>
+    <string name="keyboard_key_page_up" msgid="5654098530106845603">"ଉପର ପୃଷ୍ଠା"</string>
+    <string name="keyboard_key_page_down" msgid="8720502083731906136">"ତଳ ପୃଷ୍ଠା"</string>
+    <string name="keyboard_key_forward_del" msgid="1391451334716490176">"ଡିଲିଟ୍‍"</string>
+    <string name="keyboard_key_move_home" msgid="2765693292069487486">"ହୋମ୍‌"</string>
+    <string name="keyboard_key_move_end" msgid="5901174332047975247">"ସମାପ୍ତ"</string>
+    <string name="keyboard_key_insert" msgid="8530501581636082614">"ଇନ୍‌ସର୍ଟ"</string>
+    <string name="keyboard_key_num_lock" msgid="5052537581246772117">"ନମ୍ବର ଲକ୍‍"</string>
+    <string name="keyboard_key_numpad_template" msgid="8729216555174634026">"ନମ୍ବରପ୍ୟାଡ୍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_shortcut_group_system" msgid="6472647649616541064">"ସିଷ୍ଟମ୍‌"</string>
     <string name="keyboard_shortcut_group_system_home" msgid="3054369431319891965">"ହୋମ୍"</string>
     <string name="keyboard_shortcut_group_system_recents" msgid="3154851905021926744">"ସମ୍ପ୍ରତି"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="2207004531216446378">"ଫେରନ୍ତୁ"</string>
-    <!-- no translation found for keyboard_shortcut_group_system_notifications (8366964080041773224) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_shortcuts_helper (4892255911160332762) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_system_switch_input (2334164096341310324) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications (9129465955073449206) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_assist (9095441910537146013) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_browser (6465985474000766533) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_contacts (2064197111278436375) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_email (6257036897441939004) -->
-    <skip />
+    <string name="keyboard_shortcut_group_system_notifications" msgid="8366964080041773224">"ବିଜ୍ଞପ୍ତି"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4892255911160332762">"କୀ\'ବୋର୍ଡ ଶର୍ଟକଟ୍"</string>
+    <string name="keyboard_shortcut_group_system_switch_input" msgid="2334164096341310324">"ଇନପୁଟ୍‌ ପଦ୍ଧତି ବଦଳାନ୍ତୁ"</string>
+    <string name="keyboard_shortcut_group_applications" msgid="9129465955073449206">"ଆପ୍ଲିକେଶନ୍‌"</string>
+    <string name="keyboard_shortcut_group_applications_assist" msgid="9095441910537146013">"ସହାୟତା"</string>
+    <string name="keyboard_shortcut_group_applications_browser" msgid="6465985474000766533">"ବ୍ରାଉଜର୍"</string>
+    <string name="keyboard_shortcut_group_applications_contacts" msgid="2064197111278436375">"ଯୋଗାଯୋଗ"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="6257036897441939004">"ଇମେଲ୍"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="638701213803242744">"SMS"</string>
-    <!-- no translation found for keyboard_shortcut_group_applications_music (4775559515850922780) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_youtube (6555453761294723317) -->
-    <skip />
-    <!-- no translation found for keyboard_shortcut_group_applications_calendar (9043614299194991263) -->
-    <skip />
-    <!-- no translation found for tuner_full_zen_title (4540823317772234308) -->
-    <skip />
-    <!-- no translation found for volume_and_do_not_disturb (3373784330208603030) -->
-    <skip />
-    <!-- no translation found for volume_dnd_silent (4363882330723050727) -->
-    <skip />
+    <string name="keyboard_shortcut_group_applications_music" msgid="4775559515850922780">"ମ୍ୟୁଜିକ୍‍"</string>
+    <string name="keyboard_shortcut_group_applications_youtube" msgid="6555453761294723317">"YouTube"</string>
+    <string name="keyboard_shortcut_group_applications_calendar" msgid="9043614299194991263">"କ୍ୟାଲେଣ୍ଡର୍"</string>
+    <string name="tuner_full_zen_title" msgid="4540823317772234308">"ଭଲ୍ୟୁମ୍‍ କଣ୍ଟ୍ରୋଲ୍‌ ସହ ଦେଖାନ୍ତୁ"</string>
+    <string name="volume_and_do_not_disturb" msgid="3373784330208603030">"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ"</string>
+    <string name="volume_dnd_silent" msgid="4363882330723050727">"ଭଲ୍ୟୁମ ବଟନ୍‍ ଶର୍ଟକଟ୍‍"</string>
     <string name="volume_up_silent" msgid="7141255269783588286">"ଭଲ୍ୟୁମ୍‍ ବଢ଼ାଇ \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\"ରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
     <string name="battery" msgid="7498329822413202973">"ବ୍ୟାଟେରୀ"</string>
     <string name="clock" msgid="7416090374234785905">"ଘଣ୍ଟା"</string>
@@ -893,232 +761,149 @@
     <string name="data_saver" msgid="5037565123367048522">"ଡାଟା ସେଭର୍‍"</string>
     <string name="accessibility_data_saver_on" msgid="8454111686783887148">"ଡାଟା ସେଭର୍‌ ଅନ୍‌ ଅଛି"</string>
     <string name="accessibility_data_saver_off" msgid="8841582529453005337">"ଡାଟା ସେଭର୍‍ ଅଫ୍ ଅଛି"</string>
-    <!-- no translation found for switch_bar_on (1142437840752794229) -->
-    <skip />
-    <!-- no translation found for switch_bar_off (8803270596930432874) -->
-    <skip />
-    <!-- no translation found for nav_bar (1993221402773877607) -->
-    <skip />
-    <!-- no translation found for nav_bar_layout (3664072994198772020) -->
-    <skip />
-    <!-- no translation found for left_nav_bar_button_type (8555981238887546528) -->
-    <skip />
-    <!-- no translation found for right_nav_bar_button_type (2481056627065649656) -->
-    <skip />
-    <!-- no translation found for nav_bar_default (8587114043070993007) -->
-    <skip />
-    <!-- no translation found for nav_bar_buttons:0 (1545641631806817203) -->
-    <!-- no translation found for nav_bar_buttons:1 (5742013440802239414) -->
+    <string name="switch_bar_on" msgid="1142437840752794229">"ଅନ୍"</string>
+    <string name="switch_bar_off" msgid="8803270596930432874">"ଅଫ୍"</string>
+    <string name="nav_bar" msgid="1993221402773877607">"ନାଭିଗେଶନ୍ ବାର୍‍"</string>
+    <string name="nav_bar_layout" msgid="3664072994198772020">"ଲେଆଉଟ୍"</string>
+    <string name="left_nav_bar_button_type" msgid="8555981238887546528">"ସମ୍ପୂର୍ଣ୍ଣ ବାମ ବଟନ୍‍ ପ୍ରକାର"</string>
+    <string name="right_nav_bar_button_type" msgid="2481056627065649656">"ସମ୍ପୂର୍ଣ୍ଣ ଡାହାଣ ବଟନ୍‍ ପ୍ରକାର"</string>
+    <string name="nav_bar_default" msgid="8587114043070993007">"(ଡିଫଲ୍ଟ)"</string>
     <!-- no translation found for nav_bar_buttons:2 (1951959982985094069) -->
-    <!-- no translation found for nav_bar_buttons:3 (8175437057325747277) -->
-    <!-- no translation found for nav_bar_layouts:0 (8077901629964902399) -->
-    <!-- no translation found for nav_bar_layouts:1 (8256205964297588988) -->
-    <!-- no translation found for nav_bar_layouts:2 (8719936228094005878) -->
-    <!-- no translation found for nav_bar_layouts:3 (586019486955594690) -->
-    <!-- no translation found for menu_ime (4998010205321292416) -->
-    <skip />
-    <!-- no translation found for save (2311877285724540644) -->
-    <skip />
+  <string-array name="nav_bar_layouts">
+    <item msgid="8077901629964902399">"ସାମାନ୍ୟ"</item>
+    <item msgid="8256205964297588988">"କମ୍ପାକ୍ଟ"</item>
+    <item msgid="8719936228094005878">"ବାମକୁ-ଆଉଜେଇବା"</item>
+    <item msgid="586019486955594690">"ଡାହାଣକୁ-ଆଉଜେଇବା"</item>
+  </string-array>
+    <string name="menu_ime" msgid="4998010205321292416">"କୀ\'ବୋର୍ଡ ବଦଳକାରୀ"</string>
+    <string name="save" msgid="2311877285724540644">"ସେଭ୍‌ କରନ୍ତୁ"</string>
     <string name="reset" msgid="2448168080964209908">"ରିସେଟ୍‍ କରନ୍ତୁ"</string>
-    <!-- no translation found for adjust_button_width (6138616087197632947) -->
+    <string name="adjust_button_width" msgid="6138616087197632947">"ବଟନ୍‌ର ମୋଟେଇ ଆଡ୍‌ଜଷ୍ଟ କରନ୍ତୁ"</string>
+    <string name="clipboard" msgid="1313879395099896312">"କ୍ଲିପ୍‌ବୋର୍ଡ"</string>
+    <string name="accessibility_key" msgid="5701989859305675896">"କଷ୍ଟମ୍‍ ନାଭିଗେଶନ୍ ବଟନ୍‍"</string>
+    <string name="left_keycode" msgid="2010948862498918135">"ବାମ କୀ\'କୋଡ୍‍"</string>
+    <string name="right_keycode" msgid="708447961000848163">"ଡାହାଣ କୀ\'କୋଡ୍‍"</string>
+    <string name="left_icon" msgid="3096287125959387541">"ବାମ ଆଇକନ୍‍"</string>
+    <string name="right_icon" msgid="3952104823293824311">"ଡାହାଣ ଆଇକନ୍"</string>
+    <!-- no translation found for drag_to_add_tiles (230586591689084925) -->
     <skip />
-    <!-- no translation found for clipboard (1313879395099896312) -->
+    <string name="drag_to_remove_tiles" msgid="3361212377437088062">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍‍ କରନ୍ତୁ"</string>
+    <!-- no translation found for drag_to_remove_disabled (2390968976638993382) -->
     <skip />
-    <!-- no translation found for accessibility_key (5701989859305675896) -->
-    <skip />
-    <!-- no translation found for left_keycode (2010948862498918135) -->
-    <skip />
-    <!-- no translation found for right_keycode (708447961000848163) -->
-    <skip />
-    <!-- no translation found for left_icon (3096287125959387541) -->
-    <skip />
-    <!-- no translation found for right_icon (3952104823293824311) -->
-    <skip />
-    <!-- no translation found for drag_to_add_tiles (7058945779098711293) -->
-    <skip />
-    <!-- no translation found for drag_to_remove_tiles (3361212377437088062) -->
-    <skip />
-    <!-- no translation found for qs_edit (2232596095725105230) -->
-    <skip />
-    <!-- no translation found for tuner_time (6572217313285536011) -->
-    <skip />
-    <!-- no translation found for clock_options:0 (5965318737560463480) -->
-    <!-- no translation found for clock_options:1 (1427801730816895300) -->
-    <!-- no translation found for clock_options:2 (3830170141562534721) -->
-    <!-- no translation found for battery_options:0 (3160236755818672034) -->
-    <!-- no translation found for battery_options:1 (2139628951880142927) -->
-    <!-- no translation found for battery_options:2 (3327323682209964956) -->
-    <!-- no translation found for other (4060683095962566764) -->
-    <skip />
-    <!-- no translation found for accessibility_divider (5903423481953635044) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_left_full (2801570521881574972) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_left_70 (3612060638991687254) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_left_50 (1248083470322193075) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_left_30 (543324403127069386) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_right_full (4639381073802030463) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_top_full (5357010904067731654) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_top_70 (5090779195650364522) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_top_50 (6385859741925078668) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_top_30 (6201455163864841205) -->
-    <skip />
-    <!-- no translation found for accessibility_action_divider_bottom_full (301433196679548001) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_tile_label (8374924053307764245) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_add_tile_label (8133209638023882667) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_position_label (5055306305919289819) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_move_tile (2461819993780159542) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_remove_tile (7484493384665907197) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_tile_added (8050200862063548309) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_tile_removed (8584304916627913440) -->
-    <skip />
-    <!-- no translation found for accessibility_qs_edit_tile_moved (4343693412689365038) -->
-    <skip />
-    <!-- no translation found for accessibility_desc_quick_settings_edit (8073587401747016103) -->
-    <skip />
+    <string name="qs_edit" msgid="2232596095725105230">"ଏଡିଟ୍‌ କରନ୍ତୁ"</string>
+    <string name="tuner_time" msgid="6572217313285536011">"ସମୟ"</string>
+  <string-array name="clock_options">
+    <item msgid="5965318737560463480">"ଘଣ୍ଟା, ମିନିଟ୍‍ ଏବଂ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ"</item>
+    <item msgid="1427801730816895300">"ଘଣ୍ଟା ଏବଂ ମିନିଟ୍‍ ଦେଖାନ୍ତୁ (ଡିଫଲ୍ଟ)"</item>
+    <item msgid="3830170141562534721">"ଏହି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ ନାହିଁ"</item>
+  </string-array>
+  <string-array name="battery_options">
+    <item msgid="3160236755818672034">"ସର୍ବଦା ଶତକଡ଼ା ଦେଖାନ୍ତୁ"</item>
+    <item msgid="2139628951880142927">"ଚାର୍ଜ କରାଯିବାବେଳେ ଶତକଡ଼ା ଦେଖାନ୍ତୁ (ଡିଫଲ୍ଟ)"</item>
+    <item msgid="3327323682209964956">"ଏହି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ ନାହିଁ"</item>
+  </string-array>
+    <string name="other" msgid="4060683095962566764">"ଅନ୍ୟାନ୍ୟ"</string>
+    <string name="accessibility_divider" msgid="5903423481953635044">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନ ବିଭାଜକ"</string>
+    <string name="accessibility_action_divider_left_full" msgid="2801570521881574972">"ବାମ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_left_70" msgid="3612060638991687254">"ବାମ ପଟକୁ 70% କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_left_50" msgid="1248083470322193075">"ବାମ ପଟକୁ 50% କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_left_30" msgid="543324403127069386">"ବାମ ପଟେ 30%"</string>
+    <string name="accessibility_action_divider_right_full" msgid="4639381073802030463">"ଡାହାଣ ପଟକୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_top_full" msgid="5357010904067731654">"ଉପର ଆଡ଼କୁ ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍ କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_top_70" msgid="5090779195650364522">"ଉପର ଆଡ଼କୁ 70% କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_top_50" msgid="6385859741925078668">"ଉପର ଆଡ଼କୁ 50% କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_top_30" msgid="6201455163864841205">"ଉପର ଆଡ଼କୁ 30% କରନ୍ତୁ"</string>
+    <string name="accessibility_action_divider_bottom_full" msgid="301433196679548001">"ତଳ ଅଂଶର ପୂର୍ଣ୍ଣ ସ୍କ୍ରୀନ୍‍"</string>
+    <string name="accessibility_qs_edit_tile_label" msgid="8374924053307764245">"ଅବସ୍ଥାନ <xliff:g id="POSITION">%1$d</xliff:g>, <xliff:g id="TILE_NAME">%2$s</xliff:g>। ଏଡିଟ୍ କରିବାକୁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="accessibility_qs_edit_add_tile_label" msgid="8133209638023882667">"<xliff:g id="TILE_NAME">%1$s</xliff:g>। ଯୋଡ଼ିବା ପାଇଁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="accessibility_qs_edit_position_label" msgid="5055306305919289819">"ଅବସ୍ଥିତି <xliff:g id="POSITION">%1$d</xliff:g>। ଚୟନ କରିବାକୁ ଡବଲ୍‍-ଟାପ୍‍ କରନ୍ତୁ।"</string>
+    <string name="accessibility_qs_edit_move_tile" msgid="2461819993780159542">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ନିଅନ୍ତୁ"</string>
+    <string name="accessibility_qs_edit_remove_tile" msgid="7484493384665907197">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ବାହାର କରିଦିଅନ୍ତୁ"</string>
+    <string name="accessibility_qs_edit_tile_added" msgid="8050200862063548309">"<xliff:g id="TILE_NAME">%1$s</xliff:g>କୁ ଅବସ୍ଥାନ <xliff:g id="POSITION">%2$d</xliff:g>ରେ ଯୋଡ଼ାଗଲା"</string>
+    <string name="accessibility_qs_edit_tile_removed" msgid="8584304916627913440">"<xliff:g id="TILE_NAME">%1$s</xliff:g> ବାହାର କରିଦିଆଗଲା"</string>
+    <string name="accessibility_qs_edit_tile_moved" msgid="4343693412689365038">"<xliff:g id="TILE_NAME">%1$s</xliff:g> କୁ ଅବସ୍ଥାନ <xliff:g id="POSITION">%2$d</xliff:g> କୁ ନିଆଗଲା"</string>
+    <string name="accessibility_desc_quick_settings_edit" msgid="8073587401747016103">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଏଡିଟର୍।"</string>
     <string name="accessibility_desc_notification_icon" msgid="8352414185263916335">"<xliff:g id="ID_1">%1$s</xliff:g> ବିଜ୍ଞପ୍ତି: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
-    <!-- no translation found for dock_forced_resizable (5914261505436217520) -->
-    <skip />
-    <!-- no translation found for dock_non_resizeble_failed_to_dock_text (3871617304250207291) -->
-    <skip />
-    <!-- no translation found for forced_resizable_secondary_display (4230857851756391925) -->
-    <skip />
-    <!-- no translation found for activity_launch_on_secondary_display_failed_text (7793821742158306742) -->
-    <skip />
+    <string name="dock_forced_resizable" msgid="5914261505436217520">"ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନରେ ଆପ୍‍ କାମ କରିନପାରେ।"</string>
+    <string name="dock_non_resizeble_failed_to_dock_text" msgid="3871617304250207291">"ଆପ୍‍ ସ୍ପ୍ଲିଟ୍‍-ସ୍କ୍ରୀନକୁ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
+    <string name="forced_resizable_secondary_display" msgid="4230857851756391925">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ କାମ ନକରିପାରେ।"</string>
+    <string name="activity_launch_on_secondary_display_failed_text" msgid="7793821742158306742">"ସେକେଣ୍ଡାରୀ ଡିସପ୍ଲେରେ ଆପ୍‍ ଲଞ୍ଚ ସପୋର୍ଟ କରେ ନାହିଁ।"</string>
     <string name="accessibility_quick_settings_settings" msgid="6132460890024942157">"ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_expand" msgid="2375165227880477530">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_collapse" msgid="1792625797142648105">"ଦ୍ରୁତ ସେଟିଙ୍ଗ ବନ୍ଦ କରନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_alarm_set" msgid="1863000242431528676">"ଆଲାର୍ମ ସେଟ୍‍।"</string>
     <string name="accessibility_quick_settings_user" msgid="1567445362870421770">"<xliff:g id="ID_1">%s</xliff:g> ଭାବରେ ସାଇନ୍‌ ଇନ୍‌ କରିଛନ୍ତି"</string>
-    <string name="accessibility_quick_settings_no_internet" msgid="31890692343084075">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ ନାହିଁ"</string>
+    <!-- no translation found for data_connection_no_internet (4503302451650972989) -->
+    <skip />
     <string name="accessibility_quick_settings_open_details" msgid="4230931801728005194">"ବିବରଣୀ ଖୋଲନ୍ତୁ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="7806613775728380737">"<xliff:g id="ID_1">%s</xliff:g> ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_edit" msgid="7839992848995240393">"ସେଟିଙ୍ଗର କ୍ରମ ସଂଶୋଧନ କରନ୍ତୁ।"</string>
-    <!-- no translation found for accessibility_quick_settings_page (5032979051755200721) -->
-    <skip />
-    <!-- no translation found for tuner_lock_screen (5755818559638850294) -->
-    <skip />
+    <string name="accessibility_quick_settings_page" msgid="5032979051755200721">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
+    <string name="tuner_lock_screen" msgid="5755818559638850294">"ଲକ୍‌ ସ୍କ୍ରୀନ୍‌"</string>
     <string name="pip_phone_expand" msgid="5889780005575693909">"ବଢ଼ାନ୍ତୁ"</string>
-    <!-- no translation found for pip_phone_minimize (1079119422589131792) -->
+    <string name="pip_phone_minimize" msgid="1079119422589131792">"ଛୋଟ କରନ୍ତୁ"</string>
+    <string name="pip_phone_close" msgid="8416647892889710330">"ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="pip_phone_settings" msgid="8080777499521528521">"ସେଟିଙ୍ଗ"</string>
+    <string name="pip_phone_dismiss_hint" msgid="6351678169095923899">"ଖାରଜ କରିବାକୁ ତଳକୁ ଟାଣନ୍ତୁ"</string>
+    <string name="pip_menu_title" msgid="4707292089961887657">"ମେନୁ"</string>
+    <string name="pip_notification_title" msgid="3204024940158161322">"<xliff:g id="NAME">%s</xliff:g> \"ଛବି-ଭିତରେ-ଛବି\"ରେ ଅଛି"</string>
+    <string name="pip_notification_message" msgid="5619512781514343311">"ଏହି ବୈଶିଷ୍ଟ୍ୟ <xliff:g id="NAME">%s</xliff:g> ବ୍ୟବହାର ନକରିବାକୁ ଯଦି ଆପଣ ଚାହାଁନ୍ତି, ସେଟିଙ୍ଗ ଖୋଲିବାକୁ ଟାପ୍‍ କରନ୍ତୁ ଏବଂ ଏହା ଅଫ୍‍ କରିଦିଅନ୍ତୁ।"</string>
+    <string name="pip_play" msgid="1417176722760265888">"ପ୍ଲେ କରନ୍ତୁ"</string>
+    <string name="pip_pause" msgid="8881063404466476571">"ପଜ୍‍ କରନ୍ତୁ"</string>
+    <string name="pip_skip_to_next" msgid="1948440006726306284">"ପରବର୍ତ୍ତୀକୁ ଯାଆନ୍ତୁ"</string>
+    <string name="pip_skip_to_prev" msgid="1955311326688637914">"ପୂର୍ବବର୍ତ୍ତୀକୁ ଛାଡ଼ନ୍ତୁ"</string>
+    <string name="thermal_shutdown_title" msgid="4458304833443861111">"ଗରମ ହେତୁ ଫୋନ୍‍ ଅଫ୍‍ କରିଦିଆଗଲା"</string>
+    <string name="thermal_shutdown_message" msgid="9006456746902370523">"ଆପଣଙ୍କ ଫୋନ୍‍ ବର୍ତ୍ତମାନ ସାମାନ୍ୟ ଅବସ୍ଥାରେ ଚାଲୁଛି"</string>
+    <string name="thermal_shutdown_dialog_message" msgid="566347880005304139">"ଆପଣଙ୍କ ଫୋନ୍‍ ବହୁତ ଗରମ ଥିଲା, ତେଣୁ ଏହାକୁ ଥଣ୍ଡା କରାଯିବାକୁ ଅଫ୍‍ କରିଦିଆଗଲା। ଆପଣଙ୍କ ଫୋନ୍‍ ବର୍ତ୍ତମାନ ସାମାନ୍ୟ ଅବସ୍ଥାରେ ଚାଲୁଛି।\n\nଆପଣଙ୍କ ଫୋନ୍‍ ଅଧିକ ଗରମ ହୋଇଯାଇପାରେ ଯଦି ଆପଣ:\n	• ରିସୋର୍ସ-ଇଣ୍ଟେନସିଭ୍‍ ଆପ୍‍ (ଯେପରିକି ଗେମିଙ୍ଗ, ଭିଡିଓ, କିମ୍ବା ନେଭିଗେସନ୍‍ ଆପ୍‍) ବ୍ୟବହାର କରନ୍ତି\n	• ବଡ ଫାଇଲ୍‍ ଡାଉନଲୋଡ୍ କିମ୍ବା ଅପଲୋଡ୍‍ କରନ୍ତି\n	• ଅଧିକ ତାପମାତ୍ରାରେ ଆପଣଙ୍କ ଫୋନ୍‍ ବ୍ୟବହାର କରନ୍ତି"</string>
+    <string name="high_temp_title" msgid="4589508026407318374">"ଫୋନ୍‍ ଗରମ ହୋଇଯାଉଛି"</string>
+    <string name="high_temp_notif_message" msgid="5642466103153429279">"ଫୋନ୍‍ ଥଣ୍ଡା ହେବା ସମୟରେ କିଛି ଫିଚର୍ ସୀମିତ ଭାବେ କାମ କରିଥାଏ"</string>
+    <string name="high_temp_dialog_message" msgid="6840700639374113553">"ଆପଣଙ୍କ ଫୋନ୍‍ ସ୍ୱଚାଳିତ ଭାବେ ଥଣ୍ଡା ହେବାକୁ ଚେଷ୍ଟା କରିବ। ଆପଣ ତଥାପି ନିଜ ଫୋନ୍‍ ବ୍ୟବହାର କରିପାରିବେ, କିନ୍ତୁ ଏହା ଧୀରେ ଚାଲିପାରେ।\n\nଆପଣଙ୍କ ଫୋନ୍‍ ଥଣ୍ଡା ହୋଇଯିବାପରେ, ଏହା ସାମାନ୍ୟ ଭାବେ ଚାଲିବ।"</string>
+    <string name="lockscreen_shortcut_left" msgid="2182769107618938629">"ବାମ ଶର୍ଟକଟ୍‍"</string>
+    <string name="lockscreen_shortcut_right" msgid="3328683699505226536">"ଡାହାଣ ଶର୍ଟକଟ୍‍"</string>
+    <string name="lockscreen_unlock_left" msgid="2043092136246951985">"ବାମ ଶର୍ଟକଟ୍‍ ମଧ୍ୟ ଅନଲକ୍‍ କରେ"</string>
+    <string name="lockscreen_unlock_right" msgid="1529992940510318775">"ଡାହାଣ ଶର୍ଟକଟ୍‍ ମଧ୍ୟ ଅନଲକ୍‍ କରେ"</string>
+    <string name="lockscreen_none" msgid="4783896034844841821">"କିଛିନୁହେଁ"</string>
+    <string name="tuner_launch_app" msgid="1527264114781925348">"<xliff:g id="APP">%1$s</xliff:g> ଲଞ୍ଚ କରନ୍ତୁ"</string>
+    <string name="tuner_other_apps" msgid="4726596850501162493">"ଅନ୍ୟାନ୍ୟ ଆପ୍‍"</string>
+    <string name="tuner_circle" msgid="2340998864056901350">"ବୃତ୍ତ"</string>
+    <string name="tuner_plus" msgid="6792960658533229675">"ଯୁକ୍ତ"</string>
+    <string name="tuner_minus" msgid="4806116839519226809">"ବିଯୁକ୍ତ"</string>
+    <string name="tuner_left" msgid="8404287986475034806">"ବାମ"</string>
+    <string name="tuner_right" msgid="6222734772467850156">"ଡାହାଣ"</string>
+    <string name="tuner_menu" msgid="191640047241552081">"ମେନୁ"</string>
+    <string name="tuner_app" msgid="3507057938640108777">"<xliff:g id="APP">%1$s</xliff:g> ଆପ୍‍"</string>
+    <string name="notification_channel_alerts" msgid="4496839309318519037">"ଆଲର୍ଟ"</string>
+    <string name="notification_channel_battery" msgid="5786118169182888462">"ବ୍ୟାଟେରୀ"</string>
+    <string name="notification_channel_screenshot" msgid="6314080179230000938">"ସ୍କ୍ରୀନଶଟ୍‍"</string>
+    <string name="notification_channel_general" msgid="4525309436693914482">"ସାଧାରଣ ମେସେଜ୍"</string>
+    <string name="notification_channel_storage" msgid="3077205683020695313">"ଷ୍ଟୋରେଜ୍‌"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
     <skip />
-    <!-- no translation found for pip_phone_close (8416647892889710330) -->
-    <skip />
-    <!-- no translation found for pip_phone_settings (8080777499521528521) -->
-    <skip />
-    <!-- no translation found for pip_phone_dismiss_hint (6351678169095923899) -->
-    <skip />
-    <!-- no translation found for pip_menu_title (4707292089961887657) -->
-    <skip />
-    <!-- no translation found for pip_notification_title (3204024940158161322) -->
-    <skip />
-    <!-- no translation found for pip_notification_message (5619512781514343311) -->
-    <skip />
-    <!-- no translation found for pip_play (1417176722760265888) -->
-    <skip />
-    <!-- no translation found for pip_pause (8881063404466476571) -->
-    <skip />
-    <!-- no translation found for pip_skip_to_next (1948440006726306284) -->
-    <skip />
-    <!-- no translation found for pip_skip_to_prev (1955311326688637914) -->
-    <skip />
-    <!-- no translation found for thermal_shutdown_title (4458304833443861111) -->
-    <skip />
-    <!-- no translation found for thermal_shutdown_message (9006456746902370523) -->
-    <skip />
-    <!-- no translation found for thermal_shutdown_dialog_message (566347880005304139) -->
-    <skip />
-    <!-- no translation found for high_temp_title (4589508026407318374) -->
-    <skip />
-    <!-- no translation found for high_temp_notif_message (5642466103153429279) -->
-    <skip />
-    <!-- no translation found for high_temp_dialog_message (6840700639374113553) -->
-    <skip />
-    <!-- no translation found for lockscreen_shortcut_left (2182769107618938629) -->
-    <skip />
-    <!-- no translation found for lockscreen_shortcut_right (3328683699505226536) -->
-    <skip />
-    <!-- no translation found for lockscreen_unlock_left (2043092136246951985) -->
-    <skip />
-    <!-- no translation found for lockscreen_unlock_right (1529992940510318775) -->
-    <skip />
-    <!-- no translation found for lockscreen_none (4783896034844841821) -->
-    <skip />
-    <!-- no translation found for tuner_launch_app (1527264114781925348) -->
-    <skip />
-    <!-- no translation found for tuner_other_apps (4726596850501162493) -->
-    <skip />
-    <!-- no translation found for tuner_circle (2340998864056901350) -->
-    <skip />
-    <!-- no translation found for tuner_plus (6792960658533229675) -->
-    <skip />
-    <!-- no translation found for tuner_minus (4806116839519226809) -->
-    <skip />
-    <!-- no translation found for tuner_left (8404287986475034806) -->
-    <skip />
-    <!-- no translation found for tuner_right (6222734772467850156) -->
-    <skip />
-    <!-- no translation found for tuner_menu (191640047241552081) -->
-    <skip />
-    <!-- no translation found for tuner_app (3507057938640108777) -->
-    <skip />
-    <!-- no translation found for notification_channel_alerts (4496839309318519037) -->
-    <skip />
-    <!-- no translation found for notification_channel_battery (5786118169182888462) -->
-    <skip />
-    <!-- no translation found for notification_channel_screenshot (6314080179230000938) -->
-    <skip />
-    <!-- no translation found for notification_channel_general (4525309436693914482) -->
-    <skip />
-    <!-- no translation found for notification_channel_storage (3077205683020695313) -->
-    <skip />
-    <!-- no translation found for instant_apps (6647570248119804907) -->
-    <skip />
-    <!-- no translation found for instant_apps_message (8116608994995104836) -->
-    <skip />
+    <string name="instant_apps" msgid="6647570248119804907">"ଇନଷ୍ଟାଣ୍ଟ ଆପ୍‌"</string>
+    <string name="instant_apps_message" msgid="8116608994995104836">"ଇନଷ୍ଟାଣ୍ଟ ଆପ୍‌ ଇନଷ୍ଟଲ୍‍ କରିବାର ଆବଶ୍ୟକତା ନାହିଁ"</string>
     <string name="app_info" msgid="6856026610594615344">"ଆପ୍‍ ସୂଚନା"</string>
-    <!-- no translation found for go_to_web (2650669128861626071) -->
+    <string name="go_to_web" msgid="2650669128861626071">"ବ୍ରାଉଜର୍‌କୁ ଯାଆନ୍ତୁ"</string>
+    <string name="mobile_data" msgid="7094582042819250762">"ମୋବାଇଲ୍‌ ଡାଟା"</string>
+    <!-- no translation found for mobile_data_text_format (3526214522670876454) -->
     <skip />
-    <!-- no translation found for mobile_data (7094582042819250762) -->
+    <string name="wifi_is_off" msgid="1838559392210456893">"ୱାଇ-ଫାଇ ଅଫ୍‍ ଅଛି"</string>
+    <string name="bt_is_off" msgid="2640685272289706392">"ବ୍ଲୁ-ଟୁଥ୍‌ ଅଫ୍ ଅଛି"</string>
+    <string name="dnd_is_off" msgid="6167780215212497572">"\"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅଫ୍‍ ଅଛି"</string>
+    <string name="qs_dnd_prompt_auto_rule" msgid="862559028345233052">"ଏକ (<xliff:g id="ID_1">%s</xliff:g>) ନିୟମ ଦ୍ୱାରା \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ସ୍ୱଚାଳିତ ଭାବେ ଅନ୍‍ କରାଗଲା।"</string>
+    <string name="qs_dnd_prompt_app" msgid="7978037419334156034">"ଏକ ଆପ୍‍ (<xliff:g id="ID_1">%s</xliff:g>) ଦ୍ୱାରା \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍‌ କରାଗଲା।"</string>
+    <string name="qs_dnd_prompt_auto_rule_app" msgid="2599343675391111951">"ଏକ ସ୍ୱଚାଳିତ ନିୟମ କିମ୍ବା ଆପ୍‍ ଦ୍ୱାରା \"ବିରକ୍ତ କରନ୍ତୁ ନାହିଁ\" ଅନ୍‍ କରାଗଲା।"</string>
+    <string name="qs_dnd_until" msgid="3469471136280079874">"<xliff:g id="ID_1">%s</xliff:g> ପର୍ଯ୍ୟନ୍ତ"</string>
+    <string name="qs_dnd_keep" msgid="1825009164681928736">"ଧରି ରଖନ୍ତୁ"</string>
+    <string name="qs_dnd_replace" msgid="8019520786644276623">"ବଦଳାନ୍ତୁ"</string>
+    <string name="running_foreground_services_title" msgid="381024150898615683">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍‍ ଚାଲୁଛି"</string>
+    <string name="running_foreground_services_msg" msgid="6326247670075574355">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ"</string>
+    <!-- no translation found for mobile_data_disable_title (1068272097382942231) -->
     <skip />
-    <!-- no translation found for wifi_is_off (1838559392210456893) -->
+    <!-- no translation found for mobile_data_disable_message (4756541658791493506) -->
     <skip />
-    <!-- no translation found for bt_is_off (2640685272289706392) -->
+    <!-- no translation found for mobile_data_disable_message_default_carrier (6078110473451946831) -->
     <skip />
-    <!-- no translation found for dnd_is_off (6167780215212497572) -->
-    <skip />
-    <!-- no translation found for qs_dnd_prompt_auto_rule (862559028345233052) -->
-    <skip />
-    <!-- no translation found for qs_dnd_prompt_app (7978037419334156034) -->
-    <skip />
-    <!-- no translation found for qs_dnd_prompt_auto_rule_app (2599343675391111951) -->
-    <skip />
-    <!-- no translation found for qs_dnd_until (3469471136280079874) -->
-    <skip />
-    <!-- no translation found for qs_dnd_keep (1825009164681928736) -->
-    <skip />
-    <!-- no translation found for qs_dnd_replace (8019520786644276623) -->
-    <skip />
-    <!-- no translation found for running_foreground_services_title (381024150898615683) -->
-    <skip />
-    <!-- no translation found for running_foreground_services_msg (6326247670075574355) -->
-    <skip />
-    <string name="data_usage_disable_mobile" msgid="5116269981510015864">"ମୋବାଇଲ୍‌ ଡାଟା ଅଫ୍‌ କରିବେ?"</string>
-    <!-- no translation found for touch_filtered_warning (8671693809204767551) -->
-    <skip />
+    <string name="touch_filtered_warning" msgid="8671693809204767551">"ଗୋଟିଏ ଆପ୍‍ ଏକ ଅନୁମତି ଅନୁରୋଧକୁ ଦେଖିବାରେ ବାଧା ଦେଉଥିବାରୁ, ସେଟିଙ୍ଗ ଆପଣଙ୍କ ଉତ୍ତରକୁ ଯାଞ୍ଚ କରିପାରିବ ନାହିଁ।"</string>
     <!-- no translation found for slice_permission_title (7465009437851044444) -->
     <skip />
     <!-- no translation found for slice_permission_text_1 (3514586565609596523) -->
@@ -1131,4 +916,18 @@
     <skip />
     <!-- no translation found for slice_permission_deny (7683681514008048807) -->
     <skip />
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 080f7e4..c52de5a 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -33,7 +33,6 @@
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Em andamento"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Notificações"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"Bateria fraca"</string>
-    <string name="battery_low_title_hybrid" msgid="6268991275887381595">"A bateria está com pouca carga. Ative a Economia de bateria"</string>
     <string name="battery_low_percent_format" msgid="2900940511201380775">"<xliff:g id="PERCENTAGE">%s</xliff:g> restantes"</string>
     <string name="battery_low_percent_format_hybrid" msgid="6838677459286775617">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g> com base no seu uso"</string>
     <string name="battery_low_percent_format_hybrid_short" msgid="9025795469949145586">"<xliff:g id="PERCENTAGE">%s</xliff:g> restante(s), cerca de <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -73,11 +72,12 @@
     <string name="global_action_screenshot" msgid="8329831278085426283">"Captura de tela"</string>
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Salvando captura de tela..."</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"Salvando captura de tela..."</string>
-    <string name="screenshot_saving_text" msgid="2545047868936087248">"A captura de tela está sendo salva"</string>
     <string name="screenshot_saved_title" msgid="5637073968117370753">"Captura de tela salva"</string>
     <string name="screenshot_saved_text" msgid="7574667448002050363">"Toque para ver sua captura de tela"</string>
-    <string name="screenshot_failed_title" msgid="9096484883063264803">"Não foi possível fazer a captura de tela"</string>
-    <string name="screenshot_failed_to_save_unknown_text" msgid="8844781948876286488">"Problema encontrado ao salvar captura de tela"</string>
+    <!-- no translation found for screenshot_failed_title (7612509838919089748) -->
+    <skip />
+    <!-- no translation found for screenshot_failed_to_save_unknown_text (3637758096565605541) -->
+    <skip />
     <string name="screenshot_failed_to_save_text" msgid="3041612585107107310">"Não é possível salvar a captura de tela, porque não há espaço suficiente"</string>
     <string name="screenshot_failed_to_capture_text" msgid="173674476457581486">"O app ou a organização não permitem capturas de tela"</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"Opções transf. arq. por USB"</string>
@@ -275,8 +275,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"Mostruário de sobremesas"</string>
     <string name="start_dreams" msgid="5640361424498338327">"Protetor de tela"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"Ethernet"</string>
-    <!-- no translation found for quick_settings_header_onboarding_text (8030309023792936283) -->
-    <skip />
+    <string name="quick_settings_header_onboarding_text" msgid="8030309023792936283">"Toque nos ícones e mantenha-os pressionados para ver mais opções"</string>
     <string name="quick_settings_dnd_label" msgid="8735855737575028208">"Não perturbe"</string>
     <string name="quick_settings_dnd_priority_label" msgid="483232950670692036">"Somente prioridade"</string>
     <string name="quick_settings_dnd_alarms_label" msgid="2559229444312445858">"Somente alarmes"</string>
@@ -351,7 +350,8 @@
     <string name="quick_settings_night_secondary_label_on_at_sunset" msgid="8483259341596943314">"Ativ. ao pôr do sol"</string>
     <string name="quick_settings_night_secondary_label_until_sunrise" msgid="4453017157391574402">"Até o nascer do sol"</string>
     <string name="quick_settings_night_secondary_label_on_at" msgid="6256314040368487637">"Ativado às <xliff:g id="TIME">%s</xliff:g>"</string>
-    <string name="quick_settings_night_secondary_label_until" msgid="8664820079774824618">"Até <xliff:g id="TIME">%s</xliff:g>"</string>
+    <!-- no translation found for quick_settings_secondary_label_until (2749196569462600150) -->
+    <skip />
     <string name="quick_settings_nfc_label" msgid="9012153754816969325">"NFC"</string>
     <string name="quick_settings_nfc_off" msgid="6883274004315134333">"A NFC está desativada"</string>
     <string name="quick_settings_nfc_on" msgid="6680317193676884311">"A NFC está ativada"</string>
@@ -435,6 +435,8 @@
     <string name="media_projection_dialog_text" msgid="3071431025448218928">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> começará a capturar tudo o que for exibido na tela."</string>
     <string name="media_projection_remember_text" msgid="3103510882172746752">"Não mostrar novamente"</string>
     <string name="clear_all_notifications_text" msgid="814192889771462828">"Limpar tudo"</string>
+    <!-- no translation found for manage_notifications_text (8035284146227267681) -->
+    <skip />
     <string name="dnd_suppressing_shade_text" msgid="7986451830430707907">"O modo \"Não perturbe\" está ocultando as notificações"</string>
     <string name="media_projection_action_text" msgid="8470872969457985954">"Iniciar agora"</string>
     <string name="empty_shade_text" msgid="708135716272867002">"Sem notificações"</string>
@@ -628,6 +630,8 @@
     <string name="notification_menu_accessibility" msgid="2046162834248888553">"<xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g> do <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="2204480013726775108">"controles de notificação"</string>
     <string name="notification_menu_snooze_description" msgid="3653669438131034525">"opções de adiamento de notificação"</string>
+    <!-- no translation found for notification_menu_snooze_action (1112254519029621372) -->
+    <skip />
     <string name="snooze_undo" msgid="6074877317002985129">"DESFAZER"</string>
     <string name="snoozed_for_time" msgid="2390718332980204462">"Adiada para <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
     <plurals name="snoozeHourOptions" formatted="false" msgid="2124335842674413030">
@@ -725,11 +729,9 @@
     <string name="right_keycode" msgid="708447961000848163">"Código de tecla à direita"</string>
     <string name="left_icon" msgid="3096287125959387541">"Ícone à esquerda"</string>
     <string name="right_icon" msgid="3952104823293824311">"Ícone à direita"</string>
-    <!-- no translation found for drag_to_add_tiles (230586591689084925) -->
-    <skip />
+    <string name="drag_to_add_tiles" msgid="230586591689084925">"Mantenha pressionado e arraste para adicionar blocos"</string>
     <string name="drag_to_remove_tiles" msgid="3361212377437088062">"Arraste aqui para remover"</string>
-    <!-- no translation found for drag_to_remove_disabled (2390968976638993382) -->
-    <skip />
+    <string name="drag_to_remove_disabled" msgid="2390968976638993382">"É preciso haver pelo menos seis blocos"</string>
     <string name="qs_edit" msgid="2232596095725105230">"Editar"</string>
     <string name="tuner_time" msgid="6572217313285536011">"Horas"</string>
   <string-array name="clock_options">
@@ -816,6 +818,8 @@
     <string name="notification_channel_screenshot" msgid="6314080179230000938">"Capturas de tela"</string>
     <string name="notification_channel_general" msgid="4525309436693914482">"Mensagens gerais"</string>
     <string name="notification_channel_storage" msgid="3077205683020695313">"Armazenamento"</string>
+    <!-- no translation found for notification_channel_hints (7323870212489152689) -->
+    <skip />
     <string name="instant_apps" msgid="6647570248119804907">"Instant Apps"</string>
     <string name="instant_apps_message" msgid="8116608994995104836">"Os Instant Apps não requerem instalação."</string>
     <string name="app_info" msgid="6856026610594615344">"Informações do app"</string>
@@ -833,12 +837,9 @@
     <string name="qs_dnd_replace" msgid="8019520786644276623">"Substituir"</string>
     <string name="running_foreground_services_title" msgid="381024150898615683">"Apps sendo executados em segundo plano"</string>
     <string name="running_foreground_services_msg" msgid="6326247670075574355">"Tocar para ver detalhes sobre a bateria e o uso de dados"</string>
-    <!-- no translation found for mobile_data_disable_title (1068272097382942231) -->
-    <skip />
-    <!-- no translation found for mobile_data_disable_message (4756541658791493506) -->
-    <skip />
-    <!-- no translation found for mobile_data_disable_message_default_carrier (6078110473451946831) -->
-    <skip />
+    <string name="mobile_data_disable_title" msgid="1068272097382942231">"Desativar os dados móveis?"</string>
+    <string name="mobile_data_disable_message" msgid="4756541658791493506">"Você não terá acesso a dados ou à Internet por meio da operadora <xliff:g id="CARRIER">%s</xliff:g>. A Internet só estará disponível via Wi-Fi."</string>
+    <string name="mobile_data_disable_message_default_carrier" msgid="6078110473451946831">"sua operadora"</string>
     <string name="touch_filtered_warning" msgid="8671693809204767551">"Como um app está ocultando uma solicitação de permissão, as configurações não podem verificar sua resposta."</string>
     <string name="slice_permission_title" msgid="7465009437851044444">"Permitir que <xliff:g id="APP_0">%1$s</xliff:g> mostre partes do app <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
     <string name="slice_permission_text_1" msgid="3514586565609596523">"- Pode ler informações do app <xliff:g id="APP">%1$s</xliff:g>"</string>
@@ -846,4 +847,18 @@
     <string name="slice_permission_checkbox" msgid="7986504458640562900">"Permitir que <xliff:g id="APP">%1$s</xliff:g> mostre partes de qualquer app"</string>
     <string name="slice_permission_allow" msgid="2340244901366722709">"Permitir"</string>
     <string name="slice_permission_deny" msgid="7683681514008048807">"Negar"</string>
+    <!-- no translation found for auto_saver_title (1217959994732964228) -->
+    <skip />
+    <!-- no translation found for auto_saver_text (6324376061044218113) -->
+    <skip />
+    <!-- no translation found for no_auto_saver_action (8086002101711328500) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_title (6726474226058316862) -->
+    <skip />
+    <!-- no translation found for auto_saver_enabled_text (874711029884777579) -->
+    <skip />
+    <!-- no translation found for open_saver_setting_action (8314624730997322529) -->
+    <skip />
+    <!-- no translation found for auto_saver_okay_action (2701221740227683650) -->
+    <skip />
 </resources>
diff --git a/packages/SystemUI/res/values/attrs_car.xml b/packages/SystemUI/res/values/attrs_car.xml
index b1097c3..5e4bd79 100644
--- a/packages/SystemUI/res/values/attrs_car.xml
+++ b/packages/SystemUI/res/values/attrs_car.xml
@@ -27,6 +27,13 @@
         <attr name="categories" format="string"/>
         <!-- package names that will be added as extras to the fired intents -->
         <attr name="packages" format="string" />
+        <!-- Alpha value to used when in selected state.  Defaults 1f  -->
+        <attr name="selectedAlpha" format="float" />
+        <!-- Alpha value to used when in un-selected state.  Defaults 0.7f  -->
+        <attr name="unselectedAlpha" format="float" />
+        <!-- Render a "more" icon. Defaults true  -->
+        <attr name="useMoreIcon" format="boolean" />
+
     </declare-styleable>
 
 
@@ -39,4 +46,11 @@
         <!-- start the intent as a broad cast instead of an activity if true-->
         <attr name="broadcast" format="boolean"/>
     </declare-styleable>
+
+    <!-- Custom attributes to configure hvac values -->
+    <declare-styleable name="TemperatureView">
+        <attr name="hvacAreaId" format="integer"/>
+        <attr name="hvacPropertyId" format="integer"/>
+        <attr name="hvacTempFormat" format="string"/>
+    </declare-styleable>
 </resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f76603b..711d550 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -45,9 +45,6 @@
      interface.  This name is in the ComponentName flattened format (package/class)  -->
     <string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.StatusBar</string>
 
-    <!-- Component name of launcher service for overview to connect to -->
-    <string name="config_overviewServiceComponent" translatable="false">com.android.launcher3/com.android.quickstep.TouchInteractionService</string>
-
     <!-- Whether or not we show the number in the bar. -->
     <bool name="config_statusBarShowNumber">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ff3f696..84ca657 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -270,13 +270,15 @@
     <!-- The width of the panel that holds the quick settings. -->
     <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
 
+    <dimen name="volume_dialog_panel_transparent_padding">8dp</dimen>
+
     <!-- the amount the volume panel should be offset at the end from the view next to it (or
     the screen edge, in portrait-->
     <dimen name="volume_dialog_base_margin">8dp</dimen>
 
     <dimen name="volume_dialog_panel_width">64dp</dimen>
 
-    <dimen name="volume_dialog_slider_height">108dp</dimen>
+    <dimen name="volume_dialog_slider_height">116dp</dimen>
 
     <dimen name="volume_dialog_row_height">252dp</dimen>
 
@@ -286,7 +288,7 @@
 
     <dimen name="volume_dialog_spacer">4dp</dimen>
 
-    <dimen name="volume_dialog_slider_margin_top">22dp</dimen>
+    <dimen name="volume_dialog_slider_margin_top">14dp</dimen>
 
     <dimen name="volume_dialog_slider_margin_bottom">-2dp</dimen>
 
@@ -294,6 +296,8 @@
 
     <dimen name="volume_dialog_settings_icon_size">16dp</dimen>
 
+    <dimen name="volume_dialog_elevation">9dp</dimen>
+
     <!-- Gravity for the notification panel -->
     <integer name="notification_panel_layout_gravity">0x31</integer><!-- center_horizontal|top -->
 
@@ -313,10 +317,14 @@
     <dimen name="pull_span_min">25dp</dimen>
 
     <dimen name="qs_tile_height">106dp</dimen>
+    <dimen name="qs_tile_layout_margin_side">9dp</dimen>
     <dimen name="qs_tile_margin_horizontal">18dp</dimen>
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
+    <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
     <dimen name="qs_quick_tile_size">48dp</dimen>
+    <!-- Width for the spacer, used between QS tiles. -->
+    <dimen name="qs_quick_tile_space_width">0dp</dimen>
     <dimen name="qs_quick_tile_padding">12dp</dimen>
     <dimen name="qs_header_gear_translation">16dp</dimen>
     <dimen name="qs_header_tile_margin_horizontal">0dp</dimen>
@@ -732,8 +740,6 @@
     <dimen name="volume_expander_margin_end">2dp</dimen>
     <dimen name="volume_expander_margin_top">6dp</dimen>
 
-    <dimen name="volume_panel_elevation">8dp</dimen>
-
     <!-- Padding between icon and text for managed profile toast -->
     <dimen name="managed_profile_toast_padding">4dp</dimen>
 
diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml
index 5679dd2..6caed61 100644
--- a/packages/SystemUI/res/values/dimens_car.xml
+++ b/packages/SystemUI/res/values/dimens_car.xml
@@ -32,6 +32,8 @@
 
     <dimen name="car_navigation_button_width">64dp</dimen>
     <dimen name="car_navigation_bar_width">760dp</dimen>
+    <dimen name="car_left_navigation_bar_width">96dp</dimen>
+    <dimen name="car_right_navigation_bar_width">96dp</dimen>
 
     <dimen name="car_page_indicator_dot_diameter">12dp</dimen>
     <dimen name="car_page_indicator_margin_bottom">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 909c18b..4074042 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1057,7 +1057,7 @@
     <string name="manage_notifications_text">Manage notifications</string>
 
     <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
-    <string name="dnd_suppressing_shade_text">Do Not disturb is hiding notifications</string>
+    <string name="dnd_suppressing_shade_text">Do Not Disturb is hiding notifications</string>
 
     <!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
     <string name="media_projection_action_text">Start now</string>
@@ -1350,7 +1350,7 @@
 
     <string name="volume_dialog_title">%s volume controls</string>
 
-    <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring</string>
+    <string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring (<xliff:g id="volume level" example="56">%1$s</xliff:g>)</string>
 
     <string name="output_title">Media output</string>
     <string name="output_calls_title">Phone call output</string>
@@ -1748,6 +1748,10 @@
     <!-- Name of the headset in status bar [CHAR LIMIT=30] -->
     <string name="headset">Headset</string>
 
+    <!-- Accessibility description for long click on a quick settings tile - this is used in the
+         context of the sentence "double tap and hold to _Open settings_" [CHAR LIMIT=NONE] -->
+    <string name="accessibility_long_click_tile">Open settings</string>
+
     <!-- Accessibility description of headphones icon [CHAR LIMIT=NONE] -->
     <string name="accessibility_status_bar_headphones">Headphones connected</string>
 
@@ -2096,6 +2100,8 @@
     <string name="notification_channel_general">General Messages</string>
     <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] -->
     <string name="notification_channel_storage">Storage</string>
+    <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] -->
+    <string name="notification_channel_hints">Hints</string>
 
     <!-- App label of the instant apps notification [CHAR LIMIT=60] -->
     <string name="instant_apps">Instant Apps</string>
@@ -2186,4 +2192,25 @@
     <string-array name="recents_onboarding_blacklisted_packages" translatable="false">
     </string-array>
 
+    <!-- The title of the notification to suggest enabling automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_title">Tap to schedule Battery Saver</string>
+
+    <!-- The content of the notification to suggest enabling automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_text">Turn on automatically when battery is at <xliff:g id="percentage">%d</xliff:g>%%</string>
+
+    <!-- An action on the notification to suggest enabling automatic battery saver: Do not turn on automatic battery saver.  [CHAR LIMIT=NONE]-->
+    <string name="no_auto_saver_action">No thanks</string>
+
+    <!-- The title of the dialog that tells that scheduled (i.e. automatic) battery saver has been turned on. [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_enabled_title">Battery Saver schedule turned on</string>
+
+    <!-- The content of the dialog that tells that scheduled (i.e. automatic) battery saver has been turned on. [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_enabled_text">Battery Saver will turn on automatically once battery goes below <xliff:g id="percentage">%d</xliff:g>%%.</string>
+
+    <!-- An action on the dialog that tells that scheduled (i.e. automatic) battery saver: open the battery saver setting.  [CHAR LIMIT=NONE]-->
+    <string name="open_saver_setting_action">Settings</string>
+
+    <!-- An action on the dialog that tells that scheduled (i.e. automatic) battery saver: user acknowledges and closes the dialog.  [CHAR LIMIT=NONE]-->
+    <string name="auto_saver_okay_action">Got it</string>
+
 </resources>
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 14767f1..20d1418 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
@@ -19,6 +19,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
@@ -106,11 +107,12 @@
             return createDrawableFromBitmap(tdIcon, userId, desc);
         }
         if (desc.getIconResource() != 0) {
-            // TODO: Use task context here
             try {
-                return createBadgedDrawable(
-                        mContext.getDrawable(desc.getIconResource()), userId, desc);
-            } catch (Resources.NotFoundException e) {
+                Context packageContext = mContext.createPackageContextAsUser(
+                        taskKey.getPackageName(), 0, UserHandle.of(userId));
+                return createBadgedDrawable(packageContext.getDrawable(desc.getIconResource()),
+                        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/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 6bddbe0..6af89fc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -80,6 +80,13 @@
             return this.baseIntent.getComponent();
         }
 
+        public String getPackageName() {
+            if (this.baseIntent.getComponent() != null) {
+                return this.baseIntent.getComponent().getPackageName();
+            }
+            return this.baseIntent.getPackage();
+        }
+
         @Override
         public boolean equals(Object o) {
             if (!(o instanceof TaskKey)) {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index a59c97e..a4f8d8c 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -24,11 +24,13 @@
 import android.graphics.RectF;
 import android.graphics.Region.Op;
 import android.os.AsyncTask;
+import android.os.Handler;
 import android.os.Trace;
 import android.service.wallpaper.WallpaperService;
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
 
@@ -45,6 +47,7 @@
     private static final String GL_LOG_TAG = "ImageWallpaperGL";
     private static final boolean DEBUG = false;
     private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
+    private static final long DELAY_FORGET_WALLPAPER = 5000;
 
     private WallpaperManager mWallpaperManager;
     private DrawableEngine mEngine;
@@ -69,6 +72,10 @@
     }
 
     class DrawableEngine extends Engine {
+        private final Runnable mUnloadWallpaperCallback = () -> {
+            unloadWallpaper(false /* forgetSize */);
+        };
+
         Bitmap mBackground;
         int mBackgroundWidth = -1, mBackgroundHeight = -1;
         int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
@@ -331,6 +338,7 @@
                 }
 
                 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
+                scheduleUnloadWallpaper();
             } finally {
                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
             }
@@ -433,6 +441,9 @@
                 mBackgroundHeight = -1;
             }
 
+            final Surface surface = getSurfaceHolder().getSurface();
+            surface.hwuiDestroy();
+
             mLoader = new AsyncTask<Void, Void, Bitmap>() {
                 @Override
                 protected Bitmap doInBackground(Void... params) {
@@ -442,6 +453,12 @@
             }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
         }
 
+        private void scheduleUnloadWallpaper() {
+            Handler handler = getMainThreadHandler();
+            handler.removeCallbacks(mUnloadWallpaperCallback);
+            handler.postDelayed(mUnloadWallpaperCallback, DELAY_FORGET_WALLPAPER);
+        }
+
         @Override
         protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
             super.dump(prefix, fd, out, args);
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index a4af6b2..8cff56d 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -50,6 +50,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
 
 /**
@@ -57,6 +58,8 @@
  */
 public class OverviewProxyService implements CallbackController<OverviewProxyListener>, Dumpable {
 
+    private static final String ACTION_QUICKSTEP = "android.intent.action.QUICKSTEP_SERVICE";
+
     public static final String TAG_OPS = "OverviewProxyService";
     public static final boolean DEBUG_OVERVIEW_PROXY = false;
     private static final long BACKOFF_MILLIS = 5000;
@@ -64,7 +67,7 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Runnable mConnectionRunnable = this::internalConnectToCurrentUser;
-    private final ComponentName mLauncherComponentName;
+    private final ComponentName mRecentsComponentName;
     private final DeviceProvisionedController mDeviceProvisionedController
             = Dependency.get(DeviceProvisionedController.class);
     private final List<OverviewProxyListener> mConnectionCallbacks = new ArrayList<>();
@@ -191,8 +194,8 @@
         mContext = context;
         mHandler = new Handler();
         mConnectionBackoffAttempts = 0;
-        mLauncherComponentName = ComponentName
-                .unflattenFromString(context.getString(R.string.config_overviewServiceComponent));
+        mRecentsComponentName = ComponentName.unflattenFromString(context.getString(
+                com.android.internal.R.string.config_recentsComponentName));
 
         // Listen for the package update changes.
         if (SystemServicesProxy.getInstance(context)
@@ -200,7 +203,7 @@
             mDeviceProvisionedController.addCallback(mDeviceProvisionedCallback);
             IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
             filter.addDataScheme("package");
-            filter.addDataSchemeSpecificPart(mLauncherComponentName.getPackageName(),
+            filter.addDataSchemeSpecificPart(mRecentsComponentName.getPackageName(),
                     PatternMatcher.PATTERN_LITERAL);
             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
             mContext.registerReceiver(mLauncherAddedReceiver, filter);
@@ -223,11 +226,16 @@
             return;
         }
         mHandler.removeCallbacks(mConnectionRunnable);
-        Intent launcherServiceIntent = new Intent();
-        launcherServiceIntent.setComponent(mLauncherComponentName);
-        boolean bound = mContext.bindServiceAsUser(launcherServiceIntent,
-                mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
-                UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+        Intent launcherServiceIntent = new Intent(ACTION_QUICKSTEP)
+                .setPackage(mRecentsComponentName.getPackageName());
+        boolean bound = false;
+        try {
+            bound = mContext.bindServiceAsUser(launcherServiceIntent,
+                    mOverviewServiceConnection, Context.BIND_AUTO_CREATE,
+                    UserHandle.of(mDeviceProvisionedController.getCurrentUser()));
+        } catch (SecurityException e) {
+            Log.e(TAG_OPS, "Unable to bind because of security error", e);
+        }
         if (!bound) {
             // Retry after exponential backoff timeout
             final long timeoutMs = (long) Math.scalb(BACKOFF_MILLIS, mConnectionBackoffAttempts);
@@ -247,6 +255,10 @@
         mConnectionCallbacks.remove(listener);
     }
 
+    public boolean shouldShowSwipeUpUI() {
+        return getProxy() != null && ((mInteractionFlags & FLAG_DISABLE_SWIPE_UP) == 0);
+    }
+
     public IOverviewProxy getProxy() {
         return mOverviewProxy;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index 1a9655e..2a27147 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -53,7 +53,8 @@
             Key.NUM_APPS_LAUNCHED,
             Key.HAS_SEEN_RECENTS_ONBOARDING,
             Key.SEEN_RINGER_GUIDANCE_COUNT,
-            Key.QS_HAS_TURNED_OFF_MOBILE_DATA
+            Key.QS_HAS_TURNED_OFF_MOBILE_DATA,
+            Key.TOUCHED_RINGER_TOGGLE
     })
     public @interface Key {
         @Deprecated
@@ -91,6 +92,7 @@
         String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount";
         String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed";
         String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData";
+        String TOUCHED_RINGER_TOGGLE = "TouchedRingerToggle";
     }
 
     public static boolean getBoolean(Context context, @Key String key, boolean defaultValue) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 2c0e95b..a61ce8c 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -29,30 +29,16 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.ArraySet;
-import android.util.TimingsTraceLog;
 import android.util.Log;
+import android.util.TimingsTraceLog;
 
-import com.android.systemui.globalactions.GlobalActionsComponent;
-import com.android.systemui.keyboard.KeyboardUI;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.media.RingtonePlayer;
-import com.android.systemui.pip.PipUI;
 import com.android.systemui.plugins.OverlayPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.PluginManager;
-import com.android.systemui.power.PowerUI;
-import com.android.systemui.recents.Recents;
-import com.android.systemui.shortcut.ShortcutKeyDispatcher;
-import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowManager;
-import com.android.systemui.usb.StorageNotification;
 import com.android.systemui.util.NotificationChannels;
-import com.android.systemui.util.leak.GarbageMonitor;
-import com.android.systemui.volume.VolumeUI;
 
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -99,6 +85,10 @@
                             mServices[i].onBootCompleted();
                         }
                     }
+
+                    IntentFilter localeChangedFilter = new IntentFilter(
+                            Intent.ACTION_LOCALE_CHANGED);
+                    registerReceiver(mLocaleChangeReceiver, localeChangedFilter);
                 }
             }, filter);
         } else {
@@ -249,4 +239,14 @@
     public SystemUI[] getServices() {
         return mServices;
     }
+
+    private final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                // Update names of SystemUi notification channels
+                NotificationChannels.createAll(context);
+            }
+        }
+    };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 391843c..039e7b5 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -31,6 +31,7 @@
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.qs.QSTileHost;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.NotificationEntryManager;
 import com.android.systemui.statusbar.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationListener;
@@ -130,6 +131,8 @@
         providers.put(NotificationGroupManager.class, NotificationGroupManager::new);
         providers.put(NotificationMediaManager.class, () -> new NotificationMediaManager(context));
         providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(context));
+        providers.put(NotificationBlockingHelperManager.class,
+                () -> new NotificationBlockingHelperManager(context));
         providers.put(NotificationRemoteInputManager.class,
                 () -> new NotificationRemoteInputManager(context));
         providers.put(SmartReplyConstants.class,
diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
index 245d240..9459ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java
@@ -21,6 +21,8 @@
 import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.statusbar.NotificationEntryManager;
+import com.android.systemui.statusbar.car.CarFacetButtonController;
+import com.android.systemui.statusbar.car.hvac.HvacController;
 
 /**
  * Class factory to provide car specific SystemUI components.
@@ -32,5 +34,7 @@
         super.injectDependencies(providers, context);
         providers.put(NotificationEntryManager.class,
                 () -> new CarNotificationEntryManager(context));
+        providers.put(CarFacetButtonController.class, () -> new CarFacetButtonController(context));
+        providers.put(HvacController.class, () -> new HvacController(context));
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
index 4b15fbc..3577c0f 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogImpl.java
@@ -18,8 +18,8 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.hardware.fingerprint.FingerprintDialog;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.biometrics.BiometricDialog;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -48,7 +48,7 @@
 
     private FingerprintDialogView mDialogView;
     private WindowManager mWindowManager;
-    private IFingerprintDialogReceiver mReceiver;
+    private IBiometricDialogReceiver mReceiver;
     private boolean mDialogShowing;
 
     private Handler mHandler = new Handler() {
@@ -97,7 +97,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
         if (DEBUG) Log.d(TAG, "showFingerprintDialog");
         // Remove these messages as they are part of the previous client
         mHandler.removeMessages(MSG_FINGERPRINT_ERROR);
@@ -139,7 +139,7 @@
             Log.w(TAG, "Dialog already showing");
             return;
         }
-        mReceiver = (IFingerprintDialogReceiver) args.arg2;
+        mReceiver = (IBiometricDialogReceiver) args.arg2;
         mDialogView.setBundle((Bundle)args.arg1);
         mWindowManager.addView(mDialogView, mDialogView.getLayoutParams());
         mDialogShowing = true;
@@ -147,6 +147,9 @@
 
     private void handleFingerprintAuthenticated() {
         if (DEBUG) Log.d(TAG, "handleFingerprintAuthenticated");
+        mDialogView.announceForAccessibility(
+                mContext.getResources().getText(
+                        com.android.internal.R.string.fingerprint_authenticated));
         handleHideDialog(false /* userCanceled */);
     }
 
@@ -174,7 +177,7 @@
         }
         if (userCanceled) {
             try {
-                mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_USER_CANCEL);
+                mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_USER_CANCEL);
             } catch (RemoteException e) {
                 Log.e(TAG, "RemoteException when hiding dialog", e);
             }
@@ -190,7 +193,7 @@
             return;
         }
         try {
-            mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_NEGATIVE);
+            mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_NEGATIVE);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling negative button", e);
         }
@@ -203,7 +206,7 @@
             return;
         }
         try {
-            mReceiver.onDialogDismissed(FingerprintDialog.DISMISSED_REASON_POSITIVE);
+            mReceiver.onDialogDismissed(BiometricDialog.DISMISSED_REASON_POSITIVE);
         } catch (RemoteException e) {
             Log.e(TAG, "Remote exception when handling positive button", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
index ebdc703..95258b0 100644
--- a/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/fingerprint/FingerprintDialogView.java
@@ -23,7 +23,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
-import android.hardware.fingerprint.FingerprintDialog;
+import android.hardware.biometrics.BiometricDialog;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -55,7 +55,8 @@
 
     private static final String TAG = "FingerprintDialogView";
 
-    private static final int ANIMATION_DURATION = 250; // ms
+    private static final int ANIMATION_DURATION_SHOW = 250; // ms
+    private static final int ANIMATION_DURATION_AWAY = 350; // ms
 
     private static final int STATE_NONE = 0;
     private static final int STATE_FINGERPRINT = 1;
@@ -162,14 +163,29 @@
         mLastState = STATE_NONE;
         updateFingerprintIcon(STATE_FINGERPRINT);
 
-        title.setText(mBundle.getCharSequence(FingerprintDialog.KEY_TITLE));
+        title.setText(mBundle.getCharSequence(BiometricDialog.KEY_TITLE));
         title.setSelected(true);
-        subtitle.setText(mBundle.getCharSequence(FingerprintDialog.KEY_SUBTITLE));
-        description.setText(mBundle.getCharSequence(FingerprintDialog.KEY_DESCRIPTION));
-        negative.setText(mBundle.getCharSequence(FingerprintDialog.KEY_NEGATIVE_TEXT));
+
+        final CharSequence subtitleText = mBundle.getCharSequence(BiometricDialog.KEY_SUBTITLE);
+        if (subtitleText == null) {
+            subtitle.setVisibility(View.GONE);
+        } else {
+            subtitle.setVisibility(View.VISIBLE);
+            subtitle.setText(subtitleText);
+        }
+
+        final CharSequence descriptionText = mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION);
+        if (descriptionText == null) {
+            subtitle.setVisibility(View.VISIBLE);
+            description.setVisibility(View.GONE);
+        } else {
+            description.setText(mBundle.getCharSequence(BiometricDialog.KEY_DESCRIPTION));
+        }
+
+        negative.setText(mBundle.getCharSequence(BiometricDialog.KEY_NEGATIVE_TEXT));
 
         final CharSequence positiveText =
-                mBundle.getCharSequence(FingerprintDialog.KEY_POSITIVE_TEXT);
+                mBundle.getCharSequence(BiometricDialog.KEY_POSITIVE_TEXT);
         positive.setText(positiveText); // needs to be set for marquee to work
         if (positiveText != null) {
             positive.setVisibility(View.VISIBLE);
@@ -185,13 +201,13 @@
             public void run() {
                 mLayout.animate()
                         .alpha(1f)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_SHOW)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
                 mDialog.animate()
                         .translationY(0)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_SHOW)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
@@ -221,13 +237,13 @@
             public void run() {
                 mLayout.animate()
                         .alpha(0f)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_AWAY)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .start();
                 mDialog.animate()
                         .translationY(mAnimationTranslationOffset)
-                        .setDuration(ANIMATION_DURATION)
+                        .setDuration(ANIMATION_DURATION_AWAY)
                         .setInterpolator(mLinearOutSlowIn)
                         .withLayer()
                         .withEndAction(endActionRunnable)
@@ -255,7 +271,7 @@
         mErrorText.setTextColor(mErrorTextColor);
         mErrorText.setContentDescription(message);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_CLEAR_MESSAGE),
-                FingerprintDialog.HIDE_DIALOG_DELAY);
+                BiometricDialog.HIDE_DIALOG_DELAY);
     }
 
     public void showHelpMessage(String message) {
@@ -265,7 +281,7 @@
     public void showErrorMessage(String error) {
         showTemporaryMessage(error);
         mHandler.sendMessageDelayed(mHandler.obtainMessage(FingerprintDialogImpl.MSG_HIDE_DIALOG,
-                false /* userCanceled */), FingerprintDialog.HIDE_DIALOG_DELAY);
+                false /* userCanceled */), BiometricDialog.HIDE_DIALOG_DELAY);
     }
 
     private void updateFingerprintIcon(int newState) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a1b17e4..d6e59c7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -27,7 +27,6 @@
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
@@ -235,6 +234,9 @@
     // answer whether the input should be restricted)
     private boolean mShowing;
 
+    // AOD is enabled and status bar is in AOD state.
+    private boolean mAodShowing;
+
     // display id of the secondary display on which we have put a keyguard window
     private int mSecondaryDisplayShowing = INVALID_DISPLAY;
 
@@ -664,7 +666,7 @@
         @Override
         public void onSecondaryDisplayShowingChanged(int displayId) {
             synchronized (KeyguardViewMediator.this) {
-                setShowingLocked(mShowing, displayId, false);
+                setShowingLocked(mShowing, mAodShowing, displayId, false);
             }
         }
     };
@@ -707,10 +709,10 @@
             setShowingLocked(!shouldWaitForProvisioning()
                     && !mLockPatternUtils.isLockScreenDisabled(
                             KeyguardUpdateMonitor.getCurrentUser()),
-                    mSecondaryDisplayShowing, true /* forceCallbacks */);
+                    mAodShowing, mSecondaryDisplayShowing, true /* forceCallbacks */);
         } else {
             // The system's keyguard is disabled or missing.
-            setShowingLocked(false, mSecondaryDisplayShowing, true);
+            setShowingLocked(false, mAodShowing, mSecondaryDisplayShowing, true);
         }
 
         mStatusBarKeyguardViewManager =
@@ -1311,7 +1313,7 @@
             if (mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
                 if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
                 // Without this, settings is not enabled until the lock screen first appears
-                setShowingLocked(false);
+                setShowingLocked(false, mAodShowing);
                 hideLocked();
                 return;
             }
@@ -1713,10 +1715,12 @@
         playSound(mTrustedSoundId);
     }
 
-    private void updateActivityLockScreenState(boolean showing, int secondaryDisplayShowing) {
+    private void updateActivityLockScreenState(boolean showing, boolean aodShowing,
+            int secondaryDisplayShowing) {
         mUiOffloadThread.submit(() -> {
             try {
-                ActivityManager.getService().setLockScreenShown(showing, secondaryDisplayShowing);
+                ActivityManager.getService().setLockScreenShown(showing, aodShowing,
+                        secondaryDisplayShowing);
             } catch (RemoteException e) {
             }
         });
@@ -1740,7 +1744,7 @@
                 if (DEBUG) Log.d(TAG, "handleShow");
             }
 
-            setShowingLocked(true);
+            setShowingLocked(true, mAodShowing);
             mStatusBarKeyguardViewManager.show(options);
             mHiding = false;
             mWakeAndUnlocking = false;
@@ -1849,7 +1853,7 @@
             }
 
             mWakeAndUnlocking = false;
-            setShowingLocked(false);
+            setShowingLocked(false, mAodShowing);
             mDismissCallbackRegistry.notifyDismissSucceeded();
             mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration);
             resetKeyguardDonePendingLocked();
@@ -1909,7 +1913,7 @@
         Trace.beginSection("KeyguardViewMediator#handleVerifyUnlock");
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
-            setShowingLocked(true);
+            setShowingLocked(true, mAodShowing);
             mStatusBarKeyguardViewManager.dismissAndCollapse();
         }
         Trace.endSection();
@@ -2064,6 +2068,10 @@
         pw.print("  mDrawnCallback: "); pw.println(mDrawnCallback);
     }
 
+    public void setAodShowing(boolean aodShowing) {
+        setShowingLocked(mShowing, aodShowing);
+    }
+
     private static class StartKeyguardExitAnimParams {
 
         long startTime;
@@ -2075,20 +2083,23 @@
         }
     }
 
-    private void setShowingLocked(boolean showing) {
-        setShowingLocked(showing, mSecondaryDisplayShowing, false /* forceCallbacks */);
+    private void setShowingLocked(boolean showing, boolean aodShowing) {
+        setShowingLocked(showing, aodShowing, mSecondaryDisplayShowing,
+                false /* forceCallbacks */);
     }
 
-    private void setShowingLocked(
-            boolean showing, int secondaryDisplayShowing, boolean forceCallbacks) {
-        final boolean notifyDefaultDisplayCallbacks = showing != mShowing || forceCallbacks;
+    private void setShowingLocked(boolean showing, boolean aodShowing, int secondaryDisplayShowing,
+            boolean forceCallbacks) {
+        final boolean notifyDefaultDisplayCallbacks = showing != mShowing
+                || aodShowing != mAodShowing || forceCallbacks;
         if (notifyDefaultDisplayCallbacks || secondaryDisplayShowing != mSecondaryDisplayShowing) {
             mShowing = showing;
+            mAodShowing = aodShowing;
             mSecondaryDisplayShowing = secondaryDisplayShowing;
             if (notifyDefaultDisplayCallbacks) {
                 notifyDefaultDisplayCallbacks(showing);
             }
-            updateActivityLockScreenState(showing, secondaryDisplayShowing);
+            updateActivityLockScreenState(showing, aodShowing, secondaryDisplayShowing);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3ba3d0e..5c0576d 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -284,14 +284,19 @@
                 // Defer the update of the current movement bounds until after the user finishes
                 // touching the screen
             } else {
-                final Rect toMovementBounds = mMenuState == MENU_STATE_FULL
-                        ? expandedMovementBounds
-                        : normalMovementBounds;
-                animateToOffset(animatingBounds, toMovementBounds,
-                        fromImeAdjustment,
-                        fromImeAdjustment ? mIsImeShowing : mIsShelfShowing,
-                        // Shelf height serves as an offset, but does not change movement bounds.
-                        fromImeAdjustment ? mImeOffset : mShelfHeight);
+                final int adjustedOffset = Math.max(mIsImeShowing ? mImeHeight + mImeOffset : 0,
+                        mIsShelfShowing ? mShelfHeight : 0);
+                Rect normalAdjustedBounds = new Rect();
+                mSnapAlgorithm.getMovementBounds(mNormalBounds, insetBounds, normalAdjustedBounds,
+                        adjustedOffset);
+                Rect expandedAdjustedBounds = new Rect();
+                mSnapAlgorithm.getMovementBounds(mExpandedBounds, insetBounds,
+                        expandedAdjustedBounds, adjustedOffset);
+                final Rect toAdjustedBounds = mMenuState == MENU_STATE_FULL
+                        ? expandedAdjustedBounds
+                        : normalAdjustedBounds;
+
+                animateToOffset(animatingBounds, toAdjustedBounds);
             }
         }
 
@@ -313,23 +318,13 @@
         }
     }
 
-    private void animateToOffset(Rect animatingBounds, Rect toMovementBounds,
-            boolean fromImeAdjustment, boolean showing, int offset) {
+    private void animateToOffset(Rect animatingBounds, Rect toAdjustedBounds) {
         final Rect bounds = new Rect(animatingBounds);
-        if (showing) {
-            // IME/shelf visible, apply the IME/shelf offset if the space allows for it
-            final int calculatedOffset = toMovementBounds.bottom - Math.max(toMovementBounds.top,
-                    toMovementBounds.bottom - offset);
-            bounds.offset(0,
-                    Math.min(0, toMovementBounds.bottom - calculatedOffset - bounds.top));
-        } else {
-            // IME/shelf hidden
-            if (bounds.top >= (mMovementBounds.bottom - offset)) {
-                bounds.offset(0, toMovementBounds.bottom - bounds.top -
-                        // Counter going back home from search where keyboard is up.
-                        (fromImeAdjustment ? mShelfHeight : 0));
-            }
+        if (toAdjustedBounds.bottom < mMovementBounds.bottom
+                && bounds.top < toAdjustedBounds.bottom) {
+            return;
         }
+        bounds.offset(0, toAdjustedBounds.bottom - bounds.top);
         mMotionHelper.animateToOffset(bounds);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 8d93157..49b00ce 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -21,9 +21,6 @@
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
-import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.media.AudioAttributes;
@@ -52,15 +49,18 @@
 
     private static final String TAG_BATTERY = "low_battery";
     private static final String TAG_TEMPERATURE = "high_temp";
+    private static final String TAG_AUTO_SAVER = "auto_saver";
 
     private static final int SHOWING_NOTHING = 0;
     private static final int SHOWING_WARNING = 1;
     private static final int SHOWING_INVALID_CHARGER = 3;
+    private static final int SHOWING_AUTO_SAVER_SUGGESTION = 4;
     private static final String[] SHOWING_STRINGS = {
         "SHOWING_NOTHING",
         "SHOWING_WARNING",
         "SHOWING_SAVER",
         "SHOWING_INVALID_CHARGER",
+        "SHOWING_AUTO_SAVER_SUGGESTION",
     };
 
     private static final String ACTION_SHOW_BATTERY_SETTINGS = "PNW.batterySettings";
@@ -74,6 +74,18 @@
             "PNW.dismissedThermalShutdownWarning";
     private static final String ACTION_SHOW_START_SAVER_CONFIRMATION =
             BatterySaverUtils.ACTION_SHOW_START_SAVER_CONFIRMATION;
+    private static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION =
+            BatterySaverUtils.ACTION_SHOW_AUTO_SAVER_SUGGESTION;
+    private static final String ACTION_DISMISS_AUTO_SAVER_SUGGESTION =
+            "PNW.dismissAutoSaverSuggestion";
+
+    private static final String ACTION_ENABLE_AUTO_SAVER =
+            "PNW.enableAutoSaver";
+    private static final String ACTION_AUTO_SAVER_NO_THANKS =
+            "PNW.autoSaverNoThanks";
+
+    private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
+            "android.settings.BATTERY_SAVER_SETTINGS";
 
     private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
@@ -98,9 +110,11 @@
     private long mLowWarningThreshold;
     private long mSevereWarningThreshold;
     private boolean mWarning;
+    private boolean mShowAutoSaverSuggestion;
     private boolean mPlaySound;
     private boolean mInvalidCharger;
     private SystemUIDialog mSaverConfirmation;
+    private SystemUIDialog mSaverEnabledConfirmation;
     private boolean mHighTempWarning;
     private SystemUIDialog mHighTempDialog;
     private SystemUIDialog mThermalShutdownDialog;
@@ -119,12 +133,19 @@
         pw.print("mInvalidCharger="); pw.println(mInvalidCharger);
         pw.print("mShowing="); pw.println(SHOWING_STRINGS[mShowing]);
         pw.print("mSaverConfirmation="); pw.println(mSaverConfirmation != null ? "not null" : null);
+        pw.print("mSaverEnabledConfirmation=");
+        pw.println(mSaverEnabledConfirmation != null ? "not null" : null);
         pw.print("mHighTempWarning="); pw.println(mHighTempWarning);
         pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null);
         pw.print("mThermalShutdownDialog=");
         pw.println(mThermalShutdownDialog != null ? "not null" : null);
     }
 
+    private int getLowBatteryAutoTriggerDefaultLevel() {
+        return mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryAutoTriggerDefaultLevel);
+    }
+
     @Override
     public void update(int batteryLevel, int bucket, long screenOffTime) {
         mBatteryLevel = batteryLevel;
@@ -151,7 +172,6 @@
         mSevereWarningThreshold = severeThreshold;
     }
 
-
     private void updateNotification() {
         if (DEBUG) Slog.d(TAG, "updateNotification mWarning=" + mWarning + " mPlaySound="
                 + mPlaySound + " mInvalidCharger=" + mInvalidCharger);
@@ -161,9 +181,14 @@
         } else if (mWarning) {
             showWarningNotification();
             mShowing = SHOWING_WARNING;
+        } else if (mShowAutoSaverSuggestion) {
+            showAutoSaverSuggestionNotification();
+            mShowing = SHOWING_AUTO_SAVER_SUGGESTION;
         } else {
             mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_BAD_CHARGER, UserHandle.ALL);
             mNoMan.cancelAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, UserHandle.ALL);
+            mNoMan.cancelAsUser(TAG_AUTO_SAVER,
+                    SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, UserHandle.ALL);
             mShowing = SHOWING_NOTHING;
         }
     }
@@ -229,6 +254,28 @@
         mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
     }
 
+    private void showAutoSaverSuggestionNotification() {
+        final Notification.Builder nb =
+                new Notification.Builder(mContext, NotificationChannels.HINTS)
+                        .setSmallIcon(R.drawable.ic_power_saver)
+                        .setWhen(0)
+                        .setShowWhen(false)
+                        .setContentTitle(mContext.getString(R.string.auto_saver_title))
+                        .setContentText(mContext.getString(R.string.auto_saver_text,
+                                getLowBatteryAutoTriggerDefaultLevel()));
+        nb.setContentIntent(pendingBroadcast(ACTION_ENABLE_AUTO_SAVER));
+        nb.setDeleteIntent(pendingBroadcast(ACTION_DISMISS_AUTO_SAVER_SUGGESTION));
+        nb.addAction(0,
+                mContext.getString(R.string.no_auto_saver_action),
+                pendingBroadcast(ACTION_AUTO_SAVER_NO_THANKS));
+
+        SystemUI.overrideNotificationAppName(mContext, nb, false);
+
+        final Notification n = nb.build();
+        mNoMan.notifyAsUser(
+                TAG_AUTO_SAVER, SystemMessage.NOTE_AUTO_SAVER_SUGGESTION, n, UserHandle.ALL);
+    }
+
     private String getHybridContentString(String percentage) {
         return PowerUtil.getBatteryRemainingStringFormatted(
             mContext,
@@ -238,8 +285,8 @@
     }
 
     private PendingIntent pendingBroadcast(String action) {
-        return PendingIntent.getBroadcastAsUser(mContext,
-                0, new Intent(action), 0, UserHandle.CURRENT);
+        return PendingIntent.getBroadcastAsUser(mContext, 0,
+                new Intent(action).setPackage(mContext.getPackageName()), 0, UserHandle.CURRENT);
     }
 
     private static Intent settings(String action) {
@@ -394,6 +441,16 @@
         updateNotification();
     }
 
+    private void showAutoSaverSuggestion() {
+        mShowAutoSaverSuggestion = true;
+        updateNotification();
+    }
+
+    private void dismissAutoSaverSuggestion() {
+        mShowAutoSaverSuggestion = false;
+        updateNotification();
+    }
+
     @Override
     public void userSwitched() {
         updateNotification();
@@ -405,22 +462,53 @@
         d.setTitle(R.string.battery_saver_confirmation_title);
         d.setMessage(com.android.internal.R.string.battery_saver_description);
         d.setNegativeButton(android.R.string.cancel, null);
-        d.setPositiveButton(R.string.battery_saver_confirmation_ok, mStartSaverModeNoConfirmation);
+        d.setPositiveButton(R.string.battery_saver_confirmation_ok,
+                (dialog, which) -> setSaverMode(true, false));
         d.setShowForAllUsers(true);
-        d.setOnDismissListener(new OnDismissListener() {
-            @Override
-            public void onDismiss(DialogInterface dialog) {
-                mSaverConfirmation = null;
-            }
-        });
+        d.setOnDismissListener((dialog) -> mSaverConfirmation = null);
         d.show();
         mSaverConfirmation = d;
     }
 
+    private void showAutoSaverEnabledConfirmation() {
+        if (mSaverEnabledConfirmation != null) return;
+
+        // Open the Battery Saver setting page.
+        final Intent actionBatterySaverSetting =
+                new Intent(SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+        final SystemUIDialog d = new SystemUIDialog(mContext);
+        d.setTitle(R.string.auto_saver_enabled_title);
+        d.setMessage(mContext.getString(R.string.auto_saver_enabled_text,
+                getLowBatteryAutoTriggerDefaultLevel()));
+
+        // Negative == "got it". Just close the dialog. Battery saver has already been enabled.
+        d.setNegativeButton(R.string.auto_saver_okay_action, null);
+        d.setPositiveButton(R.string.open_saver_setting_action, (dialog, which) ->
+                mContext.startActivity(actionBatterySaverSetting));
+        d.setShowForAllUsers(true);
+        d.setOnDismissListener((dialog) -> mSaverEnabledConfirmation = null);
+        d.show();
+        mSaverEnabledConfirmation = d;
+    }
+
+
     private void setSaverMode(boolean mode, boolean needFirstTimeWarning) {
         BatterySaverUtils.setPowerSaveMode(mContext, mode, needFirstTimeWarning);
     }
 
+    private void scheduleAutoBatterySaver() {
+        int autoTriggerThreshold = mContext.getResources().getInteger(
+                com.android.internal.R.integer.config_lowBatteryWarningLevel);
+        if (autoTriggerThreshold == 0) {
+            autoTriggerThreshold = 15;
+        }
+
+        BatterySaverUtils.scheduleAutoBatterySaver(mContext, autoTriggerThreshold);
+        showAutoSaverEnabledConfirmation();
+    }
+
     private final class Receiver extends BroadcastReceiver {
 
         public void init() {
@@ -433,6 +521,9 @@
             filter.addAction(ACTION_CLICKED_THERMAL_SHUTDOWN_WARNING);
             filter.addAction(ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING);
             filter.addAction(ACTION_SHOW_START_SAVER_CONFIRMATION);
+            filter.addAction(ACTION_SHOW_AUTO_SAVER_SUGGESTION);
+            filter.addAction(ACTION_ENABLE_AUTO_SAVER);
+            filter.addAction(ACTION_AUTO_SAVER_NO_THANKS);
             mContext.registerReceiverAsUser(this, UserHandle.ALL, filter,
                     android.Manifest.permission.DEVICE_POWER, mHandler);
         }
@@ -462,10 +553,17 @@
                 showThermalShutdownDialog();
             } else if (ACTION_DISMISSED_THERMAL_SHUTDOWN_WARNING.equals(action)) {
                 dismissThermalShutdownWarning();
+            } else if (ACTION_SHOW_AUTO_SAVER_SUGGESTION.equals(action)) {
+                showAutoSaverSuggestion();
+            } else if (ACTION_DISMISS_AUTO_SAVER_SUGGESTION.equals(action)) {
+                dismissAutoSaverSuggestion();
+            } else if (ACTION_ENABLE_AUTO_SAVER.equals(action)) {
+                dismissAutoSaverSuggestion();
+                scheduleAutoBatterySaver();
+            } else if (ACTION_AUTO_SAVER_NO_THANKS.equals(action)) {
+                dismissAutoSaverSuggestion();
+                BatterySaverUtils.suppressAutoBatterySaver(context);
             }
         }
     }
-
-    private final OnClickListener mStartSaverModeNoConfirmation =
-            (dialog, which) -> setSaverMode(true, false);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index f08219a..c409f73 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -52,6 +52,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.time.Duration;
 import java.util.Arrays;
 
 public class PowerUI extends SystemUI {
@@ -61,6 +62,8 @@
     private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
     private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
     static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
+    private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
+    private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
 
     private final Handler mHandler = new Handler();
     private final Receiver mReceiver = new Receiver();
@@ -69,7 +72,6 @@
     private HardwarePropertiesManager mHardwarePropertiesManager;
     private WarningsUI mWarnings;
     private final Configuration mLastConfiguration = new Configuration();
-    private int mBatteryLevel = 100;
     private long mTimeRemaining = Long.MAX_VALUE;
     private int mPlugType = 0;
     private int mInvalidCharger = 0;
@@ -88,6 +90,7 @@
     private long mNextLogTime;
     private IThermalService mThermalService;
 
+    @VisibleForTesting int mBatteryLevel = 100;
     @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
 
     // by using the same instance (method references are not guaranteed to be the same object
@@ -205,12 +208,6 @@
 
                 final boolean plugged = mPlugType != 0;
                 final boolean oldPlugged = oldPlugType != 0;
-                // if we are now unplugged but we were previously plugged in we should allow the
-                // time based trigger again.
-                if (!plugged && plugged != oldPlugged) {
-                    mLowWarningShownThisChargeCycle = false;
-                    mSevereWarningShownThisChargeCycle = false;
-                }
 
                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
                 int bucket = findBatteryLevelBucket(mBatteryLevel);
@@ -261,7 +258,8 @@
         boolean isPowerSaver = mPowerManager.isPowerSaveMode();
         // only play SFX when the dialog comes up or the bucket changes
         final boolean playSound = bucket != oldBucket || oldPlugged;
-        if (mEnhancedEstimates.isHybridNotificationEnabled()) {
+        final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled();
+        if (hybridEnabled) {
             final Estimate estimate = mEnhancedEstimates.getEstimate();
             // Turbo is not always booted once SysUI is running so we have ot make sure we actually
             // get data back
@@ -270,6 +268,14 @@
                 mWarnings.updateEstimate(estimate);
                 mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(),
                         mEnhancedEstimates.getSevereWarningThreshold());
+
+                // if we are now over 45% battery & 6 hours remaining we can trigger hybrid
+                // notification again
+                if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET
+                        && mTimeRemaining > SIX_HOURS_MILLIS) {
+                    mLowWarningShownThisChargeCycle = false;
+                    mSevereWarningShownThisChargeCycle = false;
+                }
             }
         }
 
@@ -277,13 +283,15 @@
                 mTimeRemaining, isPowerSaver, mBatteryStatus)) {
             mWarnings.showLowBatteryWarning(playSound);
 
-            // mark if we've already shown a warning this cycle. This will prevent the time based
-            // trigger from spamming users since the time remaining can vary based on current
-            // device usage.
-            if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()) {
-                mSevereWarningShownThisChargeCycle = true;
-            } else {
-                mLowWarningShownThisChargeCycle = true;
+            // mark if we've already shown a warning this cycle. This will prevent the notification
+            // trigger from spamming users by only showing low/critical warnings once per cycle
+            if (hybridEnabled) {
+                if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
+                        || mBatteryLevel < mLowBatteryReminderLevels[1]) {
+                    mSevereWarningShownThisChargeCycle = true;
+                } else {
+                    mLowWarningShownThisChargeCycle = true;
+                }
             }
         } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining,
                 isPowerSaver)) {
@@ -295,12 +303,16 @@
 
     @VisibleForTesting
     boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket,
-            int bucket, long timeRemaining, boolean isPowerSaver, int mBatteryStatus) {
+            int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) {
+        if (mEnhancedEstimates.isHybridNotificationEnabled()) {
+            // triggering logic when enhanced estimate is available
+            return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus);
+        }
+        // legacy triggering logic
         return !plugged
                 && !isPowerSaver
-                && (((bucket < oldBucket || oldPlugged) && bucket < 0)
-                        || isTimeBasedTrigger(timeRemaining))
-                && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
+                && (((bucket < oldBucket || oldPlugged) && bucket < 0))
+                && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN;
     }
 
     @VisibleForTesting
@@ -315,19 +327,23 @@
                         || hybridWouldDismiss));
     }
 
-    private boolean isTimeBasedTrigger(long timeRemaining) {
-        if (!mEnhancedEstimates.isHybridNotificationEnabled()) {
+    private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver,
+            int batteryStatus) {
+        if (plugged || isPowerSaver || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) {
             return false;
         }
+        int warnLevel = mLowBatteryReminderLevels[0];
+        int critLevel = mLowBatteryReminderLevels[1];
 
-        // Only show the time based warning once per charge cycle
-        final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
-                && !mLowWarningShownThisChargeCycle;
+        // Only show the low warning once per charge cycle
+        final boolean canShowWarning = !mLowWarningShownThisChargeCycle
+                && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold()
+                        || mBatteryLevel <= warnLevel);
 
-        // Only show the severe time based warning once per charge cycle
-        final boolean canShowSevereWarning =
-                timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
-                        && !mSevereWarningShownThisChargeCycle;
+        // Only show the severe warning once per charge cycle
+        final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
+                && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
+                        || mBatteryLevel <= critLevel);
 
         return canShowWarning || canShowSevereWarning;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index e532217..018a635 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -360,7 +360,6 @@
         // The customize state changed, so our height changed.
         mContainer.updateExpansion();
         mQSPanel.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
-        mHeader.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
         mFooter.setVisibility(!mQSCustomizer.isCustomizing() ? View.VISIBLE : View.INVISIBLE);
         // Let the panel know the position changed and it needs to update where notifications
         // and whatnot are.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 2270b60..c9c04d9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -17,6 +17,7 @@
 package com.android.systemui.qs;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
@@ -179,16 +180,56 @@
 
         protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
         private boolean mListening;
+        /** Size of the QS tile (width & height). */
+        private int mTileDimensionSize;
 
         public HeaderTileLayout(Context context) {
             super(context);
             setClipChildren(false);
             setClipToPadding(false);
-            setGravity(Gravity.CENTER_VERTICAL);
+
+            mTileDimensionSize = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.qs_quick_tile_size);
+
+            setGravity(Gravity.CENTER);
             setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
         }
 
         @Override
+        protected void onConfigurationChanged(Configuration newConfig) {
+            super.onConfigurationChanged(newConfig);
+
+            setGravity(Gravity.CENTER);
+            LayoutParams staticSpaceLayoutParams = generateSpaceLayoutParams(
+                    mContext.getResources().getDimensionPixelSize(
+                            R.dimen.qs_quick_tile_space_width));
+
+            // Update space params since they fill any open space in portrait orientation and have
+            // a static width in landscape orientation.
+            final int childViewCount = getChildCount();
+            for (int i = 0; i < childViewCount; i++) {
+                View childView = getChildAt(i);
+                if (childView instanceof Space) {
+                    childView.setLayoutParams(staticSpaceLayoutParams);
+                }
+            }
+        }
+
+        /**
+         * Returns {@link LayoutParams} based on the given {@code spaceWidth}. If the width is 0,
+         * then we're going to have the space expand to take up as much space as possible. If the
+         * width is non-zero, we want the inter-tile spacers to be fixed.
+         */
+        private LayoutParams generateSpaceLayoutParams(int spaceWidth) {
+            LayoutParams lp = new LayoutParams(spaceWidth, mTileDimensionSize);
+            if (spaceWidth == 0) {
+                lp.weight = 1;
+            }
+            lp.gravity = Gravity.CENTER;
+            return lp;
+        }
+
+        @Override
         public void setListening(boolean listening) {
             if (mListening == listening) return;
             mListening = listening;
@@ -200,25 +241,22 @@
         @Override
         public void addTile(TileRecord tile) {
             if (getChildCount() != 0) {
-                // Add a spacer.
-                addView(new Space(mContext), getChildCount(), generateSpaceParams());
+                // Add a spacer between tiles. We want static-width spaces if we're in landscape to
+                // keep the tiles close. For portrait, we stick with spaces that fill up any
+                // available space.
+                LayoutParams spaceLayoutParams = generateSpaceLayoutParams(
+                        mContext.getResources().getDimensionPixelSize(
+                                R.dimen.qs_quick_tile_space_width));
+                addView(new Space(mContext), getChildCount(), spaceLayoutParams);
             }
-            addView(tile.tileView, getChildCount(), generateLayoutParams());
+
+            addView(tile.tileView, getChildCount(), generateTileLayoutParams());
             mRecords.add(tile);
             tile.tile.setListening(this, mListening);
         }
 
-        private LayoutParams generateSpaceParams() {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(0, size);
-            lp.weight = 1;
-            lp.gravity = Gravity.CENTER;
-            return lp;
-        }
-
-        private LayoutParams generateLayoutParams() {
-            int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size);
-            LayoutParams lp = new LayoutParams(size, size);
+        private LayoutParams generateTileLayoutParams() {
+            LayoutParams lp = new LayoutParams(mTileDimensionSize, mTileDimensionSize);
             lp.gravity = Gravity.CENTER;
             return lp;
         }
@@ -237,8 +275,8 @@
         }
 
         private int getChildIndex(QSTileView tileView) {
-            final int N = getChildCount();
-            for (int i = 0; i < N; i++) {
+            final int childViewCount = getChildCount();
+            for (int i = 0; i < childViewCount; i++) {
                 if (getChildAt(i) == tileView) {
                     return i;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 9792e41..df65d1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -21,21 +21,24 @@
 import android.annotation.ColorInt;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.Rect;
+import android.media.AudioManager;
 import android.os.Handler;
 import android.provider.AlarmClock;
 import android.support.annotation.VisibleForTesting;
-import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
 import android.view.View;
 import android.view.WindowInsets;
+import android.widget.ImageView;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
 
@@ -44,7 +47,6 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
-import com.android.systemui.R.id;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.qs.QSDetail.Callback;
@@ -91,18 +93,33 @@
     private View mQuickQsStatusIcons;
     private View mDate;
     private View mHeaderTextContainerView;
-    /** View corresponding to the next alarm info (including the icon). */
-    private View mNextAlarmView;
+    /** View containing the next alarm and ringer mode info. */
+    private View mStatusContainer;
     /** Tooltip for educating users that they can long press on icons to see more details. */
     private View mLongPressTooltipView;
+
+    private int mRingerMode = AudioManager.RINGER_MODE_NORMAL;
+    private AlarmManager.AlarmClockInfo mNextAlarm;
+
+    private ImageView mNextAlarmIcon;
     /** {@link TextView} containing the actual text indicating when the next alarm will go off. */
     private TextView mNextAlarmTextView;
+    private View mStatusSeparator;
+    private ImageView mRingerModeIcon;
+    private TextView mRingerModeTextView;
 
     private NextAlarmController mAlarmController;
-    private String mNextAlarmText;
     /** Counts how many times the long press tooltip has been shown to the user. */
     private int mShownCount;
 
+    private final BroadcastReceiver mRingerReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mRingerMode = intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1);
+            updateStatusText();
+        }
+    };
+
     /**
      * Runnable for automatically fading out the long press tooltip (as if it were animating away).
      */
@@ -110,7 +127,6 @@
 
     public QuickStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
-
         mAlarmController = Dependency.get(NextAlarmController.class);
         mShownCount = getStoredShownCount();
     }
@@ -128,8 +144,12 @@
         // Views corresponding to the header info section (e.g. tooltip and next alarm).
         mHeaderTextContainerView = findViewById(R.id.header_text_container);
         mLongPressTooltipView = findViewById(R.id.long_press_tooltip);
-        mNextAlarmView = findViewById(R.id.next_alarm);
+        mStatusContainer = findViewById(R.id.status_container);
+        mStatusSeparator = findViewById(R.id.status_separator);
+        mNextAlarmIcon = findViewById(R.id.next_alarm_icon);
         mNextAlarmTextView = findViewById(R.id.next_alarm_text);
+        mRingerModeIcon = findViewById(R.id.ringer_mode_icon);
+        mRingerModeTextView = findViewById(R.id.ringer_mode_text);
 
         updateResources();
 
@@ -140,7 +160,6 @@
 
         // Set light text on the header icons because they will always be on a black background
         applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
-        applyDarkness(id.signal_cluster, tintArea, intensity, colorForeground);
 
         // Set the correct tint for the status icons so they contrast
         mIconManager.setTint(fillColor);
@@ -149,6 +168,32 @@
         battery.setForceShowPercent(true);
     }
 
+    private void updateStatusText() {
+        boolean ringerVisible = false;
+        if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_vibrate);
+            mRingerModeTextView.setText(R.string.volume_ringer_status_vibrate);
+            ringerVisible = true;
+        } else if (mRingerMode == AudioManager.RINGER_MODE_SILENT) {
+            mRingerModeIcon.setImageResource(R.drawable.stat_sys_ringer_silent);
+            mRingerModeTextView.setText(R.string.volume_ringer_status_silent);
+            ringerVisible = true;
+        }
+        mRingerModeIcon.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
+        mRingerModeTextView.setVisibility(ringerVisible ? View.VISIBLE : View.GONE);
+
+        boolean alarmVisible = false;
+        if (mNextAlarm != null) {
+            alarmVisible = true;
+            mNextAlarmTextView.setText(formatNextAlarm(mNextAlarm));
+        }
+        mNextAlarmIcon.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
+        mNextAlarmTextView.setVisibility(alarmVisible ? View.VISIBLE : View.GONE);
+        mStatusSeparator.setVisibility(alarmVisible && ringerVisible ? View.VISIBLE : View.GONE);
+        updateTooltipShow();
+    }
+
+
     private void applyDarkness(int id, Rect tintArea, float intensity, int color) {
         View v = findViewById(id);
         if (v instanceof DarkReceiver) {
@@ -296,8 +341,11 @@
 
         if (listening) {
             mAlarmController.addCallback(this);
+            mContext.registerReceiver(mRingerReceiver,
+                    new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
         } else {
             mAlarmController.removeCallback(this);
+            mContext.unregisterReceiver(mRingerReceiver);
         }
     }
 
@@ -311,23 +359,31 @@
 
     @Override
     public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) {
-        mNextAlarmText = nextAlarm != null ? formatNextAlarm(nextAlarm) : null;
+        mNextAlarm = nextAlarm;
+        updateStatusText();
+    }
 
-        if (mNextAlarmText != null) {
-            hideLongPressTooltip(true /* shouldFadeInAlarmText */);
+    private void updateTooltipShow() {
+        if (hasStatusText()) {
+            hideLongPressTooltip(true /* shouldShowStatusText */);
         } else {
-            hideAlarmText();
+            hideStatusText();
         }
         updateHeaderTextContainerAlphaAnimator();
     }
 
+    private boolean hasStatusText() {
+        return mNextAlarmTextView.getVisibility() == View.VISIBLE
+                || mRingerModeTextView.getVisibility() == View.VISIBLE;
+    }
+
     /**
      * Animates in the long press tooltip (as long as the next alarm text isn't currently occupying
      * the space).
      */
     public void showLongPressTooltip() {
-        // If we have alarm text to show, don't bother fading in the tooltip.
-        if (!TextUtils.isEmpty(mNextAlarmText)) {
+        // If we have status text to show, don't bother fading in the tooltip.
+        if (hasStatusText()) {
             return;
         }
 
@@ -357,11 +413,11 @@
 
     /**
      * Fades out the long press tooltip if it's partially visible - short circuits any running
-     * animation. Additionally has the ability to fade in the alarm info text.
+     * animation. Additionally has the ability to fade in the status info text.
      *
-     * @param shouldShowAlarmText whether we should fade in the next alarm text
+     * @param shouldShowStatusText whether we should fade in the status text
      */
-    private void hideLongPressTooltip(boolean shouldShowAlarmText) {
+    private void hideLongPressTooltip(boolean shouldShowStatusText) {
         mLongPressTooltipView.animate().cancel();
         if (mLongPressTooltipView.getVisibility() == View.VISIBLE
                 && mLongPressTooltipView.getAlpha() != 0f) {
@@ -375,44 +431,40 @@
                             if (DEBUG) Log.d(TAG, "hideLongPressTooltip: Hid long press tip");
                             mLongPressTooltipView.setVisibility(View.INVISIBLE);
 
-                            if (shouldShowAlarmText) {
-                                showAlarmText();
+                            if (shouldShowStatusText) {
+                                showStatus();
                             }
                         }
                     })
                     .start();
         } else {
             mLongPressTooltipView.setVisibility(View.INVISIBLE);
-            if (shouldShowAlarmText) {
-                showAlarmText();
+            if (shouldShowStatusText) {
+                showStatus();
             }
         }
     }
 
     /**
-     * Fades in the updated alarm text. Note that if there's already an alarm showing, this will
-     * immediately hide it and fade in the updated time.
+     * Fades in the updated status text. Note that if there's already a status showing, this will
+     * immediately hide it and fade in the updated status.
      */
-    private void showAlarmText() {
-        mNextAlarmView.setAlpha(0f);
-        mNextAlarmView.setVisibility(View.VISIBLE);
-        mNextAlarmTextView.setText(mNextAlarmText);
+    private void showStatus() {
+        mStatusContainer.setAlpha(0f);
+        mStatusContainer.setVisibility(View.VISIBLE);
 
         // Animate the alarm back in. Make sure to clear the animator listener for the animation!
-        mNextAlarmView.animate()
+        mStatusContainer.animate()
                 .alpha(1f)
                 .setDuration(FADE_ANIMATION_DURATION_MS)
                 .setListener(null)
                 .start();
     }
 
-    /**
-     * Fades out and hides the next alarm text. This also resets the text contents to null in
-     * preparation for the next alarm update.
-     */
-    private void hideAlarmText() {
-        if (mNextAlarmView.getVisibility() == View.VISIBLE) {
-            mNextAlarmView.animate()
+    /** Fades out and hides the status text. */
+    private void hideStatusText() {
+        if (mStatusContainer.getVisibility() == View.VISIBLE) {
+            mStatusContainer.animate()
                     .alpha(0f)
                     .setListener(new AnimatorListenerAdapter() {
                         @Override
@@ -421,15 +473,11 @@
 
                             // Reset the alpha regardless of how the animation ends for the next
                             // time we show this view/want to animate it.
-                            mNextAlarmView.setVisibility(View.INVISIBLE);
-                            mNextAlarmView.setAlpha(1f);
-                            mNextAlarmTextView.setText(null);
+                            mStatusContainer.setVisibility(View.INVISIBLE);
+                            mStatusContainer.setAlpha(1f);
                         }
                     })
                     .start();
-        } else {
-            // Next alarm view is already hidden, only need to clear the text.
-            mNextAlarmTextView.setText(null);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index 1cb89c4..64e7a63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -23,6 +23,7 @@
     protected int mCellHeight;
     protected int mCellMarginHorizontal;
     protected int mCellMarginVertical;
+    protected int mSidePadding;
 
     protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
     private int mCellMarginTop;
@@ -80,6 +81,7 @@
         mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal);
         mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical);
         mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top);
+        mSidePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_layout_margin_side);
         if (mColumns != columns) {
             mColumns = columns;
             requestLayout();
@@ -93,7 +95,7 @@
         final int numTiles = mRecords.size();
         final int width = MeasureSpec.getSize(widthMeasureSpec);
         final int numRows = (numTiles + mColumns - 1) / mColumns;
-        mCellWidth = (width - (mCellMarginHorizontal * (mColumns + 1))) / mColumns;
+        mCellWidth = (width - mSidePadding * 2 - (mCellMarginHorizontal * mColumns)) / mColumns;
 
         // Measure each QS tile.
         View previousView = this;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
index 6797bb9..31c455d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java
@@ -19,7 +19,7 @@
 import android.support.annotation.IdRes;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.RelativeLayout;
+import android.widget.LinearLayout;
 
 import com.android.settingslib.Utils;
 import com.android.systemui.BatteryMeterView;
@@ -30,7 +30,7 @@
  * A view that forms the header of the notification panel. This view will ensure that any
  * status icons that are displayed are tinted accordingly to the current theme.
  */
-public class CarStatusBarHeader extends RelativeLayout {
+public class CarStatusBarHeader extends LinearLayout {
     public CarStatusBarHeader(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -44,7 +44,6 @@
         float intensity = colorForeground == Color.WHITE ? 0f : 1f;
         Rect tintArea = new Rect(0, 0, 0, 0);
 
-        applyDarkness(R.id.signal_cluster, tintArea, intensity, colorForeground);
         applyDarkness(R.id.battery, tintArea, intensity, colorForeground);
         applyDarkness(R.id.clock, tintArea, intensity, colorForeground);
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index a3d6c6c..4aa83d0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Point;
 import android.os.Bundle;
 import android.support.v7.widget.DefaultItemAnimator;
 import android.support.v7.widget.GridLayoutManager;
@@ -37,6 +38,7 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.plugins.qs.QS;
@@ -81,10 +83,9 @@
 
     public QSCustomizer(Context context, AttributeSet attrs) {
         super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
-        mClipper = new QSDetailClipper(this);
 
         LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
-
+        mClipper = new QSDetailClipper(findViewById(R.id.customize_container));
         mToolbar = findViewById(com.android.internal.R.id.action_bar);
         TypedValue value = new TypedValue();
         mContext.getTheme().resolveAttribute(android.R.attr.homeAsUpIndicator, value, true);
@@ -100,7 +101,10 @@
         mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0,
                 mContext.getString(com.android.internal.R.string.reset));
         mToolbar.setTitle(R.string.qs_edit);
-
+        int accentColor = Utils.getColorAttr(context, android.R.attr.colorAccent);
+        mToolbar.setTitleTextColor(accentColor);
+        mToolbar.getNavigationIcon().setTint(accentColor);
+        mToolbar.getOverflowIcon().setTint(accentColor);
         mRecyclerView = findViewById(android.R.id.list);
         mTileAdapter = new TileAdapter(getContext());
         mTileQueryHelper = new TileQueryHelper(context, mTileAdapter);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 943a176..6f664d7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -102,7 +102,9 @@
             mTokenMap.remove(service.getToken());
             mTiles.remove(tile.getComponent());
             final String slot = tile.getComponent().getClassName();
-            mMainHandler.post(() -> mHost.getIconController().removeIcon(slot));
+            // TileServices doesn't know how to add more than 1 icon per slot, so remove all
+            mMainHandler.post(() -> mHost.getIconController()
+                    .removeAllIconsForSlot(slot));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index a9defc8..09d928f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -194,6 +194,7 @@
         setClickable(state.state != Tile.STATE_UNAVAILABLE);
         mIcon.setIcon(state);
         setContentDescription(state.contentDescription);
+
         mAccessibilityClass = state.expandedAccessibilityClassName;
         if (state instanceof QSTile.BooleanState) {
             boolean newState = ((BooleanState) state).value;
@@ -269,6 +270,10 @@
                 info.setText(label);
                 info.setChecked(b);
                 info.setCheckable(true);
+                info.addAction(
+                        new AccessibilityNodeInfo.AccessibilityAction(
+                                AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(),
+                                getResources().getString(R.string.accessibility_long_click_tile)));
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 674ccd8..0f85c5b 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -492,6 +492,13 @@
                     }
                 }
                 mDraggingInRecentsCurrentUser = currentUser;
+
+                if (mOverviewProxyService.getProxy() != null) {
+                    // The overview service is handling split screen, so just skip the wait for the
+                    // first draw and notify the divider to start animating now
+                    EventBus.getDefault().post(new RecentsDrawnEvent());
+                }
+
                 return true;
             } else {
                 EventBus.getDefault().send(new ShowUserToastEvent(
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 1596d12..3d8e037 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -592,7 +592,16 @@
 
             // Record last snap target the divider moved to
             if (mHomeStackResizable && !mIsInMinimizeInteraction) {
-                saveSnapTargetBeforeMinimized(snapTarget);
+                // The last snapTarget position can be negative when the last divider position was
+                // offscreen. In that case, save the middle (default) SnapTarget so calculating next
+                // position isn't negative.
+                final SnapTarget saveTarget;
+                if (snapTarget.position < 0) {
+                    saveTarget = mSnapAlgorithm.getMiddleTarget();
+                } else {
+                    saveTarget = snapTarget;
+                }
+                saveSnapTargetBeforeMinimized(saveTarget);
             }
         };
         Runnable notCancelledEndAction = () -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index fa177f2..65037f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -18,7 +18,7 @@
 
 import android.content.ComponentName;
 import android.graphics.Rect;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -160,7 +160,7 @@
 
         default void onRotationProposal(int rotation, boolean isValid) { }
 
-        default void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) { }
+        default void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) { }
         default void onFingerprintAuthenticated() { }
         default void onFingerprintHelp(String message) { }
         default void onFingerprintError(String error) { }
@@ -513,7 +513,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
         synchronized (mLock) {
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = bundle;
@@ -759,7 +759,7 @@
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).showFingerprintDialog(
                                 (Bundle)((SomeArgs)msg.obj).arg1,
-                                (IFingerprintDialogReceiver)((SomeArgs)msg.obj).arg2);
+                                (IBiometricDialogReceiver)((SomeArgs)msg.obj).arg2);
                     }
                     break;
                 case MSG_FINGERPRINT_AUTHENTICATED:
@@ -810,4 +810,3 @@
         }
     }
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 24ebc83..3ece2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,7 +24,10 @@
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
+import android.app.NotificationChannel;
 import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Path;
@@ -34,11 +37,11 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.os.Bundle;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
+import android.util.Log;
 import android.util.MathUtils;
 import android.util.Property;
 import android.view.KeyEvent;
@@ -90,12 +93,17 @@
 import java.util.function.BooleanSupplier;
 import java.util.function.Consumer;
 
+/**
+ * View representing a notification item - this can be either the individual child notification or
+ * the group summary (which contains 1 or more child notifications).
+ */
 public class ExpandableNotificationRow extends ActivatableNotificationView
         implements PluginListener<NotificationMenuRowPlugin> {
 
     private static final int DEFAULT_DIVIDER_ALPHA = 0x29;
     private static final int COLORED_DIVIDER_ALPHA = 0x7B;
     private static final int MENU_VIEW_INDEX = 0;
+    private static final String TAG = "ExpandableNotifRow";
 
     public interface LayoutListener {
         public void onLayout();
@@ -127,6 +135,8 @@
     private boolean mHasUserChangedExpansion;
     /** If {@link #mHasUserChangedExpansion}, has the user expanded this row */
     private boolean mUserExpanded;
+    /** Whether the blocking helper is showing on this notification (even if dismissed) */
+    private boolean mIsBlockingHelperShowing;
 
     /**
      * Has this notification been expanded while it was pinned
@@ -164,6 +174,8 @@
     private NotificationGuts mGuts;
     private NotificationData.Entry mEntry;
     private StatusBarNotification mStatusBarNotification;
+    private PackageManager mCachedPackageManager;
+    private PackageInfo mCachedPackageInfo;
     private String mAppName;
     private boolean mIsHeadsUp;
     private boolean mLastChronometerRunning = true;
@@ -181,7 +193,6 @@
     private AboveShelfChangedListener mAboveShelfChangedListener;
     private HeadsUpManager mHeadsUpManager;
     private Consumer<Boolean> mHeadsUpAnimatingAwayListener;
-    private View mHelperButton;
     private boolean mChildIsExpanding;
 
     private boolean mJustClicked;
@@ -371,6 +382,53 @@
         mEntry = entry;
         mStatusBarNotification = entry.notification;
         mNotificationInflater.inflateNotificationViews();
+
+        perhapsCachePackageInfo();
+    }
+
+    /**
+     * Caches the package manager and info objects which are expensive to obtain.
+     */
+    private void perhapsCachePackageInfo() {
+        if (mCachedPackageInfo == null) {
+            mCachedPackageManager = StatusBar.getPackageManagerForUser(
+                    mContext, mStatusBarNotification.getUser().getIdentifier());
+            try {
+                mCachedPackageInfo = mCachedPackageManager.getPackageInfo(
+                        mStatusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "perhapsCachePackageInfo: Could not find package info");
+            }
+        }
+    }
+
+    /**
+     * 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).
+     */
+    public boolean getIsNonblockable() {
+        boolean isNonblockable;
+
+        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;
+        }
+
+        // Only bother with IPC if the package is still blockable.
+        if (!isNonblockable && mCachedPackageManager != null && mCachedPackageInfo != null) {
+            if (com.android.settingslib.Utils.isSystemPackage(
+                    mContext.getResources(), mCachedPackageManager, mCachedPackageInfo)) {
+                if (mEntry.channel != null
+                        && !mEntry.channel.isBlockableSystem()) {
+                    isNonblockable = true;
+                }
+            }
+        }
+        return isNonblockable;
     }
 
     public void onNotificationUpdated() {
@@ -399,9 +457,6 @@
         updateLimits();
         updateIconVisibilities();
         updateShelfIconColor();
-
-        showBlockingHelper(mEntry.userSentiment ==
-                NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
         updateRippleAllowed();
     }
 
@@ -594,6 +649,13 @@
         return mNotificationParent != null;
     }
 
+    /**
+     * @return whether this notification is the only child in the group summary
+     */
+    public boolean isOnlyChildInGroup() {
+        return mGroupManager.isOnlyChildInGroup(getStatusBarNotification());
+    }
+
     public ExpandableNotificationRow getNotificationParent() {
         return mNotificationParent;
     }
@@ -1061,6 +1123,7 @@
     }
 
     public void setDismissed(boolean fromAccessibility) {
+        setLongPressListener(null);
         mDismissed = true;
         mGroupParentWhenDismissed = mNotificationParent;
         mRefocusOnDismiss = fromAccessibility;
@@ -1149,11 +1212,31 @@
         return mGroupParentWhenDismissed;
     }
 
+    /**
+     * Dismisses the notification with the option of showing the blocking helper in-place if we have
+     * a negative user sentiment.
+     *
+     * @param fromAccessibility whether this dismiss is coming from an accessibility action
+     * @return whether a blocking helper is shown in this row
+     */
+    public boolean performDismissWithBlockingHelper(boolean fromAccessibility) {
+        NotificationBlockingHelperManager manager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        boolean isBlockingHelperShown = manager.perhapsShowBlockingHelper(this, mMenuRow);
+
+        // Continue with dismiss since we don't want the blocking helper to be directly associated
+        // with a certain notification.
+        performDismiss(fromAccessibility);
+        return isBlockingHelperShown;
+    }
+
     public void performDismiss(boolean fromAccessibility) {
-        if (mGroupManager.isOnlyChildInGroup(getStatusBarNotification())) {
+        if (isOnlyChildInGroup()) {
             ExpandableNotificationRow groupSummary =
                     mGroupManager.getLogicalGroupSummary(getStatusBarNotification());
             if (groupSummary.isClearable()) {
+                // If this is the only child in the group, dismiss the group, but don't try to show
+                // the blocking helper affordance!
                 groupSummary.performDismiss(fromAccessibility);
             }
         }
@@ -1165,6 +1248,14 @@
         }
     }
 
+    public void setBlockingHelperShowing(boolean isBlockingHelperShowing) {
+        mIsBlockingHelperShowing = isBlockingHelperShowing;
+    }
+
+    public boolean isBlockingHelperShowing() {
+        return mIsBlockingHelperShowing;
+    }
+
     public void setOnDismissRunnable(Runnable onDismissRunnable) {
         mOnDismissRunnable = onDismissRunnable;
     }
@@ -1389,10 +1480,6 @@
         requestLayout();
     }
 
-    public void showBlockingHelper(boolean show) {
-        mHelperButton.setVisibility(show ? View.VISIBLE : View.GONE);
-    }
-
     public void showAppOpsIcons(ArraySet<Integer> activeOps) {
         if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() != null) {
             mChildrenContainer.getHeaderView().showAppOpsIcons(activeOps);
@@ -1422,12 +1509,6 @@
         mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded);
         mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout};
 
-        final NotificationGutsManager gutsMan = Dependency.get(NotificationGutsManager.class);
-        mHelperButton = findViewById(R.id.helper);
-        mHelperButton.setOnClickListener(view -> {
-            doLongClickCallback();
-        });
-
         for (NotificationContentView l : mLayouts) {
             l.setExpandClickListener(mExpandClickListener);
             l.setContainingNotification(this);
@@ -1995,6 +2076,32 @@
         updateChildrenVisibility();
         applyChildrenRoundness();
     }
+    /**
+     * Returns the number of channels covered by the notification row (including its children if
+     * it's a summary notification).
+     */
+    public int getNumUniqueChannels() {
+        ArraySet<NotificationChannel> channels = new ArraySet<>();
+
+        channels.add(mEntry.channel);
+
+        // If this is a summary, then add in the children notification channels for the
+        // same user and pkg.
+        if (mIsSummaryWithChildren) {
+            final List<ExpandableNotificationRow> childrenRows = getNotificationChildren();
+            final int numChildren = childrenRows.size();
+            for (int i = 0; i < numChildren; i++) {
+                final ExpandableNotificationRow childRow = childrenRows.get(i);
+                final NotificationChannel childChannel = childRow.getEntry().channel;
+                final StatusBarNotification childSbn = childRow.getStatusBarNotification();
+                if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
+                        childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
+                    channels.add(childChannel);
+                }
+            }
+        }
+        return channels.size();
+    }
 
     public void updateChildrenHeaderAppearance() {
         if (mIsSummaryWithChildren) {
@@ -2525,7 +2632,7 @@
         }
         switch (action) {
             case AccessibilityNodeInfo.ACTION_DISMISS:
-                performDismiss(true /* fromAccessibility */);
+                performDismissWithBlockingHelper(true /* fromAccessibility */);
                 return true;
             case AccessibilityNodeInfo.ACTION_COLLAPSE:
             case AccessibilityNodeInfo.ACTION_EXPAND:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java
new file mode 100644
index 0000000..cdb0514
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import android.content.Context;
+import android.content.res.Resources.Theme;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+
+/**
+ * NeutralGoodDrawable implements a drawable that will load 2 underlying drawable resources, one
+ * with each the DualToneDarkTheme and DualToneLightTheme, choosing which one based on what
+ * DarkIconDispatcher tells us about darkness
+ */
+public class NeutralGoodDrawable extends LayerDrawable {
+
+    public static NeutralGoodDrawable create(Context context, int resId) {
+        int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
+        int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
+        ContextThemeWrapper light = new ContextThemeWrapper(context, dualToneLightTheme);
+        ContextThemeWrapper dark = new ContextThemeWrapper(context, dualToneDarkTheme);
+
+        return create(light, dark, resId);
+    }
+
+    /**
+     * For the on-the-go young entrepreneurial who wants to cache contexts
+     * @param light - a context using the R.attr.lightIconTheme
+     * @param dark - a context using the R.attr.darkIconTheme
+     * @param resId - the resId for our drawable
+     */
+    public static NeutralGoodDrawable create(Context light, Context dark, int resId) {
+        return new NeutralGoodDrawable(
+                new Drawable[] {
+                        light.getDrawable(resId).mutate(),
+                        dark.getDrawable(resId).mutate() });
+    }
+
+    protected NeutralGoodDrawable(Drawable []drawables) {
+        super(drawables);
+
+        for (int i = 0; i < drawables.length; i++) {
+            setLayerGravity(i, Gravity.CENTER);
+        }
+
+        mutate();
+        setDarkIntensity(0);
+    }
+
+    public void setDarkIntensity(float intensity) {
+
+        getDrawable(0).setAlpha((int) ((1 - intensity) * 255f));
+        getDrawable(1).setAlpha((int) (intensity * 255f));
+
+        invalidateSelf();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
new file mode 100644
index 0000000..20e5f86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java
@@ -0,0 +1,155 @@
+/*
+ * 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.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+
+/**
+ * Manager for the notification blocking helper - tracks and helps create the blocking helper
+ * affordance.
+ */
+public class NotificationBlockingHelperManager {
+    /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
+    private static final boolean DEBUG = false;
+    private static final String TAG = "BlockingHelper";
+
+    private final Context mContext;
+    /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
+    private ExpandableNotificationRow mBlockingHelperRow;
+    private Set<String> mNonBlockablePkgs;
+
+    /**
+     * Whether the notification shade/stack is expanded - used to determine blocking helper
+     * eligibility.
+     */
+    private boolean mIsShadeExpanded;
+
+    public NotificationBlockingHelperManager(Context context) {
+        mContext = context;
+        mNonBlockablePkgs = new HashSet<>();
+        Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
+                com.android.internal.R.array.config_nonBlockableNotificationPackages));
+    }
+
+    /**
+     * Potentially shows the blocking helper, represented via the {@link NotificationInfo} menu
+     * item, in the current row if user sentiment is negative.
+     *
+     * @param row row to render the blocking helper in
+     * @param menuRow menu used to generate the {@link NotificationInfo} view that houses the
+     *                blocking helper UI
+     * @return whether we're showing a blocking helper in the given notification row
+     */
+    boolean perhapsShowBlockingHelper(
+            ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) {
+        // We only show the blocking helper if:
+        // - User sentiment is negative (DEBUG flag can bypass)
+        // - The notification shade is fully expanded (guarantees we're not touching a HUN).
+        // - The row is blockable (i.e. not non-blockable)
+        // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group
+        if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG)
+                && mIsShadeExpanded
+                && !row.getIsNonblockable()
+                && (!row.isChildInGroup() || row.isOnlyChildInGroup())) {
+            // Dismiss any current blocking helper before continuing forward (only one can be shown
+            // at a given time).
+            dismissCurrentBlockingHelper();
+
+            if (DEBUG) {
+                Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
+            }
+            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
+
+            // Enable blocking helper on the row before moving forward so everything in the guts is
+            // correctly prepped.
+            mBlockingHelperRow = row;
+            mBlockingHelperRow.setBlockingHelperShowing(true);
+
+            // We don't care about the touch origin (x, y) since we're opening guts without any
+            // explicit user interaction.
+            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Dismiss the currently showing blocking helper, if any, through a notification update.
+     *
+     * @return whether the blocking helper was dismissed
+     */
+    boolean dismissCurrentBlockingHelper() {
+        if (!isBlockingHelperRowNull()) {
+            if (DEBUG) {
+                Log.d(TAG, "Manager.dismissCurrentBlockingHelper: Dismissing current helper");
+            }
+            if (!mBlockingHelperRow.isBlockingHelperShowing()) {
+                Log.e(TAG, "Manager.dismissCurrentBlockingHelper: "
+                        + "Non-null row is not showing a blocking helper");
+            }
+
+            mBlockingHelperRow.setBlockingHelperShowing(false);
+            if (mBlockingHelperRow.isAttachedToWindow()) {
+                Dependency.get(NotificationEntryManager.class).updateNotifications();
+            }
+            mBlockingHelperRow = null;
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Update the expansion status of the notification shade/stack.
+     *
+     * @param expandedHeight how much the shade is expanded ({code 0} indicating it's collapsed)
+     */
+    public void setNotificationShadeExpanded(float expandedHeight) {
+        mIsShadeExpanded = expandedHeight > 0.0f;
+    }
+
+    /**
+     * Returns whether the given package name is in the list of non-blockable packages.
+     */
+    public boolean isNonblockablePackage(String packageName) {
+        return mNonBlockablePkgs.contains(packageName);
+    }
+
+    @VisibleForTesting
+    boolean isBlockingHelperRowNull() {
+        return mBlockingHelperRow == null;
+    }
+
+    @VisibleForTesting
+    void setBlockingHelperRowForTest(ExpandableNotificationRow blockingHelperRowForTest) {
+        mBlockingHelperRow = blockingHelperRowForTest;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 775faee..4b6ab64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -18,25 +18,21 @@
 
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT;
-import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR;
 
+import android.Manifest;
 import android.app.AppGlobals;
-import android.app.AppOpsManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
+import android.content.Context;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.Context;
 import android.graphics.drawable.Icon;
-import android.os.AsyncTask;
-import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationListenerService.Ranking;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.SnoozeCriterion;
@@ -46,10 +42,8 @@
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.RemoteViews;
-import android.Manifest;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.internal.util.NotificationColorUtil;
 import com.android.systemui.Dependency;
@@ -454,47 +448,44 @@
         return Ranking.VISIBILITY_NO_OVERRIDE;
     }
 
-    public boolean shouldSuppressFullScreenIntent(String key) {
+    public boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_FULL_SCREEN_INTENT);
+    }
+
+    public boolean shouldSuppressPeek(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_PEEK);
+    }
+
+    public boolean shouldSuppressStatusBar(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_STATUS_BAR);
+    }
+
+    public boolean shouldSuppressAmbient(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_AMBIENT);
+    }
+
+    public boolean shouldSuppressNotificationList(StatusBarNotification sbn) {
+        return shouldSuppressVisualEffect(sbn, SUPPRESSED_EFFECT_NOTIFICATION_LIST);
+    }
+
+    private boolean shouldSuppressVisualEffect(StatusBarNotification sbn, int effect) {
+        if (isExemptFromDndVisualSuppression(sbn)) {
+            return false;
+        }
+        String key = sbn.getKey();
         if (mRankingMap != null) {
             getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_FULL_SCREEN_INTENT) != 0;
+            return (mTmpRanking.getSuppressedVisualEffects() & effect) != 0;
         }
         return false;
     }
 
-    public boolean shouldSuppressPeek(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_PEEK) != 0;
+    protected boolean isExemptFromDndVisualSuppression(StatusBarNotification sbn) {
+        if ((sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
+            return true;
         }
-        return false;
-    }
-
-    public boolean shouldSuppressStatusBar(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_STATUS_BAR) != 0;
-        }
-        return false;
-    }
-
-    public boolean shouldSuppressAmbient(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_AMBIENT) != 0;
-        }
-        return false;
-    }
-
-    public boolean shouldSuppressNotificationList(String key) {
-        if (mRankingMap != null) {
-            getRanking(key, mTmpRanking);
-            return (mTmpRanking.getSuppressedVisualEffects()
-                    & SUPPRESSED_EFFECT_NOTIFICATION_LIST) != 0;
+        if (sbn.getNotification().isMediaNotification()) {
+            return true;
         }
         return false;
     }
@@ -620,11 +611,11 @@
             return true;
         }
 
-        if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn.getKey())) {
+        if (mEnvironment.isDozing() && shouldSuppressAmbient(sbn)) {
             return true;
         }
 
-        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn.getKey())) {
+        if (!mEnvironment.isDozing() && shouldSuppressNotificationList(sbn)) {
             return true;
         }
 
@@ -641,9 +632,14 @@
             // this is a foreground-service disclosure for a user that does not need to show one
             return true;
         }
-        if (mFsc.isSystemAlertNotification(sbn) && !mFsc.isSystemAlertWarningNeeded(
-                sbn.getUserId(), sbn.getPackageName())) {
-            return true;
+        if (mFsc.isSystemAlertNotification(sbn)) {
+            final String[] apps = sbn.getNotification().extras.getStringArray(
+                    Notification.EXTRA_FOREGROUND_APPS);
+            if (apps != null && apps.length >= 1) {
+                if (!mFsc.isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) {
+                    return true;
+                }
+            }
         }
 
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
index 7a7cc99..45df450 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationEntryManager.java
@@ -299,12 +299,12 @@
         updateNotifications();
     }
 
-    private boolean shouldSuppressFullScreenIntent(String key) {
+    private boolean shouldSuppressFullScreenIntent(StatusBarNotification sbn) {
         if (mPresenter.isDeviceInVrMode()) {
             return true;
         }
 
-        return mNotificationData.shouldSuppressFullScreenIntent(key);
+        return mNotificationData.shouldSuppressFullScreenIntent(sbn);
     }
 
     private void inflateViews(NotificationData.Entry entry, ViewGroup parent) {
@@ -690,7 +690,7 @@
         NotificationData.Entry shadeEntry = createNotificationViews(notification);
         boolean isHeadsUped = shouldPeek(shadeEntry);
         if (!isHeadsUped && notification.getNotification().fullScreenIntent != null) {
-            if (shouldSuppressFullScreenIntent(key)) {
+            if (shouldSuppressFullScreenIntent(notification)) {
                 if (DEBUG) {
                     Log.d(TAG, "No Fullscreen intent: suppressed by DND: " + key);
                 }
@@ -846,13 +846,13 @@
             return false;
         }
 
-        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn.getKey())) {
+        if (!mPresenter.isDozing() && mNotificationData.shouldSuppressPeek(sbn)) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
 
         // Peeking triggers an ambient display pulse, so disable peek is ambient is active
-        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn.getKey())) {
+        if (mPresenter.isDozing() && mNotificationData.shouldSuppressAmbient(sbn)) {
             if (DEBUG) Log.d(TAG, "No peeking: suppressed by DND: " + sbn.getKey());
             return false;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 52776d7..bc572a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -30,6 +30,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.stack.StackStateAnimator;
@@ -189,8 +190,12 @@
     }
 
     public void openControls(
-            int x, int y, boolean needsFalsingProtection, @Nullable Runnable onAnimationEnd) {
-        animateOpen(x, y, onAnimationEnd);
+            boolean shouldDoCircularReveal,
+            int x,
+            int y,
+            boolean needsFalsingProtection,
+            @Nullable Runnable onAnimationEnd) {
+        animateOpen(shouldDoCircularReveal, x, y, onAnimationEnd);
         setExposed(true /* exposed */, needsFalsingProtection);
     }
 
@@ -204,7 +209,20 @@
         }
     }
 
+    /**
+     * Closes any exposed guts/views.
+     *
+     * @param x x coordinate to animate the close circular reveal with
+     * @param y y coordinate to animate the close circular reveal with
+     * @param save whether the state should be saved
+     * @param force whether the guts should be force-closed regardless of state.
+     */
     public void closeControls(int x, int y, boolean save, boolean force) {
+        // First try to dismiss any blocking helper.
+        boolean wasBlockingHelperDismissed =
+                Dependency.get(NotificationBlockingHelperManager.class)
+                        .dismissCurrentBlockingHelper();
+
         if (getWindowToken() == null) {
             if (mClosedListener != null) {
                 mClosedListener.onGutsClosed(this);
@@ -212,8 +230,12 @@
             return;
         }
 
-        if (mGutsContent == null || !mGutsContent.handleCloseControls(save, force)) {
-            animateClose(x, y);
+        if (mGutsContent == null
+                || !mGutsContent.handleCloseControls(save, force)
+                || wasBlockingHelperDismissed) {
+            // We only want to do a circular reveal if we're not showing the blocking helper.
+            animateClose(x, y, !wasBlockingHelperDismissed /* shouldDoCircularReveal */);
+
             setExposed(false, mNeedsFalsingProtection);
             if (mClosedListener != null) {
                 mClosedListener.onGutsClosed(this);
@@ -221,47 +243,58 @@
         }
     }
 
-    private void animateOpen(int x, int y, @Nullable Runnable onAnimationEnd) {
-        final double horz = Math.max(getWidth() - x, x);
-        final double vert = Math.max(getHeight() - y, y);
-        final float r = (float) Math.hypot(horz, vert);
-
-        final Animator a
-                = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
-        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
-        a.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                if (onAnimationEnd != null) {
-                    onAnimationEnd.run();
-                }
-            }
-        });
-        a.start();
+    /** Animates in the guts view via either a fade or a circular reveal. */
+    private void animateOpen(
+            boolean shouldDoCircularReveal, int x, int y, @Nullable Runnable onAnimationEnd) {
+        if (shouldDoCircularReveal) {
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            // Circular reveal originating at (x, y)
+            Animator a = ViewAnimationUtils.createCircularReveal(this, x, y, 0, r);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+            a.addListener(new AnimateOpenListener(onAnimationEnd));
+            a.start();
+        } else {
+            // Fade in content
+            this.setAlpha(0f);
+            this.animate()
+                    .alpha(1f)
+                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                    .setInterpolator(Interpolators.ALPHA_IN)
+                    .setListener(new AnimateOpenListener(onAnimationEnd))
+                    .start();
+        }
     }
 
-    private void animateClose(int x, int y) {
-        if (x == -1 || y == -1) {
-            x = (getLeft() + getRight()) / 2;
-            y = (getTop() + getHeight() / 2);
-        }
-        final double horz = Math.max(getWidth() - x, x);
-        final double vert = Math.max(getHeight() - y, y);
-        final float r = (float) Math.hypot(horz, vert);
-        final Animator a = ViewAnimationUtils.createCircularReveal(this,
-                x, y, r, 0);
-        a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
-        a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
-        a.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                super.onAnimationEnd(animation);
-                setVisibility(View.GONE);
+
+    /** Animates out the guts view via either a fade or a circular reveal. */
+    private void animateClose(int x, int y, boolean shouldDoCircularReveal) {
+        if (shouldDoCircularReveal) {
+            // Circular reveal originating at (x, y)
+            if (x == -1 || y == -1) {
+                x = (getLeft() + getRight()) / 2;
+                y = (getTop() + getHeight() / 2);
             }
-        });
-        a.start();
+            double horz = Math.max(getWidth() - x, x);
+            double vert = Math.max(getHeight() - y, y);
+            float r = (float) Math.hypot(horz, vert);
+            Animator a = ViewAnimationUtils.createCircularReveal(this,
+                    x, y, r, 0);
+            a.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+            a.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
+            a.addListener(new AnimateCloseListener(this /* view */));
+            a.start();
+        } else {
+            // Fade in the blocking helper.
+            this.animate()
+                    .alpha(0f)
+                    .setDuration(StackStateAnimator.ANIMATION_DURATION_BLOCKING_HELPER_FADE)
+                    .setInterpolator(Interpolators.ALPHA_OUT)
+                    .setListener(new AnimateCloseListener(this /* view */))
+                    .start();
+        }
     }
 
     public void setActualHeight(int actualHeight) {
@@ -336,4 +369,36 @@
     public boolean isLeavebehind() {
         return mGutsContent != null && mGutsContent.isLeavebehind();
     }
+
+    /** Listener for animations executed in {@link #animateOpen(boolean, int, int, Runnable)}. */
+    private static class AnimateOpenListener extends AnimatorListenerAdapter {
+        final Runnable mOnAnimationEnd;
+
+        private AnimateOpenListener(Runnable onAnimationEnd) {
+            mOnAnimationEnd = onAnimationEnd;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            if (mOnAnimationEnd != null) {
+                mOnAnimationEnd.run();
+            }
+        }
+    }
+
+    /** Listener for animations executed in {@link #animateClose(int, int, boolean)}. */
+    private static class AnimateCloseListener extends AnimatorListenerAdapter {
+        final View mView;
+
+        private AnimateCloseListener(View view) {
+            mView = view;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            super.onAnimationEnd(animation);
+            mView.setVisibility(View.GONE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index 9b2f939..dff5f38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -21,7 +21,6 @@
 import static android.service.notification.NotificationListenerService.Ranking
         .USER_SENTIMENT_NEGATIVE;
 
-import android.app.AppOpsManager;
 import android.app.INotificationManager;
 import android.app.NotificationChannel;
 import android.content.Context;
@@ -34,6 +33,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
+import android.support.annotation.VisibleForTesting;
 import android.util.ArraySet;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
@@ -65,7 +65,6 @@
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
 
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
-    private final Set<String> mNonBlockablePkgs;
     private final Context mContext;
     private final AccessibilityManager mAccessibilityManager;
 
@@ -87,10 +86,6 @@
         mContext = context;
         Resources res = context.getResources();
 
-        mNonBlockablePkgs = new HashSet<>();
-        Collections.addAll(mNonBlockablePkgs, res.getStringArray(
-                com.android.internal.R.array.config_nonBlockableNotificationPackages));
-
         mAccessibilityManager = (AccessibilityManager)
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
@@ -119,22 +114,6 @@
         bindGuts(row);
     }
 
-    private void saveAndCloseNotificationMenu(
-            ExpandableNotificationRow row, NotificationGuts guts, View done) {
-        guts.resetFalsingCheck();
-        int[] rowLocation = new int[2];
-        int[] doneLocation = new int[2];
-        row.getLocationOnScreen(rowLocation);
-        done.getLocationOnScreen(doneLocation);
-
-        final int centerX = done.getWidth() / 2;
-        final int centerY = done.getHeight() / 2;
-        final int x = doneLocation[0] - rowLocation[0] + centerX;
-        final int y = doneLocation[1] - rowLocation[1] + centerY;
-        closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
-                true /* removeControls */, x, y, true /* resetMenu */);
-    }
-
     /**
      * Sends an intent to open the app settings for a particular package and optional
      * channel.
@@ -174,12 +153,12 @@
 
     private void bindGuts(final ExpandableNotificationRow row,
             NotificationMenuRowPlugin.MenuItem item) {
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
         row.inflateGuts();
         row.setGutsView(item);
-        final StatusBarNotification sbn = row.getStatusBarNotification();
         row.setTag(sbn.getPackageName());
-        final NotificationGuts guts = row.getGuts();
-        guts.setClosedListener((NotificationGuts g) -> {
+        row.getGuts().setClosedListener((NotificationGuts g) -> {
             if (!g.willBeRemoved() && !row.isRemoved()) {
                 mListContainer.onHeightChanged(
                         row, !mPresenter.isPresenterFullyCollapsed() /* needsAnimation */);
@@ -197,86 +176,114 @@
 
         View gutsView = item.getGutsView();
         if (gutsView instanceof NotificationSnooze) {
-            NotificationSnooze snoozeGuts = (NotificationSnooze) gutsView;
-            snoozeGuts.setSnoozeListener(mListContainer.getSwipeActionHelper());
-            snoozeGuts.setStatusBarNotification(sbn);
-            snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
-            guts.setHeightChangedListener((NotificationGuts g) -> {
-                mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
-            });
+            initializeSnoozeView(row, (NotificationSnooze) gutsView);
+        } else if (gutsView instanceof AppOpsInfo) {
+            initializeAppOpsInfo(row, (AppOpsInfo) gutsView);
+        } else if (gutsView instanceof NotificationInfo) {
+            initializeNotificationInfo(row, (NotificationInfo) gutsView);
         }
+    }
 
-        if (gutsView instanceof AppOpsInfo) {
-            AppOpsInfo info = (AppOpsInfo) gutsView;
-            final UserHandle userHandle = sbn.getUser();
-            PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                    userHandle.getIdentifier());
-            final AppOpsInfo.OnSettingsClickListener onSettingsClick = (View v,
-                    String pkg, int uid, ArraySet<Integer> ops) -> {
-                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
-                guts.resetFalsingCheck();
-                startAppOpsSettingsActivity(pkg, uid, ops, row);
-            };
-            if (!row.getEntry().mActiveAppOps.isEmpty()) {
-                info.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
-            }
+    /**
+     * Sets up the {@link NotificationSnooze} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationSnoozeView view to set up/bind within {@code row}
+     */
+    private void initializeSnoozeView(
+            final ExpandableNotificationRow row,
+            NotificationSnooze notificationSnoozeView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+
+        notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
+        notificationSnoozeView.setStatusBarNotification(sbn);
+        notificationSnoozeView.setSnoozeOptions(row.getEntry().snoozeCriteria);
+        guts.setHeightChangedListener((NotificationGuts g) -> {
+            mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
+        });
+    }
+
+    /**
+     * Sets up the {@link AppOpsInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param appOpsInfoView view to set up/bind within {@code row}
+     */
+    private void initializeAppOpsInfo(
+            final ExpandableNotificationRow row,
+            AppOpsInfo appOpsInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+                userHandle.getIdentifier());
+
+        AppOpsInfo.OnSettingsClickListener onSettingsClick =
+                (View v, String pkg, int uid, ArraySet<Integer> ops) -> {
+            mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_OPS_GUTS_SETTINGS);
+            guts.resetFalsingCheck();
+            startAppOpsSettingsActivity(pkg, uid, ops, row);
+        };
+        if (!row.getEntry().mActiveAppOps.isEmpty()) {
+            appOpsInfoView.bindGuts(pmUser, onSettingsClick, sbn, row.getEntry().mActiveAppOps);
         }
+    }
 
-        if (gutsView instanceof NotificationInfo) {
-            final UserHandle userHandle = sbn.getUser();
-            PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
-                    userHandle.getIdentifier());
-            final INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
-                    ServiceManager.getService(Context.NOTIFICATION_SERVICE));
-            final String pkg = sbn.getPackageName();
-            NotificationInfo info = (NotificationInfo) gutsView;
-            // Settings link is only valid for notifications that specify a user, unless this is the
-            // system user.
-            NotificationInfo.OnSettingsClickListener onSettingsClick = null;
-            if (!userHandle.equals(UserHandle.ALL)
-                    || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
-                onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
-                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+    /**
+     * Sets up the {@link NotificationInfo} inside the notification row's guts.
+     *
+     * @param row view to set up the guts for
+     * @param notificationInfoView view to set up/bind within {@code row}
+     */
+    @VisibleForTesting
+    void initializeNotificationInfo(
+            final ExpandableNotificationRow row,
+            NotificationInfo notificationInfoView) {
+        NotificationGuts guts = row.getGuts();
+        StatusBarNotification sbn = row.getStatusBarNotification();
+        String packageName = sbn.getPackageName();
+        // Settings link is only valid for notifications that specify a non-system user
+        NotificationInfo.OnSettingsClickListener onSettingsClick = null;
+        UserHandle userHandle = sbn.getUser();
+        PackageManager pmUser = StatusBar.getPackageManagerForUser(
+                mContext, userHandle.getIdentifier());
+        INotificationManager iNotificationManager = INotificationManager.Stub.asInterface(
+                ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+        final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
+                (View v, Intent intent) -> {
+                    mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
                     guts.resetFalsingCheck();
-                    mOnSettingsClickListener.onClick(sbn.getKey());
-                    startAppNotificationSettingsActivity(pkg, appUid, channel, row);
+                    mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
                 };
-            }
-            final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick = (View v,
-                    Intent intent) -> {
-                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_APP_NOTE_SETTINGS);
-                guts.resetFalsingCheck();
-                mPresenter.startNotificationGutsIntent(intent, sbn.getUid(), row);
-            };
-            final View.OnClickListener onDoneClick = (View v) -> {
-                saveAndCloseNotificationMenu(row, guts, v);
-            };
+        boolean isForBlockingHelper = row.isBlockingHelperShowing();
 
-            ArraySet<NotificationChannel> channels = new ArraySet<>();
-            channels.add(row.getEntry().channel);
-            if (row.isSummaryWithChildren()) {
-                // If this is a summary, then add in the children notification channels for the
-                // same user and pkg.
-                final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren();
-                final int numChildren = childrenRows.size();
-                for (int i = 0; i < numChildren; i++) {
-                    final ExpandableNotificationRow childRow = childrenRows.get(i);
-                    final NotificationChannel childChannel = childRow.getEntry().channel;
-                    final StatusBarNotification childSbn = childRow.getStatusBarNotification();
-                    if (childSbn.getUser().equals(userHandle) &&
-                            childSbn.getPackageName().equals(pkg)) {
-                        channels.add(childChannel);
-                    }
-                }
-            }
-            try {
-                info.bindNotification(pmUser, iNotificationManager, pkg, row.getEntry().channel,
-                        channels.size(), sbn, mCheckSaveListener, onSettingsClick,
-                        onAppSettingsClick, mNonBlockablePkgs,
-                        row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
-            } catch (RemoteException e) {
-                Log.e(TAG, e.toString());
-            }
+        if (!userHandle.equals(UserHandle.ALL)
+                || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+            onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
+                mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
+                guts.resetFalsingCheck();
+                mOnSettingsClickListener.onClick(sbn.getKey());
+                startAppNotificationSettingsActivity(packageName, appUid, channel, row);
+            };
+        }
+
+        try {
+            notificationInfoView.bindNotification(
+                    pmUser,
+                    iNotificationManager,
+                    packageName,
+                    row.getEntry().channel,
+                    row.getNumUniqueChannels(),
+                    sbn,
+                    mCheckSaveListener,
+                    onSettingsClick,
+                    onAppSettingsClick,
+                    row.getIsNonblockable(),
+                    isForBlockingHelper,
+                    row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE);
+        } catch (RemoteException e) {
+            Log.e(TAG, e.toString());
         }
     }
 
@@ -312,37 +319,42 @@
     }
 
     /**
-     * Opens guts on the given ExpandableNotificationRow |v|.
+     * Opens guts on the given ExpandableNotificationRow {@code view}. This handles opening guts for
+     * the normal half-swipe and long-press use cases via a circular reveal. When the blocking
+     * helper needs to be shown on the row, this will skip the circular reveal.
      *
-     * @param v ExpandableNotificationRow to open guts on
+     * @param view ExpandableNotificationRow to open guts on
      * @param x x coordinate of origin of circular reveal
      * @param y y coordinate of origin of circular reveal
-     * @param item MenuItem the guts should display
+     * @param menuItem MenuItem the guts should display
      * @return true if guts was opened
      */
-    public boolean openGuts(View v, int x, int y,
-            NotificationMenuRowPlugin.MenuItem item) {
-        if (!(v instanceof ExpandableNotificationRow)) {
+    boolean openGuts(
+            View view,
+            int x,
+            int y,
+            NotificationMenuRowPlugin.MenuItem menuItem) {
+        if (!(view instanceof ExpandableNotificationRow)) {
             return false;
         }
 
-        if (v.getWindowToken() == null) {
+        if (view.getWindowToken() == null) {
             Log.e(TAG, "Trying to show notification guts, but not attached to window");
             return false;
         }
 
-        final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+        final ExpandableNotificationRow row = (ExpandableNotificationRow) view;
         if (row.isDark()) {
             return false;
         }
-        v.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        view.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
         if (row.areGutsExposed()) {
             closeAndSaveGuts(false /* removeLeavebehind */, false /* force */,
                     true /* removeControls */, -1 /* x */, -1 /* y */,
                     true /* resetMenu */);
             return false;
         }
-        bindGuts(row, item);
+        bindGuts(row, menuItem);
         NotificationGuts guts = row.getGuts();
 
         // Assume we are a status_bar_notification_row
@@ -372,15 +384,18 @@
                 final boolean needsFalsingProtection =
                         (mPresenter.isPresenterLocked() &&
                                 !mAccessibilityManager.isTouchExplorationEnabled());
-                guts.openControls(x, y, needsFalsingProtection, () -> {
-                    // Move the notification view back over the menu
-                    row.resetTranslation();
-                });
+
+                guts.openControls(
+                        !row.isBlockingHelperShowing(),
+                        x,
+                        y,
+                        needsFalsingProtection,
+                        row::resetTranslation);
 
                 row.closeRemoteInput();
                 mListContainer.onHeightChanged(row, true /* needsAnimation */);
                 mNotificationGutsExposed = guts;
-                mGutsMenuItem = item;
+                mGutsMenuItem = menuItem;
             }
         });
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index b1ad30c..a93be00 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -31,7 +31,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
@@ -46,14 +45,14 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 
 import java.util.List;
-import java.util.Set;
 
 /**
  * The guts of a notification revealed when performing a long press.
@@ -72,7 +71,7 @@
     private int mStartingUserImportance;
     private int mChosenImportance;
     private boolean mIsSingleDefaultChannel;
-    private boolean mNonblockable;
+    private boolean mIsNonblockable;
     private StatusBarNotification mSbn;
     private AnimatorSet mExpandAnimation;
     private boolean mIsForeground;
@@ -81,11 +80,12 @@
     private OnSettingsClickListener mOnSettingsClickListener;
     private OnAppSettingsClickListener mAppSettingsClickListener;
     private NotificationGuts mGutsContainer;
+
+    /** Whether this view is being shown as part of the blocking helper */
+    private boolean mIsForBlockingHelper;
     private boolean mNegativeUserSentiment;
 
-    private OnClickListener mOnKeepShowing = v -> {
-        closeControls(v);
-    };
+    private OnClickListener mOnKeepShowing = this::closeControls;
 
     private OnClickListener mOnStopMinNotifications = v -> {
         swapContent(false);
@@ -114,7 +114,9 @@
         void onClick(View v, Intent intent);
     }
 
-    public void bindNotification(final PackageManager pm,
+    @VisibleForTesting
+    void bindNotification(
+            final PackageManager pm,
             final INotificationManager iNotificationManager,
             final String pkg,
             final NotificationChannel notificationChannel,
@@ -123,24 +125,28 @@
             final CheckSaveListener checkSaveListener,
             final OnSettingsClickListener onSettingsClick,
             final OnAppSettingsClickListener onAppSettingsClick,
-            final Set<String> nonBlockablePkgs)
+            boolean isNonblockable)
             throws RemoteException {
         bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn,
-                checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs,
-                false /* negative sentiment */);
+                checkSaveListener, onSettingsClick, onAppSettingsClick, isNonblockable,
+                false /* isBlockingHelper */,
+                false /* isUserSentimentNegative */);
     }
 
-    public void bindNotification(final PackageManager pm,
-            final INotificationManager iNotificationManager,
-            final String pkg,
-            final NotificationChannel notificationChannel,
-            final int numChannels,
-            final StatusBarNotification sbn,
-            final CheckSaveListener checkSaveListener,
-            final OnSettingsClickListener onSettingsClick,
-            final OnAppSettingsClickListener onAppSettingsClick,
-            final Set<String> nonBlockablePkgs,
-            boolean negativeUserSentiment)  throws RemoteException {
+    public void bindNotification(
+            PackageManager pm,
+            INotificationManager iNotificationManager,
+            String pkg,
+            NotificationChannel notificationChannel,
+            int numChannels,
+            StatusBarNotification sbn,
+            CheckSaveListener checkSaveListener,
+            OnSettingsClickListener onSettingsClick,
+            OnAppSettingsClickListener onAppSettingsClick,
+            boolean isNonblockable,
+            boolean isForBlockingHelper,
+            boolean isUserSentimentNegative)
+            throws RemoteException {
         mINotificationManager = iNotificationManager;
         mPkg = pkg;
         mNumNotificationChannels = numChannels;
@@ -152,9 +158,12 @@
         mOnSettingsClickListener = onSettingsClick;
         mSingleNotificationChannel = notificationChannel;
         mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance();
-        mNegativeUserSentiment = negativeUserSentiment;
+        mNegativeUserSentiment = isUserSentimentNegative;
+        mIsNonblockable = isNonblockable;
         mIsForeground =
                 (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
+        mIsForBlockingHelper = isForBlockingHelper;
+        mAppUid = mSbn.getUid();
 
         int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage(
                 pkg, mAppUid, false /* includeDeleted */);
@@ -163,27 +172,11 @@
         } else  {
             // Special behavior for the Default channel if no other channels have been defined.
             mIsSingleDefaultChannel = mNumNotificationChannels == 1
-                    && mSingleNotificationChannel.getId()
-                    .equals(NotificationChannel.DEFAULT_CHANNEL_ID)
-                    && numTotalChannels <= 1;
+                    && mSingleNotificationChannel.getId().equals(
+                            NotificationChannel.DEFAULT_CHANNEL_ID)
+                    && numTotalChannels == 1;
         }
 
-        try {
-            final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES);
-            if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) {
-                if (mSingleNotificationChannel != null
-                        && !mSingleNotificationChannel.isBlockableSystem()) {
-                    mNonblockable = true;
-                }
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            // unlikely.
-        }
-        if (nonBlockablePkgs != null) {
-            mNonblockable |= nonBlockablePkgs.contains(pkg);
-        }
-        mNonblockable |= (mNumNotificationChannels > 1);
-
         bindHeader();
         bindPrompt();
         bindButtons();
@@ -200,7 +193,6 @@
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                             | PackageManager.MATCH_DIRECT_BOOT_AWARE);
             if (info != null) {
-                mAppUid = mSbn.getUid();
                 mAppName = String.valueOf(mPm.getApplicationLabel(info));
                 pkgicon = mPm.getApplicationIcon(info);
             }
@@ -251,7 +243,7 @@
     private void bindPrompt() {
         final TextView blockPrompt = findViewById(R.id.block_prompt);
         bindName();
-        if (mNonblockable) {
+        if (mIsNonblockable) {
             blockPrompt.setText(R.string.notification_unblockable_desc);
         } else {
             if (mNegativeUserSentiment) {
@@ -278,7 +270,7 @@
     }
 
     private void saveImportance() {
-        if (mNonblockable) {
+        if (mIsNonblockable) {
             return;
         }
         MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE,
@@ -294,15 +286,17 @@
     }
 
     private void bindButtons() {
+        // Set up stay-in-notification actions
         View block =  findViewById(R.id.block);
-        block.setOnClickListener(mOnStopMinNotifications);
         TextView keep = findViewById(R.id.keep);
-        keep.setOnClickListener(mOnKeepShowing);
-        findViewById(R.id.undo).setOnClickListener(mOnUndo);
         View minimize = findViewById(R.id.minimize);
+
+        findViewById(R.id.undo).setOnClickListener(mOnUndo);
+        block.setOnClickListener(mOnStopMinNotifications);
+        keep.setOnClickListener(mOnKeepShowing);
         minimize.setOnClickListener(mOnStopMinNotifications);
 
-        if (mNonblockable) {
+        if (mIsNonblockable) {
             keep.setText(R.string.notification_done);
             block.setVisibility(GONE);
             minimize.setVisibility(GONE);
@@ -314,7 +308,7 @@
             minimize.setVisibility(GONE);
         }
 
-        // app settings link
+        // Set up app settings link
         TextView settingsLinkView = findViewById(R.id.app_settings);
         Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel,
                 mSbn.getId(), mSbn.getTag());
@@ -421,16 +415,23 @@
         return intent;
     }
 
-    private void closeControls(View v) {
-        int[] parentLoc = new int[2];
-        int[] targetLoc = new int[2];
-        mGutsContainer.getLocationOnScreen(parentLoc);
-        v.getLocationOnScreen(targetLoc);
-        final int centerX = v.getWidth() / 2;
-        final int centerY = v.getHeight() / 2;
-        final int x = targetLoc[0] - parentLoc[0] + centerX;
-        final int y = targetLoc[1] - parentLoc[1] + centerY;
-        mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+    @VisibleForTesting
+    void closeControls(View v) {
+        if (mIsForBlockingHelper) {
+            NotificationBlockingHelperManager manager =
+                    Dependency.get(NotificationBlockingHelperManager.class);
+            manager.dismissCurrentBlockingHelper();
+        } else {
+            int[] parentLoc = new int[2];
+            int[] targetLoc = new int[2];
+            mGutsContainer.getLocationOnScreen(parentLoc);
+            v.getLocationOnScreen(targetLoc);
+            final int centerX = v.getWidth() / 2;
+            final int centerY = v.getHeight() / 2;
+            final int x = targetLoc[0] - parentLoc[0] + centerX;
+            final int y = targetLoc[1] - parentLoc[1] + centerY;
+            mGutsContainer.closeControls(x, y, true /* save */, false /* force */);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index a4aa598..0112661 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -227,7 +227,6 @@
             expandAmount = Math.min(1.0f, expandAmount);
         }
         //  find the first view that doesn't overlap with the shelf
-        int notificationIndex = 0;
         int notGoneIndex = 0;
         int colorOfViewBeforeLast = NO_COLOR;
         boolean backgroundForceHidden = false;
@@ -247,13 +246,15 @@
         int baseZHeight = mAmbientState.getBaseZHeight();
         int backgroundTop = 0;
         float firstElementRoundness = 0.0f;
-        while (notificationIndex < mHostLayout.getChildCount()) {
-            ExpandableView child = (ExpandableView) mHostLayout.getChildAt(notificationIndex);
-            notificationIndex++;
+
+        for (int i = 0; i < mHostLayout.getChildCount(); i++) {
+            ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i);
+
             if (!(child instanceof ExpandableNotificationRow)
                     || child.getVisibility() == GONE) {
                 continue;
             }
+
             ExpandableNotificationRow row = (ExpandableNotificationRow) child;
             float notificationClipEnd;
             boolean aboveShelf = ViewState.getFinalTranslationZ(row) > baseZHeight
@@ -315,6 +316,9 @@
             notGoneIndex++;
             previousColor = ownColorUntinted;
         }
+
+        clipTransientViews();
+
         setBackgroundTop(backgroundTop);
         setFirstElementRoundness(firstElementRoundness);
         mShelfIcons.setSpeedBumpIndex(mAmbientState.getSpeedBumpIndex());
@@ -337,6 +341,25 @@
         }
     }
 
+    /**
+     * Clips transient views to the top of the shelf - Transient views are only used for
+     * disappearing views/animations and need to be clipped correctly by the shelf to ensure they
+     * don't show underneath the notification stack when something is animating and the user
+     * swipes quickly.
+     */
+    private void clipTransientViews() {
+        for (int i = 0; i < mHostLayout.getTransientViewCount(); i++) {
+            View transientView = mHostLayout.getTransientView(i);
+            if (transientView instanceof ExpandableNotificationRow) {
+                ExpandableNotificationRow transientRow = (ExpandableNotificationRow) transientView;
+                updateNotificationClipHeight(transientRow, getTranslationY());
+            } else {
+                Log.e(TAG, "NotificationShelf.clipTransientViews(): "
+                        + "Trying to clip non-row transient view");
+            }
+        }
+    }
+
     private void setFirstElementRoundness(float firstElementRoundness) {
         if (mFirstElementRoundness != firstElementRoundness) {
             mFirstElementRoundness = firstElementRoundness;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 75b8b37..fd3a9d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -125,24 +125,30 @@
 
         }
 
-        ArrayList<ExpandableNotificationRow> toRemove = new ArrayList<>();
+        ArrayList<ExpandableNotificationRow> viewsToRemove = new ArrayList<>();
         for (int i=0; i< mListContainer.getContainerChildCount(); i++) {
             View child = mListContainer.getContainerChildAt(i);
             if (!toShow.contains(child) && child instanceof ExpandableNotificationRow) {
-                toRemove.add((ExpandableNotificationRow) child);
+                ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+
+                // Blocking helper is effectively a detached view. Don't bother removing it from the
+                // layout.
+                if (!row.isBlockingHelperShowing()) {
+                    viewsToRemove.add((ExpandableNotificationRow) child);
+                }
             }
         }
 
-        for (ExpandableNotificationRow remove : toRemove) {
-            if (mGroupManager.isChildInGroupWithSummary(remove.getStatusBarNotification())) {
+        for (ExpandableNotificationRow viewToRemove : viewsToRemove) {
+            if (mGroupManager.isChildInGroupWithSummary(viewToRemove.getStatusBarNotification())) {
                 // we are only transferring this notification to its parent, don't generate an
                 // animation
                 mListContainer.setChildTransferInProgress(true);
             }
-            if (remove.isSummaryWithChildren()) {
-                remove.removeAllChildren();
+            if (viewToRemove.isSummaryWithChildren()) {
+                viewToRemove.removeAllChildren();
             }
-            mListContainer.removeContainerView(remove);
+            mListContainer.removeContainerView(viewToRemove);
             mListContainer.setChildTransferInProgress(false);
         }
 
@@ -168,6 +174,10 @@
                 // We don't care about non-notification views.
                 continue;
             }
+            if (((ExpandableNotificationRow) child).isBlockingHelperShowing()) {
+                // Don't count/reorder notifications that are showing the blocking helper!
+                continue;
+            }
 
             ExpandableNotificationRow targetChild = toShow.get(j);
             if (child != targetChild) {
@@ -340,9 +350,6 @@
                 }
             }
 
-            row.showBlockingHelper(entry.userSentiment ==
-                    NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE);
-
             row.showAppOpsIcons(entry.mActiveAppOps);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
new file mode 100644
index 0000000..56f78f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Holds an array of {@link com.android.internal.statusbar.StatusBarIcon}s and draws them
+ * in a linear layout
+ */
+public class StatusBarIconContainer {
+    private final List<StatusBarIcon> mIcons = new ArrayList<>();
+
+    public StatusBarIconContainer(List<StatusBarIcon> icons) {
+        mIcons.addAll(icons);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 603902a..bd6bd12 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -23,6 +25,7 @@
 import android.app.Notification;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -56,7 +59,7 @@
 import java.text.NumberFormat;
 import java.util.Arrays;
 
-public class StatusBarIconView extends AnimatedImageView {
+public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable {
     public static final int NO_COLOR = 0;
 
     /**
@@ -867,6 +870,21 @@
         mOnDismissListener = onDismissListener;
     }
 
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mIcon != null && mIcon.visible;
+    }
+
+    @Override
+    public boolean isIconBlocked() {
+        return mBlocked;
+    }
+
     public interface OnVisibilityChangedListener {
         void onVisibilityChanged(int newVisibility);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
new file mode 100644
index 0000000..b7620f3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -0,0 +1,207 @@
+/*
+ * 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 static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements DarkReceiver,
+        StatusIconDisplayable {
+    private static final String TAG = "StatusBarMobileView";
+
+    private String mSlot;
+    private MobileIconState mState;
+    private SignalDrawable mMobileDrawable;
+    private View mInoutContainer;
+    private ImageView mIn;
+    private ImageView mOut;
+    private ImageView mMobile, mMobileType, mMobileRoaming;
+    private View mMobileRoamingSpace;
+
+    public static StatusBarMobileView fromContext(Context context) {
+        LayoutInflater inflater = LayoutInflater.from(context);
+
+        return (StatusBarMobileView)
+                inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
+    }
+
+    public StatusBarMobileView(Context context) {
+        super(context);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        init();
+    }
+
+    private void init() {
+        mMobile = findViewById(R.id.mobile_signal);
+        mMobileType = findViewById(R.id.mobile_type);
+        mMobileRoaming = findViewById(R.id.mobile_roaming);
+        mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
+        mIn = findViewById(R.id.mobile_in);
+        mOut = findViewById(R.id.mobile_out);
+        mInoutContainer = findViewById(R.id.inout_container);
+
+        mMobileDrawable = new SignalDrawable(getContext());
+        mMobile.setImageDrawable(mMobileDrawable);
+    }
+
+    public void applyMobileState(MobileIconState state) {
+        if (state == null) {
+            setVisibility(View.GONE);
+            mState = null;
+            return;
+        }
+
+        if (mState == null) {
+            mState = state;
+            initViewState();
+            return;
+        }
+
+        if (!mState.equals(state)) {
+            updateState(state);
+        }
+    }
+
+    private void initViewState() {
+        setContentDescription(mState.contentDescription);
+        if (!mState.visible) {
+            setVisibility(View.GONE);
+        } else {
+            setVisibility(View.VISIBLE);
+        }
+        mMobileDrawable.setLevel(mState.strengthId);
+        if (mState.typeId > 0) {
+            mMobileType.setContentDescription(mState.typeContentDescription);
+            mMobileType.setImageResource(mState.typeId);
+            mMobileType.setVisibility(View.VISIBLE);
+        } else {
+            mMobileType.setVisibility(View.GONE);
+        }
+
+        mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
+        mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
+        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility((mState.activityIn || mState.activityOut)
+                ? View.VISIBLE : View.GONE);
+    }
+
+    private void updateState(MobileIconState state) {
+        setContentDescription(state.contentDescription);
+        if (mState.visible != state.visible) {
+            setVisibility(state.visible ? View.VISIBLE : View.GONE);
+        }
+        if (mState.strengthId != state.strengthId) {
+            mMobileDrawable.setLevel(state.strengthId);
+        }
+        if (mState.typeId != state.typeId && state.typeId != 0) {
+            mMobileType.setContentDescription(state.typeContentDescription);
+            mMobileType.setImageResource(state.typeId);
+            mMobileType.setVisibility(View.VISIBLE);
+        } else {
+            mMobileType.setVisibility(View.GONE);
+        }
+
+        mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
+        mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
+        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility((state.activityIn || state.activityOut)
+                ? View.VISIBLE : View.GONE);
+
+        mState = state;
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mMobileDrawable.setDarkIntensity(darkIntensity);
+        ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint));
+        mIn.setImageTintList(color);
+        mOut.setImageTintList(color);
+        mMobileType.setImageTintList(color);
+        mMobileRoaming.setImageTintList(color);
+    }
+
+    @Override
+    public String getSlot() {
+        return mSlot;
+    }
+
+    public void setSlot(String slot) {
+        mSlot = slot;
+    }
+
+    @Override
+    public void setStaticDrawableColor(int color) {
+        ColorStateList list = ColorStateList.valueOf(color);
+        float intensity = color == Color.WHITE ? 0 : 1;
+        mMobileDrawable.setDarkIntensity(intensity);
+
+        mIn.setImageTintList(list);
+        mOut.setImageTintList(list);
+        mMobileType.setImageTintList(list);
+        mMobileRoaming.setImageTintList(list);
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mState.visible;
+    }
+
+    @VisibleForTesting
+    public MobileIconState getState() {
+        return mState;
+    }
+
+    @Override
+    public String toString() {
+        return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
new file mode 100644
index 0000000..afd373e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -0,0 +1,192 @@
+/*
+ * 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 static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.ContextThemeWrapper;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.keyguard.AlphaOptimizedLinearLayout;
+import com.android.settingslib.Utils;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+/**
+ * Start small: StatusBarWifiView will be able to layout from a WifiIconState
+ */
+public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements DarkReceiver,
+        StatusIconDisplayable {
+    private static final String TAG = "StatusBarWifiView";
+
+    private ImageView mWifiIcon;
+    private ImageView mIn;
+    private ImageView mOut;
+    private View mInoutContainer;
+    private View mSignalSpacer;
+    private View mAirplaneSpacer;
+    private WifiIconState mState;
+    private String mSlot;
+    private float mDarkIntensity = 0;
+
+    private ContextThemeWrapper mDarkContext;
+    private ContextThemeWrapper mLightContext;
+
+    public static StatusBarWifiView fromContext(Context context) {
+        LayoutInflater inflater = LayoutInflater.from(context);
+        return (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null);
+    }
+
+    public StatusBarWifiView(Context context) {
+        super(context);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        init();
+    }
+
+    public void setSlot(String slot) {
+        mSlot = slot;
+    }
+
+    @Override
+    public void setStaticDrawableColor(int color) {
+        ColorStateList list = ColorStateList.valueOf(color);
+        mWifiIcon.setImageTintList(list);
+        mIn.setImageTintList(list);
+        mOut.setImageTintList(list);
+    }
+
+    @Override
+    public String getSlot() {
+        return mSlot;
+    }
+
+    @Override
+    public boolean isIconVisible() {
+        return mState != null && mState.visible;
+    }
+
+    private void init() {
+        int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
+        int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
+        mLightContext = new ContextThemeWrapper(mContext, dualToneLightTheme);
+        mDarkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme);
+
+        mWifiIcon = findViewById(R.id.wifi_signal);
+        mIn = findViewById(R.id.wifi_in);
+        mOut = findViewById(R.id.wifi_out);
+        mSignalSpacer = findViewById(R.id.wifi_signal_spacer);
+        mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer);
+        mInoutContainer = findViewById(R.id.inout_container);
+    }
+
+    public void applyWifiState(WifiIconState state) {
+        if (state == null) {
+            setVisibility(View.GONE);
+            mState = null;
+            return;
+        }
+
+        if (mState == null) {
+            mState = state;
+            initViewState();
+        }
+
+        if (!mState.equals(state)) {
+            updateState(state);
+        }
+    }
+
+    private void updateState(WifiIconState state) {
+        if (mState.resId != state.resId && state.resId >= 0) {
+            NeutralGoodDrawable drawable = NeutralGoodDrawable
+                    .create(mLightContext, mDarkContext, state.resId);
+            drawable.setDarkIntensity(mDarkIntensity);
+            mWifiIcon.setImageDrawable(drawable);
+        }
+
+        mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility(
+                (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE);
+        mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
+        mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE);
+        if (mState.visible != state.visible) {
+            setVisibility(state.visible ? View.VISIBLE : View.GONE);
+        }
+
+        mState = state;
+    }
+
+    private void initViewState() {
+        if (mState.resId >= 0) {
+            NeutralGoodDrawable drawable = NeutralGoodDrawable.create(
+                    mLightContext, mDarkContext, mState.resId);
+            drawable.setDarkIntensity(mDarkIntensity);
+            mWifiIcon.setImageDrawable(drawable);
+        }
+
+        mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
+        mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE);
+        mInoutContainer.setVisibility(
+                (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE);
+        mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE);
+        mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE);
+        setVisibility(mState.visible ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void onDarkChanged(Rect area, float darkIntensity, int tint) {
+        mDarkIntensity = darkIntensity;
+        Drawable d = mWifiIcon.getDrawable();
+        if (d instanceof NeutralGoodDrawable) {
+            ((NeutralGoodDrawable)d).setDarkIntensity(darkIntensity);
+        }
+        mIn.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+        mOut.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint)));
+    }
+
+
+    @Override
+    public String toString() {
+        return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
new file mode 100644
index 0000000..ccab0d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
+
+public interface StatusIconDisplayable extends DarkReceiver {
+    String getSlot();
+    void setStaticDrawableColor(int color);
+    boolean isIconVisible();
+    default boolean isIconBlocked() {
+        return false;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
index 53101a5..5f3e2e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButton.java
@@ -3,6 +3,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
@@ -10,6 +11,7 @@
 import android.widget.LinearLayout;
 
 import com.android.keyguard.AlphaOptimizedImageButton;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 
 /**
@@ -21,9 +23,6 @@
  * other music apps installed.
  */
 public class CarFacetButton extends LinearLayout {
-    private static final float SELECTED_ALPHA = 1f;
-    private static final float UNSELECTED_ALPHA = 0.7f;
-
     private static final String FACET_FILTER_DELIMITER = ";";
     /**
      * Extra information to be sent to a helper to make the decision of what app to launch when
@@ -42,6 +41,10 @@
     private String[] mFacetCategories;
     /** App packages that are allowed to be used with this widget */
     private String[] mFacetPackages;
+    private int mIconResourceId;
+    private boolean mUseMoreIcon = true;
+    private float mSelectedAlpha = 1f;
+    private float mUnselectedAlpha = 1f;
 
 
     public CarFacetButton(Context context, AttributeSet attrs) {
@@ -53,6 +56,10 @@
         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CarFacetButton);
         setupIntents(typedArray);
         setupIcons(typedArray);
+        CarFacetButtonController carFacetButtonController = Dependency.get(
+                CarFacetButtonController.class);
+        carFacetButtonController.addFacetButton(this);
+
     }
 
     /**
@@ -96,21 +103,25 @@
 
 
     private void setupIcons(TypedArray styledAttributes) {
+        mSelectedAlpha = styledAttributes.getFloat(
+                R.styleable.CarFacetButton_selectedAlpha, mSelectedAlpha);
+        mUnselectedAlpha = styledAttributes.getFloat(
+                R.styleable.CarFacetButton_unselectedAlpha, mUnselectedAlpha);
         mIcon = findViewById(R.id.car_nav_button_icon);
         mIcon.setScaleType(ImageView.ScaleType.CENTER);
         mIcon.setClickable(false);
-        mIcon.setAlpha(UNSELECTED_ALPHA);
-        int iconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
-        if (iconResourceId == 0)  {
+        mIcon.setAlpha(mUnselectedAlpha);
+        mIconResourceId = styledAttributes.getResourceId(R.styleable.CarFacetButton_icon, 0);
+        if (mIconResourceId == 0)  {
             throw new RuntimeException("specified icon resource was not found and is required");
         }
-        mIcon.setImageResource(iconResourceId);
+        mIcon.setImageResource(mIconResourceId);
 
         mMoreIcon = findViewById(R.id.car_nav_button_more_icon);
         mMoreIcon.setClickable(false);
-        mMoreIcon.setImageDrawable(getContext().getDrawable(R.drawable.car_ic_arrow));
-        mMoreIcon.setAlpha(UNSELECTED_ALPHA);
+        mMoreIcon.setAlpha(mSelectedAlpha);
         mMoreIcon.setVisibility(GONE);
+        mUseMoreIcon = styledAttributes.getBoolean(R.styleable.CarFacetButton_useMoreIcon, true);
     }
 
     /**
@@ -145,17 +156,27 @@
     /**
      * Updates the visual state to let the user know if it's been selected.
      * @param selected true if should update the alpha of the icon to selected, false otherwise
-     * @param showMoreIcon true if the "more icon" should be shown, false otherwise
+     * @param showMoreIcon true if the "more icon" should be shown, false otherwise. Note this
+     *                     is ignored if the attribute useMoreIcon is set to false
      */
     public void setSelected(boolean selected, boolean showMoreIcon) {
         mSelected = selected;
         if (selected) {
-            mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
-            mMoreIcon.setAlpha(SELECTED_ALPHA);
-            mIcon.setAlpha(SELECTED_ALPHA);
+            if (mUseMoreIcon) {
+                mMoreIcon.setVisibility(showMoreIcon ? VISIBLE : GONE);
+            }
+            mIcon.setAlpha(mSelectedAlpha);
         } else {
             mMoreIcon.setVisibility(GONE);
-            mIcon.setAlpha(UNSELECTED_ALPHA);
+            mIcon.setAlpha(mUnselectedAlpha);
+        }
+    }
+
+    public void setIcon(Drawable d) {
+        if (d != null) {
+            mIcon.setImageDrawable(d);
+        } else {
+            mIcon.setImageResource(mIconResourceId);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
index e8c9a5e..b7d501e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java
@@ -5,8 +5,6 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.view.View;
-import android.view.ViewGroup;
 
 import java.util.HashMap;
 import java.util.List;
@@ -29,38 +27,30 @@
     }
 
     /**
-     * Goes through the supplied CarNavigationBarView and keeps track of all the CarFacetButtons
-     * such that it can select and unselect them based on running task chages
-     * @param bar that may contain CarFacetButtons
+     * Add facet button to this controller. The expected use is for the facet button
+     * to get a reference to this controller via {@link com.android.systemui.Dependency}
+     * and self add.
+     * @param facetButton
      */
-    public void addCarNavigationBar(CarNavigationBarView bar) {
-        findFacets(bar);
-    }
+    public void addFacetButton(CarFacetButton facetButton) {
+        String[] categories = facetButton.getCategories();
+        for (int j = 0; j < categories.length; j++) {
+            String category = categories[j];
+            mButtonsByCategory.put(category, facetButton);
+        }
 
-    private void findFacets(ViewGroup root) {
-        final int childCount = root.getChildCount();
-
-        for (int i = 0; i < childCount; ++i) {
-            final View v = root.getChildAt(i);
-            if (v instanceof CarFacetButton) {
-                CarFacetButton facetButton = (CarFacetButton) v;
-                String[] categories = facetButton.getCategories();
-                for (int j = 0; j < categories.length; j++) {
-                    String category = categories[j];
-                    mButtonsByCategory.put(category, facetButton);
-                }
-
-                String[] facetPackages = facetButton.getFacetPackages();
-                for (int j = 0; j < facetPackages.length; j++) {
-                    String facetPackage = facetPackages[j];
-                    mButtonsByPackage.put(facetPackage, facetButton);
-                }
-            } else if (v instanceof ViewGroup) {
-                findFacets((ViewGroup) v);
-            }
+        String[] facetPackages = facetButton.getFacetPackages();
+        for (int j = 0; j < facetPackages.length; j++) {
+            String facetPackage = facetPackages[j];
+            mButtonsByPackage.put(facetPackage, facetButton);
         }
     }
 
+    public void removeAll() {
+        mButtonsByCategory.clear();
+        mButtonsByPackage.clear();
+        mSelectedFacetButton = null;
+    }
 
     /**
      * This will unselect the currently selected CarFacetButton and determine which one should be
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 1d9ef61..e73b173 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.view.View;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.R;
@@ -36,9 +37,11 @@
     private LinearLayout mNavButtons;
     private AlphaOptimizedImageButton mNotificationsButton;
     private CarStatusBar mCarStatusBar;
+    private Context mContext;
 
     public CarNavigationBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
     }
 
     @Override
@@ -46,7 +49,9 @@
         mNavButtons = findViewById(R.id.nav_buttons);
 
         mNotificationsButton = findViewById(R.id.notifications);
-        mNotificationsButton.setOnClickListener(this::onNotificationsClick);
+        if (mNotificationsButton != null) {
+            mNotificationsButton.setOnClickListener(this::onNotificationsClick);
+        }
     }
 
     void setStatusBar(CarStatusBar carStatusBar) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index c15a013..3530e0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -17,13 +17,9 @@
 package com.android.systemui.statusbar.car;
 
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.content.Intent;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
@@ -45,10 +41,11 @@
 import com.android.systemui.recents.misc.SysUiTaskStackChangeListener;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.car.hvac.HvacController;
 import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.NavigationBarView;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 
 import java.io.FileDescriptor;
@@ -60,6 +57,8 @@
 public class CarStatusBar extends StatusBar implements
         CarBatteryController.BatteryViewHandler {
     private static final String TAG = "CarStatusBar";
+    public static final boolean ENABLE_HVAC_CONNECTION
+            = !SystemProperties.getBoolean("android.car.hvac.demo", true);
 
     private TaskStackListenerImpl mTaskStackListener;
 
@@ -82,23 +81,74 @@
     private boolean mShowRight;
     private boolean mShowBottom;
     private CarFacetButtonController mCarFacetButtonController;
+    private ActivityManagerWrapper mActivityManagerWrapper;
+    private DeviceProvisionedController mDeviceProvisionedController;
+    private boolean mDeviceIsProvisioned = true;
 
     @Override
     public void start() {
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
-        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
+        mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
+        mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener);
 
         mStackScroller.setScrollingEnabled(true);
 
         createBatteryController();
         mCarBatteryController.startListening();
+
+        if (ENABLE_HVAC_CONNECTION) {
+            Log.d(TAG, "Connecting to HVAC service");
+            Dependency.get(HvacController.class).connectToCarService();
+        }
+        mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
+        mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+        if (!mDeviceIsProvisioned) {
+            mDeviceProvisionedController.addCallback(
+                    new DeviceProvisionedController.DeviceProvisionedListener() {
+                        @Override
+                        public void onDeviceProvisionedChanged() {
+                            mDeviceIsProvisioned =
+                                    mDeviceProvisionedController.isDeviceProvisioned();
+                            restartNavBars();
+                        }
+                    });
+        }
     }
 
+    /**
+     * Remove all content from navbars and rebuild them. Used to allow for different nav bars
+     * before and after the device is provisioned
+     */
+    private void restartNavBars() {
+        mCarFacetButtonController.removeAll();
+        if (ENABLE_HVAC_CONNECTION) {
+            Dependency.get(HvacController.class).removeAllComponents();
+        }
+        if (mNavigationBarWindow != null) {
+            mNavigationBarWindow.removeAllViews();
+            mNavigationBarView = null;
+        }
+
+        if (mLeftNavigationBarWindow != null) {
+            mLeftNavigationBarWindow.removeAllViews();
+            mLeftNavigationBarView = null;
+        }
+
+        if (mRightNavigationBarWindow != null) {
+            mRightNavigationBarWindow.removeAllViews();
+            mRightNavigationBarView = null;
+        }
+        buildNavBarContent();
+    }
+
+
     @Override
     public void destroy() {
         mCarBatteryController.stopListening();
         mConnectedDeviceSignalController.stopListening();
+        mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener);
 
         if (mNavigationBarWindow != null) {
             mWindowManager.removeViewImmediate(mNavigationBarWindow);
@@ -114,10 +164,10 @@
             mWindowManager.removeViewImmediate(mRightNavigationBarWindow);
             mRightNavigationBarView = null;
         }
-
         super.destroy();
     }
 
+
     @Override
     protected void makeStatusBarView() {
         super.makeStatusBarView();
@@ -164,132 +214,132 @@
 
     @Override
     protected void createNavigationBar() {
-        mCarFacetButtonController = new CarFacetButtonController(mContext);
-        if (mNavigationBarView != null) {
-            return;
-        }
-
         mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar);
-        if (mShowBottom) {
-            buildBottomBar();
-        }
-
-        int widthForSides = mContext.getResources().getDimensionPixelSize(
-                R.dimen.navigation_bar_height_car_mode);
-
-
         mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar);
-
-        if (mShowLeft) {
-            buildLeft(widthForSides);
-        }
-
         mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar);
 
+        buildNavBarWindows();
+        buildNavBarContent();
+        attachNavBarWindows();
+    }
+
+    private void buildNavBarContent() {
+        if (mShowBottom) {
+            buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+                    R.layout.car_navigation_bar_unprovisioned);
+        }
+
+        if (mShowLeft) {
+            buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+                    R.layout.car_left_navigation_bar_unprovisioned);
+        }
+
         if (mShowRight) {
-            buildRight(widthForSides);
+            buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+                    R.layout.car_right_navigation_bar_unprovisioned);
+        }
+    }
+
+    private void buildNavBarWindows() {
+        if (mShowBottom) {
+
+             mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                    R.layout.navigation_bar_window, null);
+        }
+        if (mShowLeft) {
+            mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        }
+        if (mShowRight) {
+            mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
+                    R.layout.navigation_bar_window, null);
         }
 
     }
 
+    private void attachNavBarWindows() {
 
-    private void buildBottomBar() {
+        if (mShowBottom) {
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            lp.setTitle("CarNavigationBar");
+            lp.windowAnimations = 0;
+            mWindowManager.addView(mNavigationBarWindow, lp);
+        }
+        if (mShowLeft) {
+            int width = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.car_left_navigation_bar_width);
+            WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
+                    width, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            leftlp.setTitle("LeftCarNavigationBar");
+            leftlp.windowAnimations = 0;
+            leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+            leftlp.gravity = Gravity.LEFT;
+            mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
+        }
+        if (mShowRight) {
+            int width = mContext.getResources().getDimensionPixelSize(
+                    R.dimen.car_right_navigation_bar_width);
+            WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
+                    width, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            rightlp.setTitle("RightCarNavigationBar");
+            rightlp.windowAnimations = 0;
+            rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
+            rightlp.gravity = Gravity.RIGHT;
+            mWindowManager.addView(mRightNavigationBarWindow, rightlp);
+        }
+
+    }
+
+    private void buildBottomBar(int layout) {
         // SystemUI requires that the navigation bar view have a parent. Since the regular
         // StatusBar inflates navigation_bar_window as this parent view, use the same view for the
         // CarNavigationBarView.
-        mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
-                R.layout.navigation_bar_window, null);
-        if (mNavigationBarWindow == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
-        }
-
-
-        View.inflate(mContext, R.layout.car_navigation_bar, mNavigationBarWindow);
+        View.inflate(mContext, layout, mNavigationBarWindow);
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
             throw new RuntimeException("Unable to build botom nav bar due to missing layout");
         }
         mNavigationBarView.setStatusBar(this);
-
-
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        lp.setTitle("CarNavigationBar");
-        lp.windowAnimations = 0;
-
-
-        mCarFacetButtonController.addCarNavigationBar(mNavigationBarView);
-        mWindowManager.addView(mNavigationBarWindow, lp);
     }
 
-    private void buildLeft(int widthForSides) {
-        mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext,
-                R.layout.navigation_bar_window, null);
-        if (mLeftNavigationBarWindow == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
-        }
-
-        View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow);
+    private void buildLeft(int layout) {
+        View.inflate(mContext, layout, mLeftNavigationBarWindow);
         mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
         if (mLeftNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
             throw new RuntimeException("Unable to build left nav bar due to missing layout");
         }
         mLeftNavigationBarView.setStatusBar(this);
-        mCarFacetButtonController.addCarNavigationBar(mLeftNavigationBarView);
-
-        WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams(
-                widthForSides, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        leftlp.setTitle("LeftCarNavigationBar");
-        leftlp.windowAnimations = 0;
-        leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-        leftlp.gravity = Gravity.LEFT;
-        mWindowManager.addView(mLeftNavigationBarWindow, leftlp);
     }
 
 
-    private void buildRight(int widthForSides) {
-        mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext,
-                R.layout.navigation_bar_window, null);
-        if (mRightNavigationBarWindow == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window");
-        }
-
-        View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow);
+    private void buildRight(int layout) {
+        View.inflate(mContext, layout, mRightNavigationBarWindow);
         mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
         if (mRightNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
             throw new RuntimeException("Unable to build right nav bar due to missing layout");
         }
-        mRightNavigationBarView.setStatusBar(this);
-        mCarFacetButtonController.addCarNavigationBar(mRightNavigationBarView);
-
-        WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams(
-                widthForSides, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
-                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        rightlp.setTitle("RightCarNavigationBar");
-        rightlp.windowAnimations = 0;
-        rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR;
-        rightlp.gravity = Gravity.RIGHT;
-        mWindowManager.addView(mRightNavigationBarWindow, rightlp);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
new file mode 100644
index 0000000..7d283d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java
@@ -0,0 +1,213 @@
+/*
+ * 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.car.hvac;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.hardware.CarPropertyValue;
+import android.car.hardware.hvac.CarHvacManager;
+import android.car.hardware.hvac.CarHvacManager.CarHvacEventCallback;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Manages the connection to the Car service and delegates value changes to the registered
+ * {@link TemperatureView}s
+ */
+public class HvacController {
+
+    public static final String TAG = "HvacController";
+    public final static int BIND_TO_HVAC_RETRY_DELAY = 5000;
+
+    private Context mContext;
+    private Handler mHandler;
+    private Car mCar;
+    private CarHvacManager mHvacManager;
+    private HashMap<HvacKey, TemperatureView> mTempComponents = new HashMap<>();
+
+    public HvacController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Create connection to the Car service. Note: call backs from the Car service
+     * ({@link CarHvacManager}) will happen on the same thread this method was called from.
+     */
+    public void connectToCarService() {
+        mHandler = new Handler();
+        mCar = Car.createCar(mContext, mServiceConnection, mHandler);
+        if (mCar != null) {
+            // note: this connect call handles the retries
+            mCar.connect();
+        }
+    }
+
+    /**
+     * Registers callbacks and initializes components upon connection.
+     */
+    private ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                service.linkToDeath(mRestart, 0);
+                mHvacManager = (CarHvacManager) mCar.getCarManager(Car.HVAC_SERVICE);
+                mHvacManager.registerCallback(mHardwareCallback);
+                initComponents();
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to correctly connect to HVAC", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            destroyHvacManager();
+        }
+    };
+
+    private void destroyHvacManager() {
+        if (mHvacManager != null) {
+            mHvacManager.unregisterCallback(mHardwareCallback);
+            mHvacManager = null;
+        }
+    }
+
+    /**
+     * If the connection to car service goes away then restart it.
+     */
+    private final IBinder.DeathRecipient mRestart = new IBinder.DeathRecipient() {
+        @Override
+        public void binderDied() {
+            Log.d(TAG, "Death of HVAC triggering a restart");
+            if (mCar != null) {
+                mCar.disconnect();
+            }
+            destroyHvacManager();
+            mHandler.postDelayed(() -> mCar.connect(), BIND_TO_HVAC_RETRY_DELAY);
+        }
+    };
+
+    /**
+     * Add component to list and initialize it if the connection is up.
+     * @param temperatureView
+     */
+    public void addHvacTextView(TemperatureView temperatureView) {
+        mTempComponents.put(
+                new HvacKey(temperatureView.getPropertyId(), temperatureView.getAreaId()),
+                temperatureView);
+        initComponent(temperatureView);
+    }
+
+    private void initComponents() {
+        Iterator<Map.Entry<HvacKey, TemperatureView>> iterator =
+                mTempComponents.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry<HvacKey, TemperatureView> next = iterator.next();
+            initComponent(next.getValue());
+        }
+    }
+
+
+    private void initComponent(TemperatureView view) {
+        int id = view.getPropertyId();
+        int zone = view.getAreaId();
+        try {
+            if (mHvacManager == null || !mHvacManager.isPropertyAvailable(id, zone)) {
+                view.setTemp(Float.NaN);
+                return;
+            }
+            view.setTemp(mHvacManager.getFloatProperty(id, zone));
+        } catch (CarNotConnectedException e) {
+            view.setTemp(Float.NaN);
+            Log.e(TAG, "Failed to get value from hvac service", e);
+        }
+    }
+
+    /**
+     * Callback for getting changes from {@link CarHvacManager} and setting the UI elements to
+     * match.
+     */
+    private final CarHvacEventCallback mHardwareCallback = new CarHvacEventCallback() {
+        @Override
+        public void onChangeEvent(final CarPropertyValue val) {
+            try {
+                int areaId = val.getAreaId();
+                int propertyId = val.getPropertyId();
+                TemperatureView temperatureView = mTempComponents.get(
+                        new HvacKey(propertyId, areaId));
+                if (temperatureView != null) {
+                    float value = (float) val.getValue();
+                    temperatureView.setTemp(value);
+                } // else the data is not of interest
+            } catch (Exception e) {
+                // catch all so we don't take down the sysui if a new data type is
+                // introduced.
+                Log.e(TAG, "Failed handling hvac change event", e);
+            }
+        }
+
+        @Override
+        public void onErrorEvent(final int propertyId, final int zone) {
+            Log.d(TAG, "HVAC error event, propertyId: " + propertyId +
+                    " zone: " + zone);
+        }
+    };
+
+    /**
+     * Removes all registered components. This is useful if you need to rebuild the UI since
+     * components self register.
+     */
+    public void removeAllComponents() {
+        mTempComponents.clear();
+    }
+
+    /**
+     * Key for storing {@link TemperatureView}s in a hash map
+     */
+    private static class HvacKey {
+
+        int mPropertyId;
+        int mAreaId;
+
+        public HvacKey(int propertyId, int areaId) {
+            mPropertyId = propertyId;
+            mAreaId = areaId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            HvacKey hvacKey = (HvacKey) o;
+            return mPropertyId == hvacKey.mPropertyId &&
+                    mAreaId == hvacKey.mAreaId;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mPropertyId, mAreaId);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
new file mode 100644
index 0000000..4049ec3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/TemperatureView.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car.hvac;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+
+/**
+ * Simple text display of HVAC properties, It is designed to show temperature and is configured in
+ * the XML.
+ * XML properties:
+ * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+ * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+ * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
+ *
+ * Note: It registers itself with {@link HvacController}
+ */
+public class TemperatureView extends TextView {
+
+    private final int mAreaId;
+    private final int mPropertyId;
+    private final String mTempFormat;
+
+    public TemperatureView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.TemperatureView);
+        mAreaId = typedArray.getInt(R.styleable.TemperatureView_hvacAreaId,-1);
+        mPropertyId = typedArray.getInt(R.styleable.TemperatureView_hvacPropertyId, -1);
+        String format = typedArray.getString(R.styleable.TemperatureView_hvacTempFormat);
+        mTempFormat = (format == null) ? "%.1f\u00B0" : format;
+
+        // register with controller
+        HvacController hvacController = Dependency.get(HvacController.class);
+        hvacController.addHvacTextView(this);
+    }
+
+    /**
+     * Formats the float for display
+     * @param temp - The current temp or NaN
+     */
+    public void setTemp(float temp) {
+        if (Float.isNaN(temp)) {
+            setText("--");
+            return;
+        }
+        setText(String.format(mTempFormat, temp));
+    }
+
+    /**
+     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
+     */
+    public int getPropertyId() {
+        return mPropertyId;
+    }
+
+    /**
+     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
+     */
+    public int getAreaId() {
+        return mAreaId;
+    }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index f42473d..75b31c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -17,8 +17,6 @@
 import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
 import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
 
-import static com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster;
-
 import android.annotation.Nullable;
 import android.app.Fragment;
 import android.app.StatusBarManager;
@@ -34,7 +32,6 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.SignalClusterView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.EncryptionHelper;
@@ -63,7 +60,6 @@
     private int mDisabled1;
     private StatusBar mStatusBarComponent;
     private DarkIconManager mDarkIconManager;
-    private SignalClusterView mSignalClusterView;
     private View mOperatorNameFrame;
 
     private SignalCallback mSignalCallback = new SignalCallback() {
@@ -99,9 +95,6 @@
         Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager);
         mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
         mClockView = mStatusBar.findViewById(R.id.clock);
-        mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster);
-        Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
-        // Default to showing until we know otherwise.
         showSystemIconArea(false);
         initEmergencyCryptkeeperText();
         initOperatorName();
@@ -128,7 +121,6 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
-        Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView);
         Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager);
         if (mNetworkController.hasEmergencyCryptKeeperText()) {
             mNetworkController.removeCallback(mSignalCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
index 3f9ae80..80c4eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java
@@ -69,7 +69,7 @@
         mReceivers.remove(object);
     }
 
-    public void applyDark(ImageView object) {
+    public void applyDark(DarkReceiver object) {
         mReceivers.get(object).onDarkChanged(mTintArea, mDarkIntensity, mIconTint);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index edfd02b..48540b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -20,25 +20,33 @@
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.settingslib.Utils;
 import com.android.systemui.DemoMode;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.policy.LocationControllerImpl;
-import com.android.systemui.util.leak.LeakDetector;
+import java.util.ArrayList;
 
 public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver {
+    private static final String TAG = "DemoStatusIcons";
+
     private final LinearLayout mStatusIcons;
+    private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
     private final int mIconSize;
 
+    private StatusBarWifiView mWifiView;
     private boolean mDemoMode;
     private int mColor;
 
@@ -56,6 +64,7 @@
     }
 
     public void remove() {
+        mMobileViews.clear();
         ((ViewGroup) getParent()).removeView(this);
     }
 
@@ -66,7 +75,7 @@
 
     private void updateColors() {
         for (int i = 0; i < getChildCount(); i++) {
-            StatusBarIconView child = (StatusBarIconView) getChildAt(i);
+            StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i);
             child.setStaticDrawableColor(mColor);
         }
     }
@@ -145,6 +154,7 @@
         }
     }
 
+    /// Can only be used to update non-signal related slots
     private void updateSlot(String slot, String iconPkg, int iconId) {
         if (!mDemoMode) return;
         if (iconPkg == null) {
@@ -152,7 +162,11 @@
         }
         int removeIndex = -1;
         for (int i = 0; i < getChildCount(); i++) {
-            StatusBarIconView v = (StatusBarIconView) getChildAt(i);
+            View child = getChildAt(i);
+            if (!(child instanceof StatusBarIconView)) {
+                continue;
+            }
+            StatusBarIconView v = (StatusBarIconView) child;
             if (slot.equals(v.getTag())) {
                 if (iconId == 0) {
                     removeIndex = i;
@@ -182,8 +196,101 @@
         addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize));
     }
 
+    public void addDemoWifiView(WifiIconState state) {
+        Log.d(TAG, "addDemoWifiView: ");
+        StatusBarWifiView view = StatusBarWifiView.fromContext(mContext);
+        view.setSlot(state.slot);
+
+        int viewIndex = getChildCount();
+        // If we have mobile views, put wifi before them
+        for (int i = 0; i < getChildCount(); i++) {
+            View child = getChildAt(i);
+            if (child instanceof StatusBarMobileView) {
+                viewIndex = i;
+                break;
+            }
+        }
+
+        mWifiView = view;
+        mWifiView.applyWifiState(state);
+        mWifiView.setStaticDrawableColor(mColor);
+        addView(view, viewIndex);
+    }
+
+    public void updateWifiState(WifiIconState state) {
+        Log.d(TAG, "updateWifiState: ");
+        if (mWifiView == null) {
+            addDemoWifiView(state);
+        } else {
+            mWifiView.applyWifiState(state);
+        }
+    }
+
+    public void addMobileView(MobileIconState state) {
+        Log.d(TAG, "addMobileView: ");
+        StatusBarMobileView view = StatusBarMobileView.fromContext(mContext);
+
+        view.setSlot(state.slot);
+        view.applyMobileState(state);
+        view.setStaticDrawableColor(mColor);
+
+        // mobile always goes at the end
+        mMobileViews.add(view);
+        addView(view, getChildCount());
+    }
+
+    public void updateMobileState(MobileIconState state) {
+        Log.d(TAG, "updateMobileState: ");
+        // If the view for this subId exists already, use it
+        for (int i = 0; i < mMobileViews.size(); i++) {
+            StatusBarMobileView view = mMobileViews.get(i);
+            if (view.getState().subId == state.subId) {
+                view.applyMobileState(state);
+                return;
+            }
+        }
+
+        // Else we have to add it
+        addMobileView(state);
+    }
+
+    public void onRemoveIcon(StatusIconDisplayable view) {
+        if (view.getSlot().equals("wifi")) {
+            removeView(mWifiView);
+            mWifiView = null;
+        } else {
+            StatusBarMobileView mobileView = matchingMobileView(view);
+            if (mobileView != null) {
+                removeView(mobileView);
+                mMobileViews.remove(mobileView);
+            }
+        }
+    }
+
+    private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) {
+        if (!(otherView instanceof StatusBarMobileView)) {
+            return null;
+        }
+
+        StatusBarMobileView v = (StatusBarMobileView) otherView;
+        for (StatusBarMobileView view : mMobileViews) {
+            if (view.getState().subId == v.getState().subId) {
+                return view;
+            }
+        }
+
+        return null;
+    }
+
     @Override
     public void onDarkChanged(Rect area, float darkIntensity, int tint) {
         setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint));
+
+        if (mWifiView != null) {
+            mWifiView.onDarkChanged(area, darkIntensity, tint);
+        }
+        for (StatusBarMobileView view : mMobileViews) {
+            view.onDarkChanged(area, darkIntensity, tint);
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index a2b1013..6576eb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -399,4 +399,11 @@
         pw.print("   mMode="); pw.println(mMode);
         pw.print("   mWakeLock="); pw.println(mWakeLock);
     }
+
+    public boolean isWakeAndUnlock() {
+        return mMode == MODE_UNLOCK
+                || mMode == MODE_WAKE_AND_UNLOCK
+                || mMode == MODE_WAKE_AND_UNLOCK_PULSING
+                || mMode == MODE_WAKE_AND_UNLOCK_FROM_DREAM;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index fcd4e8f..df2b817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -334,6 +334,20 @@
         return mKeyguardView != null && mKeyguardView.hasDismissActions();
     }
 
+    public int getTop() {
+        if (mKeyguardView == null) {
+            return 0;
+        }
+
+        int top = mKeyguardView.getTop();
+        // The password view has an extra top padding that should be ignored.
+        if (mKeyguardView.getCurrentSecurityMode() == SecurityMode.Password) {
+            View messageArea = mKeyguardView.findViewById(R.id.keyguard_message_area);
+            top += messageArea.getTop();
+        }
+        return top;
+    }
+
     protected void ensureView() {
         // Removal of the view might be deferred to reduce unlock latency,
         // in this case we need to force the removal, otherwise we'll
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 19e8295..3d7067d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -114,6 +114,11 @@
     private boolean mTracking;
 
     /**
+     * Distance in pixels between the top of the screen and the first view of the bouncer.
+     */
+    private int mBouncerTop;
+
+    /**
      * Refreshes the dimension values.
      */
     public void loadDimens(Resources res) {
@@ -129,7 +134,7 @@
 
     public void setup(int minTopMargin, int maxShadeBottom, int notificationStackHeight,
             float expandedHeight, float maxPanelHeight, int parentHeight, int keyguardStatusHeight,
-            float dark, boolean secure, boolean tracking) {
+            float dark, boolean secure, boolean tracking, int bouncerTop) {
         mMinTopMargin = minTopMargin + mContainerTopPadding;
         mMaxShadeBottom = maxShadeBottom;
         mNotificationStackHeight = notificationStackHeight;
@@ -140,6 +145,7 @@
         mDarkAmount = dark;
         mCurrentlySecure = secure;
         mTracking = tracking;
+        mBouncerTop = bouncerTop;
     }
 
     public void run(Result result) {
@@ -189,8 +195,10 @@
     private int getClockY() {
         // Dark: Align the bottom edge of the clock at about half of the screen:
         final float clockYDark = getMaxClockY() + burnInPreventionOffsetY();
-        float clockYRegular = getExpandedClockPosition();
-        float clockYTarget = mCurrentlySecure ? mMinTopMargin : -mKeyguardStatusHeight;
+        final float clockYRegular = getExpandedClockPosition();
+        final boolean hasEnoughSpace = mMinTopMargin + mKeyguardStatusHeight < mBouncerTop;
+        float clockYTarget = mCurrentlySecure && hasEnoughSpace ?
+                mMinTopMargin : -mKeyguardStatusHeight;
 
         // Move clock up while collapsing the shade
         float shadeExpansion = mExpandedHeight / mMaxPanelHeight;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 994c0ab..b817809 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -207,7 +207,6 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         updateLayoutConsideringCutout();
-        setSignalClusterLayoutWidth();
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
@@ -296,17 +295,6 @@
         return true;
     }
 
-    //TODO: Something is setting signal_cluster to MATCH_PARENT. Why?
-    private void setSignalClusterLayoutWidth() {
-        View signalCluster = findViewById(R.id.signal_cluster);
-        if (signalCluster == null) {
-            return;
-        }
-
-        LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams();
-        lp.width = LinearLayout.LayoutParams.WRAP_CONTENT;
-    }
-
     public void setListening(boolean listening) {
         if (listening == mBatteryListening) {
             return;
@@ -459,7 +447,6 @@
         mIconManager.setTint(iconColor);
         Rect tintArea = new Rect(0, 0, 0, 0);
 
-        applyDarkness(R.id.signal_cluster, tintArea, intensity, iconColor);
         applyDarkness(R.id.battery, tintArea, intensity, iconColor);
         applyDarkness(R.id.clock, tintArea, intensity, iconColor);
         // Reload user avatar
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 a39800d..58f8baa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -171,7 +171,7 @@
     private final OverviewProxyListener mOverviewProxyListener = new OverviewProxyListener() {
         @Override
         public void onConnectionChanged(boolean isConnected) {
-            mNavigationBarView.onOverviewProxyConnectionChanged(isConnected);
+            mNavigationBarView.updateStates();
             updateScreenPinningGestures();
             WindowManagerWrapper.getInstance()
                     .setNavBarVirtualKeyHapticFeedbackEnabled(!isConnected);
@@ -188,6 +188,7 @@
         @Override
         public void onInteractionFlagsChanged(@InteractionType int flags) {
             mNavigationBarView.updateStates();
+            updateScreenPinningGestures();
         }
     };
 
@@ -925,7 +926,9 @@
     private boolean onLongPressRecents() {
         if (mRecents == null || !ActivityManager.supportsMultiWindow(getContext())
                 || !mDivider.getView().getSnapAlgorithm().isSplitScreenFeasible()
-                || Recents.getConfiguration().isLowRamDevice) {
+                || Recents.getConfiguration().isLowRamDevice
+                // If we are connected to the overview service, then disable the recents button
+                || mOverviewProxyService.getProxy() != null) {
             return 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 88bc6ea..84582b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -79,7 +79,6 @@
 import java.util.function.Consumer;
 
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
-import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_HIDE_BACK_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_OVERVIEW;
@@ -385,17 +384,13 @@
     }
 
     public boolean isQuickStepSwipeUpEnabled() {
-        return mOverviewProxyService.getProxy() != null
-                && isOverviewEnabled()
-                && ((mOverviewProxyService.getInteractionFlags()
-                        & FLAG_DISABLE_SWIPE_UP) == 0);
+        return mOverviewProxyService.shouldShowSwipeUpUI() && isOverviewEnabled();
     }
 
     public boolean isQuickScrubEnabled() {
         return SystemProperties.getBoolean("persist.quickstep.scrub.enabled", true)
                 && mOverviewProxyService.getProxy() != null && isOverviewEnabled()
-                && ((mOverviewProxyService.getInteractionFlags()
-                        & FLAG_DISABLE_QUICK_SCRUB) == 0);
+                && ((mOverviewProxyService.getInteractionFlags() & FLAG_DISABLE_QUICK_SCRUB) == 0);
     }
 
     private void updateCarModeIcons(Context ctx) {
@@ -468,7 +463,7 @@
     private KeyButtonDrawable chooseNavigationIconDrawable(Context ctx, @DrawableRes int iconLight,
             @DrawableRes int iconDark, @DrawableRes int quickStepIconLight,
             @DrawableRes int quickStepIconDark) {
-        final boolean quickStepEnabled = isQuickStepSwipeUpEnabled() || isQuickScrubEnabled();
+        final boolean quickStepEnabled = mOverviewProxyService.shouldShowSwipeUpUI();
         return quickStepEnabled
                 ? getDrawable(ctx, quickStepIconLight, quickStepIconDark)
                 : getDrawable(ctx, iconLight, iconDark);
@@ -681,6 +676,7 @@
         updateSlippery();
         reloadNavIcons();
         updateNavButtonIcons();
+        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
     }
 
     private void updateSlippery() {
@@ -816,11 +812,6 @@
         }
     }
 
-    public void onOverviewProxyConnectionChanged(boolean isConnected) {
-        updateStates();
-        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
-    }
-
     @Override
     protected void onDraw(Canvas canvas) {
         mGestureHelper.onDraw(canvas);
@@ -1045,7 +1036,7 @@
         onPluginDisconnected(null); // Create default gesture helper
         Dependency.get(PluginManager.class).addPluginListener(this,
                 NavGesture.class, false /* Only one */);
-        setUpSwipeUpOnboarding(mOverviewProxyService.getProxy() != null);
+        setUpSwipeUpOnboarding(isQuickStepSwipeUpEnabled());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
index b75c7e0..ca65965 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java
@@ -169,7 +169,7 @@
             if (group.suppressed) {
                 handleSuppressedSummaryHeadsUpped(group.summary);
             }
-            if (!mIsUpdatingUnchangedGroup) {
+            if (!mIsUpdatingUnchangedGroup && mListener != null) {
                 mListener.onGroupsChanged();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 9063dea..b6a11f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -150,7 +150,7 @@
 
         // showAmbient == show in shade but not shelf
         if (!showAmbient && mEntryManager.getNotificationData().shouldSuppressStatusBar(
-                entry.key)) {
+                entry.notification)) {
             return false;
         }
 
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 cccda90..27ca0d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -160,6 +160,7 @@
     protected int mQsMinExpansionHeight;
     protected int mQsMaxExpansionHeight;
     private int mQsPeekHeight;
+    private int mBouncerTop;
     private boolean mStackScrollerOverscrolling;
     private boolean mQsExpansionFromOverscroll;
     private float mLastOverscroll;
@@ -476,7 +477,8 @@
                     mKeyguardStatusView.getHeight(),
                     mDarkAmount,
                     mStatusBar.isKeyguardCurrentlySecure(),
-                    mTracking);
+                    mTracking,
+                    mBouncerTop);
             mClockPositionAlgorithm.run(mClockPositionResult);
             if (animate || mClockAnimator != null) {
                 startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY);
@@ -550,6 +552,11 @@
         return count;
     }
 
+    public void setBouncerTop(int bouncerTop) {
+        mBouncerTop = bouncerTop;
+        positionClockAndNotifications();
+    }
+
     private void startClockAnimation(int x, int y) {
         if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index c326fee..3e7b0d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -297,7 +297,7 @@
             mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID,
                     mContext.getString(R.string.accessibility_location_active));
         } else {
-            mIconController.removeIcon(mSlotLocation);
+            mIconController.removeAllIconsForSlot(mSlotLocation);
         }
     }
 
@@ -363,16 +363,16 @@
             zenDescription = mContext.getString(R.string.interruption_level_priority);
         }
 
-        if (DndTile.isVisible(mContext) && !DndTile.isCombinedIcon(mContext)
-                && audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
-            volumeVisible = true;
-            volumeIconId = R.drawable.stat_sys_ringer_silent;
-            volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
-        } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
+        if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
                 audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
             volumeVisible = true;
             volumeIconId = R.drawable.stat_sys_ringer_vibrate;
             volumeDescription = mContext.getString(R.string.accessibility_ringer_vibrate);
+        } else if (zen != Global.ZEN_MODE_NO_INTERRUPTIONS && zen != Global.ZEN_MODE_ALARMS &&
+                audioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
+            volumeVisible = true;
+            volumeIconId = R.drawable.stat_sys_ringer_silent;
+            volumeDescription = mContext.getString(R.string.accessibility_ringer_silent);
         }
 
         if (zenVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
index fb1addf..12bdfc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java
@@ -32,7 +32,7 @@
     private final PhoneStatusBarView mView;
     private final float mIconAlphaWhenOpaque;
 
-    private View mLeftSide, mStatusIcons, mSignalCluster, mBattery, mClock;
+    private View mLeftSide, mStatusIcons, mBattery, mClock;
     private Animator mCurrentAnimation;
 
     public PhoneStatusBarTransitions(PhoneStatusBarView view) {
@@ -45,7 +45,6 @@
     public void init() {
         mLeftSide = mView.findViewById(R.id.notification_icon_area);
         mStatusIcons = mView.findViewById(R.id.statusIcons);
-        mSignalCluster = mView.findViewById(R.id.signal_cluster);
         mBattery = mView.findViewById(R.id.battery);
         mClock = mView.findViewById(R.id.clock);
         applyModeBackground(-1, getMode(), false /*animate*/);
@@ -90,7 +89,6 @@
             anims.playTogether(
                     animateTransitionTo(mLeftSide, newAlpha),
                     animateTransitionTo(mStatusIcons, newAlpha),
-                    animateTransitionTo(mSignalCluster, newAlpha),
                     animateTransitionTo(mBattery, newAlphaBC),
                     animateTransitionTo(mClock, newAlphaBC)
                     );
@@ -102,7 +100,6 @@
         } else {
             mLeftSide.setAlpha(newAlpha);
             mStatusIcons.setAlpha(newAlpha);
-            mSignalCluster.setAlpha(newAlpha);
             mBattery.setAlpha(newAlphaBC);
             mClock.setAlpha(newAlphaBC);
         }
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 7a6e98d..2c025b5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -347,7 +347,9 @@
         if (mExpansionFraction != fraction) {
             mExpansionFraction = fraction;
 
-            if (!(mState == ScrimState.UNLOCKED || mState == ScrimState.KEYGUARD)) {
+            final boolean keyguardOrUnlocked = mState == ScrimState.UNLOCKED
+                    || mState == ScrimState.KEYGUARD;
+            if (!keyguardOrUnlocked || !mExpansionAffectsAlpha) {
                 return;
             }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 5b734eb..f4b6c38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -156,7 +156,6 @@
                 mCurrentBehindTint = Color.BLACK;
                 mBlankScreen = true;
             } else {
-                // Scrims should still be black at the end of the transition.
                 mCurrentInFrontTint = Color.TRANSPARENT;
                 mCurrentBehindTint = Color.TRANSPARENT;
                 mBlankScreen = false;
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 ddb67df..750d2a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -348,6 +348,7 @@
     protected boolean mBouncerShowing;
 
     private PhoneStatusBarPolicy mIconPolicy;
+    private StatusBarSignalPolicy mSignalPolicy;
 
     private VolumeComponent mVolumeComponent;
     private BrightnessMirrorController mBrightnessMirrorController;
@@ -408,6 +409,7 @@
     protected NotificationEntryManager mEntryManager;
     protected NotificationViewHierarchyManager mViewHierarchyManager;
     protected AppOpsListener mAppOpsListener;
+    protected KeyguardViewMediator mKeyguardViewMediator;
     private ZenModeController mZenController;
 
     /**
@@ -561,7 +563,7 @@
     };
 
     private KeyguardUserSwitcher mKeyguardUserSwitcher;
-    private UserSwitcherController mUserSwitcherController;
+    protected UserSwitcherController mUserSwitcherController;
     private NetworkController mNetworkController;
     private KeyguardMonitorImpl mKeyguardMonitor
             = (KeyguardMonitorImpl) Dependency.get(KeyguardMonitor.class);
@@ -587,7 +589,7 @@
         }
     };
     private boolean mNoAnimationOnNextBarModeChange;
-    private FalsingManager mFalsingManager;
+    protected FalsingManager mFalsingManager;
 
     private final KeyguardUpdateMonitorCallback mUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -632,6 +634,7 @@
         mAppOpsListener = Dependency.get(AppOpsListener.class);
         mAppOpsListener.setUpWithPresenter(this, mEntryManager);
         mZenController = Dependency.get(ZenModeController.class);
+        mKeyguardViewMediator = getComponent(KeyguardViewMediator.class);
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
@@ -745,6 +748,7 @@
 
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
+        mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController);
 
         mUnlockMethodCache = UnlockMethodCache.getInstance(mContext);
         mUnlockMethodCache.addListener(this);
@@ -1129,7 +1133,6 @@
     }
 
     protected void reevaluateStyles() {
-        inflateSignalClusters();
         inflateFooterView();
         updateFooter();
         inflateEmptyShadeView();
@@ -1143,36 +1146,6 @@
         }
     }
 
-    private void inflateSignalClusters() {
-        if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar);
-    }
-
-    public static SignalClusterView reinflateSignalCluster(View view) {
-        Context context = view.getContext();
-        SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster);
-        if (signalCluster != null) {
-            ViewParent parent = signalCluster.getParent();
-            if (parent instanceof ViewGroup) {
-                ViewGroup viewParent = (ViewGroup) parent;
-                int index = viewParent.indexOfChild(signalCluster);
-                viewParent.removeView(signalCluster);
-                SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context)
-                        .inflate(R.layout.signal_cluster_view, viewParent, false);
-                ViewGroup.MarginLayoutParams layoutParams =
-                        (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams();
-                layoutParams.setMarginsRelative(
-                        context.getResources().getDimensionPixelSize(
-                                R.dimen.signal_cluster_margin_start),
-                        0, 0, 0);
-                newCluster.setLayoutParams(layoutParams);
-                viewParent.addView(newCluster, index);
-                return newCluster;
-            }
-            return signalCluster;
-        }
-        return null;
-    }
-
     private void inflateEmptyShadeView() {
         if (mStackScroller == null) {
             return;
@@ -3446,6 +3419,13 @@
         return updateIsKeyguard();
     }
 
+    /**
+     * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
+     */
+    public boolean isFullScreenUserSwitcherState() {
+        return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
+    }
+
     private boolean updateIsKeyguard() {
         boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
                 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -4628,6 +4608,7 @@
         boolean dozing = mDozingRequested && mState == StatusBarState.KEYGUARD
                 || mFingerprintUnlockController.getMode()
                         == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+        final boolean alwaysOn = DozeParameters.getInstance(mContext).getAlwaysOn();
         // When in wake-and-unlock we may not have received a change to mState
         // but we still should not be dozing, manually set to false.
         if (mFingerprintUnlockController.getMode() ==
@@ -4635,6 +4616,7 @@
             dozing = false;
         }
         mDozing = dozing;
+        mKeyguardViewMediator.setAodShowing(mDozing && alwaysOn);
         mStatusBarWindowManager.setDozing(mDozing);
         mStatusBarKeyguardViewManager.setDozing(mDozing);
         if (mAmbientIndicationContainer instanceof DozeReceiver) {
@@ -4655,9 +4637,8 @@
         final boolean wakeAndUnlocking = mFingerprintUnlockController.getMode()
                 == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
 
-        // Do not animate the scrim expansion when it's triggered by the fingerprint sensor.
-        mScrimController.setExpansionAffectsAlpha(mFingerprintUnlockController.getMode()
-                != FingerprintUnlockController.MODE_UNLOCK);
+        // Do not animate the scrim expansion when triggered by the fingerprint sensor.
+        mScrimController.setExpansionAffectsAlpha(!mFingerprintUnlockController.isWakeAndUnlock());
 
         if (mBouncerShowing) {
             // Bouncer needs the front scrim when it's on top of an activity,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 956bebb..94e004b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -16,12 +16,16 @@
 
 import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS;
 import static android.app.StatusBarManager.DISABLE_NONE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
 
 import android.content.Context;
 import android.os.Bundle;
 import android.support.annotation.VisibleForTesting;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -33,19 +37,39 @@
 import com.android.systemui.DemoMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.util.Utils.DisableStateTracker;
+import java.util.List;
 
 public interface StatusBarIconController {
 
+    /**
+     * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon
+     * in that slot and not added as a sub slot.
+     */
+    public static final int TAG_PRIMARY = 0;
+
     public void addIconGroup(IconManager iconManager);
     public void removeIconGroup(IconManager iconManager);
     public void setExternalIcon(String slot);
     public void setIcon(String slot, int resourceId, CharSequence contentDescription);
     public void setIcon(String slot, StatusBarIcon icon);
-    public void setIconVisibility(String slotTty, boolean b);
-    public void removeIcon(String slot);
+    public void setSignalIcon(String slot, WifiIconState state);
+    public void setMobileIcons(String slot, List<MobileIconState> states);
+    public void setIconVisibility(String slot, boolean b);
+    /**
+     * If you don't know what to pass for `tag`, either remove all icons for slot, or use
+     * TAG_PRIMARY to refer to the first icon at a given slot.
+     */
+    public void removeIcon(String slot, int tag);
+    public void removeAllIconsForSlot(String slot);
 
     public static final String ICON_BLACKLIST = "icon_blacklist";
 
@@ -79,9 +103,9 @@
 
         @Override
         protected void onIconAdded(int index, String slot, boolean blocked,
-                StatusBarIcon icon) {
-            StatusBarIconView v = addIcon(index, slot, blocked, icon);
-            mDarkIconDispatcher.addDarkReceiver(v);
+                StatusBarIconHolder holder) {
+            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
+            mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view);
         }
 
         @Override
@@ -95,21 +119,21 @@
         @Override
         protected void destroy() {
             for (int i = 0; i < mGroup.getChildCount(); i++) {
-                mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i));
+                mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i));
             }
             mGroup.removeAllViews();
         }
 
         @Override
         protected void onRemoveIcon(int viewIndex) {
-            mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex));
+            mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex));
             super.onRemoveIcon(viewIndex);
         }
 
         @Override
         public void onSetIcon(int viewIndex, StatusBarIcon icon) {
             super.onSetIcon(viewIndex, icon);
-            mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex));
+            mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex));
         }
 
         @Override
@@ -135,17 +159,18 @@
         }
 
         @Override
-        protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) {
-            StatusBarIconView v = addIcon(index, slot, blocked, icon);
-            v.setStaticDrawableColor(mColor);
+        protected void onIconAdded(int index, String slot, boolean blocked,
+                StatusBarIconHolder holder) {
+            StatusIconDisplayable view = addHolder(index, slot, blocked, holder);
+            view.setStaticDrawableColor(mColor);
         }
 
         public void setTint(int color) {
             mColor = color;
             for (int i = 0; i < mGroup.getChildCount(); i++) {
                 View child = mGroup.getChildAt(i);
-                if (child instanceof StatusBarIconView) {
-                    StatusBarIconView icon = (StatusBarIconView) child;
+                if (child instanceof StatusIconDisplayable) {
+                    StatusIconDisplayable icon = (StatusIconDisplayable) child;
                     icon.setStaticDrawableColor(mColor);
                 }
             }
@@ -171,6 +196,7 @@
 
         // Enables SystemUI demo mode to take effect in this group
         protected boolean mDemoable = true;
+        private boolean mIsInDemoMode;
         protected DemoStatusIcons mDemoStatusIcons;
 
         public IconManager(ViewGroup group) {
@@ -205,10 +231,27 @@
         }
 
         protected void onIconAdded(int index, String slot, boolean blocked,
-                StatusBarIcon icon) {
-            addIcon(index, slot, blocked, icon);
+                StatusBarIconHolder holder) {
+            addHolder(index, slot, blocked, holder);
         }
 
+        protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked,
+                StatusBarIconHolder holder) {
+            switch (holder.getType()) {
+                case TYPE_ICON:
+                    return addIcon(index, slot, blocked, holder.getIcon());
+
+                case TYPE_WIFI:
+                    return addSignalIcon(index, slot, holder.getWifiState());
+
+                case TYPE_MOBILE:
+                    return addMobileIcon(index, slot, holder.getMobileState());
+            }
+
+            return null;
+        }
+
+        @VisibleForTesting
         protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
                 StatusBarIcon icon) {
             StatusBarIconView view = onCreateStatusBarIconView(slot, blocked);
@@ -218,10 +261,45 @@
         }
 
         @VisibleForTesting
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView view = onCreateStatusBarWifiView(slot);
+            view.applyWifiState(state);
+            mGroup.addView(view, index, onCreateLayoutParams());
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.addDemoWifiView(state);
+            }
+            return view;
+        }
+
+        @VisibleForTesting
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView view = onCreateStatusBarMobileView(slot);
+            view.applyMobileState(state);
+            mGroup.addView(view, index, onCreateLayoutParams());
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.addMobileView(state);
+            }
+            return view;
+        }
+
+        private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
             return new StatusBarIconView(mContext, slot, null, blocked);
         }
 
+        private StatusBarWifiView onCreateStatusBarWifiView(String slot) {
+            StatusBarWifiView view = StatusBarWifiView.fromContext(mContext);
+            view.setSlot(slot);
+            return view;
+        }
+
+        private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
+            StatusBarMobileView view = StatusBarMobileView.fromContext(mContext);
+            view.setSlot(slot);
+            return view;
+        }
+
         protected LinearLayout.LayoutParams onCreateLayoutParams() {
             return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
         }
@@ -256,6 +334,9 @@
         }
 
         protected void onRemoveIcon(int viewIndex) {
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
+            }
             mGroup.removeViewAt(viewIndex);
         }
 
@@ -264,17 +345,59 @@
             view.set(icon);
         }
 
+        public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) {
+            switch (holder.getType()) {
+                case TYPE_ICON:
+                    onSetIcon(viewIndex, holder.getIcon());
+                    return;
+                case TYPE_WIFI:
+                    onSetSignalIcon(viewIndex, holder.getWifiState());
+                    return;
+
+                case TYPE_MOBILE:
+                    onSetMobileIcon(viewIndex, holder.getMobileState());
+                default:
+                    break;
+            }
+        }
+
+        public void onSetSignalIcon(int viewIndex, WifiIconState state) {
+            StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex);
+            if (wifiView != null) {
+                wifiView.applyWifiState(state);
+            }
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.updateWifiState(state);
+            }
+        }
+
+        public void onSetMobileIcon(int viewIndex, MobileIconState state) {
+            StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex);
+            if (view != null) {
+                view.applyMobileState(state);
+            }
+
+            if (mIsInDemoMode) {
+                mDemoStatusIcons.updateMobileState(state);
+            }
+        }
+
         @Override
         public void dispatchDemoCommand(String command, Bundle args) {
             if (!mDemoable) {
                 return;
             }
 
-            if (mDemoStatusIcons != null && command.equals(COMMAND_EXIT)) {
-                mDemoStatusIcons.dispatchDemoCommand(command, args);
-                exitDemoMode();
+            if (command.equals(COMMAND_EXIT)) {
+                if (mDemoStatusIcons != null) {
+                    mDemoStatusIcons.dispatchDemoCommand(command, args);
+                    exitDemoMode();
+                }
+                mIsInDemoMode = false;
             } else {
                 if (mDemoStatusIcons == null) {
+                    mIsInDemoMode = true;
                     mDemoStatusIcons = createDemoStatusIcons();
                 }
                 mDemoStatusIcons.dispatchDemoCommand(command, args);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index 8f5e705..510af03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -16,13 +16,14 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.graphics.drawable.Icon;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.Dependency;
@@ -30,7 +31,9 @@
 import com.android.systemui.R;
 import com.android.systemui.SysUiServiceProvider;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
 import com.android.systemui.statusbar.policy.IconLogger;
@@ -40,8 +43,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
 
-import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.STATUS_BAR_ICON_MANAGER_TAG;
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 
 /**
  * Receives the callbacks from CommandQueue related to icons and tracks the state of
@@ -50,20 +54,25 @@
  */
 public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable,
         ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController {
+
     private static final String TAG = "StatusBarIconController";
 
     private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
     private final ArraySet<String> mIconBlacklist = new ArraySet<>();
     private final IconLogger mIconLogger = Dependency.get(IconLogger.class);
 
+    // Points to light or dark context depending on the... context?
     private Context mContext;
-    private DemoStatusIcons mDemoStatusIcons;
-    private IconManager mStatusBarIconManager;
+    private Context mLightContext;
+    private Context mDarkContext;
+
+    private boolean mIsDark = false;
 
     public StatusBarIconControllerImpl(Context context) {
         super(context.getResources().getStringArray(
                 com.android.internal.R.array.config_statusBarIcons));
         Dependency.get(ConfigurationController.class).addCallback(this);
+
         mContext = context;
 
         loadDimens();
@@ -76,12 +85,16 @@
     @Override
     public void addIconGroup(IconManager group) {
         mIconGroups.add(group);
-        for (int i = 0; i < mIcons.size(); i++) {
-            StatusBarIcon icon = mIcons.get(i);
-            if (icon != null) {
-                String slot = mSlots.get(i);
-                boolean blocked = mIconBlacklist.contains(slot);
-                group.onIconAdded(getViewIndex(getSlotIndex(slot)), slot, blocked, icon);
+        List<Slot> allSlots = getSlots();
+        for (int i = 0; i < allSlots.size(); i++) {
+            Slot slot = allSlots.get(i);
+            List<StatusBarIconHolder> holders = slot.getHolderList();
+            boolean blocked = mIconBlacklist.contains(slot.getName());
+
+            for (StatusBarIconHolder holder : holders) {
+                int tag = holder.getTag();
+                int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
+                group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
             }
         }
     }
@@ -99,104 +112,209 @@
         }
         mIconBlacklist.clear();
         mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue));
-        ArrayList<StatusBarIcon> current = new ArrayList<>(mIcons);
-        ArrayList<String> currentSlots = new ArrayList<>(mSlots);
+        ArrayList<Slot> currentSlots = getSlots();
+        ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
+
+        // This is a little hacky... Peel off all of the holders on all of the slots
+        // but keep them around so they can be re-added
+
         // Remove all the icons.
-        for (int i = current.size() - 1; i >= 0; i--) {
-            removeIcon(currentSlots.get(i));
+        for (int i = currentSlots.size() - 1; i >= 0; i--) {
+            Slot s = currentSlots.get(i);
+            slotsToReAdd.put(s, s.getHolderList());
+            removeAllIconsForSlot(s.getName());
         }
+
         // Add them all back
-        for (int i = 0; i < current.size(); i++) {
-            setIcon(currentSlots.get(i), current.get(i));
+        for (int i = 0; i < currentSlots.size(); i++) {
+            Slot item = currentSlots.get(i);
+            List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item);
+            if (iconsForSlot == null) continue;
+            for (StatusBarIconHolder holder : iconsForSlot) {
+                setIcon(getSlotIndex(item.getName()), holder);
+            }
         }
     }
 
     private void loadDimens() {
     }
 
-    private void addSystemIcon(int index, StatusBarIcon icon) {
-        String slot = getSlot(index);
-        int viewIndex = getViewIndex(index);
+    private void addSystemIcon(int index, StatusBarIconHolder holder) {
+        String slot = getSlotName(index);
+        int viewIndex = getViewIndex(index, holder.getTag());
         boolean blocked = mIconBlacklist.contains(slot);
 
-        mIconLogger.onIconVisibility(getSlot(index), icon.visible);
-        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon));
+        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
+        mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
     }
 
     @Override
     public void setIcon(String slot, int resourceId, CharSequence contentDescription) {
         int index = getSlotIndex(slot);
-        StatusBarIcon icon = getIcon(index);
-        if (icon == null) {
-            icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                    Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription);
-            setIcon(slot, icon);
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null) {
+            StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
+                    Icon.createWithResource(
+                            mContext, resourceId), 0, 0, contentDescription);
+            holder = StatusBarIconHolder.fromIcon(icon);
+            setIcon(index, holder);
         } else {
-            icon.icon = Icon.createWithResource(mContext, resourceId);
-            icon.contentDescription = contentDescription;
-            handleSet(index, icon);
+            holder.getIcon().icon = Icon.createWithResource(mContext, resourceId);
+            holder.getIcon().contentDescription = contentDescription;
+            handleSet(index, holder);
+        }
+    }
+
+    /**
+     * Signal icons need to be handled differently, because they can be
+     * composite views
+     */
+    @Override
+    public void setSignalIcon(String slot, WifiIconState state) {
+
+        int index = getSlotIndex(slot);
+
+        if (state == null) {
+            removeIcon(index, 0);
+            return;
+        }
+
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null) {
+            holder = StatusBarIconHolder.fromWifiIconState(state);
+            setIcon(index, holder);
+        } else {
+            holder.setWifiState(state);
+            handleSet(index, holder);
+        }
+    }
+
+    /**
+     * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted
+     * by subId. Don't worry this definitely makes sense and works.
+     * @param slot da slot
+     * @param iconStates All of the mobile icon states
+     */
+    @Override
+    public void setMobileIcons(String slot, List<MobileIconState> iconStates) {
+        Slot mobileSlot = getSlot(slot);
+        int slotIndex = getSlotIndex(slot);
+
+        for (MobileIconState state : iconStates) {
+            StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId);
+            if (holder == null) {
+                holder = StatusBarIconHolder.fromMobileIconState(state);
+                setIcon(slotIndex, holder);
+            } else {
+                holder.setMobileState(state);
+                handleSet(slotIndex, holder);
+            }
         }
     }
 
     @Override
     public void setExternalIcon(String slot) {
-        int viewIndex = getViewIndex(getSlotIndex(slot));
+        int viewIndex = getViewIndex(getSlotIndex(slot), 0);
         int height = mContext.getResources().getDimensionPixelSize(
                 R.dimen.status_bar_icon_drawing_size);
         mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height));
     }
 
+    //TODO: remove this (used in command queue and for 3rd party tiles?)
     @Override
     public void setIcon(String slot, StatusBarIcon icon) {
         setIcon(getSlotIndex(slot), icon);
     }
 
+    /**
+     * For backwards compatibility, in the event that someone gives us a slot and a status bar icon
+     */
+    private void setIcon(int index, StatusBarIcon icon) {
+        if (icon == null) {
+            removeAllIconsForSlot(getSlotName(index));
+            return;
+        }
+
+        StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon);
+        setIcon(index, holder);
+    }
+
     @Override
-    public void removeIcon(String slot) {
-        int index = getSlotIndex(slot);
-        removeIcon(index);
+    public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
+        boolean isNew = getIcon(index, holder.getTag()) == null;
+        super.setIcon(index, holder);
+
+        if (isNew) {
+            addSystemIcon(index, holder);
+        } else {
+            handleSet(index, holder);
+        }
     }
 
     public void setIconVisibility(String slot, boolean visibility) {
         int index = getSlotIndex(slot);
-        StatusBarIcon icon = getIcon(index);
-        if (icon == null || icon.visible == visibility) {
+        StatusBarIconHolder holder = getIcon(index, 0);
+        if (holder == null || holder.isVisible() == visibility) {
             return;
         }
-        icon.visible = visibility;
-        handleSet(index, icon);
+
+        holder.setVisible(visibility);
+        handleSet(index, holder);
+    }
+
+    public void removeIcon(String slot) {
+        removeAllIconsForSlot(slot);
     }
 
     @Override
-    public void removeIcon(int index) {
-        if (getIcon(index) == null) {
+    public void removeIcon(String slot, int tag) {
+        removeIcon(getSlotIndex(slot), tag);
+    }
+
+    @Override
+    public void removeAllIconsForSlot(String slotName) {
+        Slot slot = getSlot(slotName);
+        if (!slot.hasIconsInSlot()) {
             return;
         }
-        mIconLogger.onIconHidden(getSlot(index));
-        super.removeIcon(index);
-        int viewIndex = getViewIndex(index);
+
+        mIconLogger.onIconHidden(slotName);
+
+        int slotIndex = getSlotIndex(slotName);
+        List<StatusBarIconHolder> iconsToRemove = slot.getHolderList();
+        for (StatusBarIconHolder holder : iconsToRemove) {
+            int viewIndex = getViewIndex(slotIndex, holder.getTag());
+            slot.removeForTag(holder.getTag());
+            mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
+        }
+    }
+
+    @Override
+    public void removeIcon(int index, int tag) {
+        if (getIcon(index, tag) == null) {
+            return;
+        }
+        mIconLogger.onIconHidden(getSlotName(index));
+        super.removeIcon(index, tag);
+        int viewIndex = getViewIndex(index, 0);
         mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex));
     }
 
-    @Override
-    public void setIcon(int index, StatusBarIcon icon) {
-        if (icon == null) {
-            removeIcon(index);
-            return;
-        }
-        boolean isNew = getIcon(index) == null;
-        super.setIcon(index, icon);
-        if (isNew) {
-            addSystemIcon(index, icon);
-        } else {
-            handleSet(index, icon);
-        }
+    private void handleSet(int index, StatusBarIconHolder holder) {
+        int viewIndex = getViewIndex(index, holder.getTag());
+        mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible());
+        mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
     }
 
-    private void handleSet(int index, StatusBarIcon icon) {
-        int viewIndex = getViewIndex(index);
-        mIconLogger.onIconVisibility(getSlot(index), icon.visible);
-        mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon));
+    /**
+     * For mobile essentially (an array of holders in one slot)
+     */
+    private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) {
+        for (StatusBarIconHolder holder : holders) {
+            int viewIndex = getViewIndex(slotIndex, holder.getTag());
+            mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible());
+            mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder));
+        }
     }
 
     @Override
@@ -208,7 +326,7 @@
                 int N = group.getChildCount();
                 pw.println("  icon views: " + N);
                 for (int i = 0; i < N; i++) {
-                    StatusBarIconView ic = (StatusBarIconView) group.getChildAt(i);
+                    StatusIconDisplayable ic = (StatusIconDisplayable) group.getChildAt(i);
                     pw.println("    [" + i + "] icon=" + ic);
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
new file mode 100644
index 0000000..e854dd0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.UserHandle;
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+
+/**
+ * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list
+ */
+public class StatusBarIconHolder {
+    public static final int TYPE_ICON = 0;
+    public static final int TYPE_WIFI = 1;
+    public static final int TYPE_MOBILE = 2;
+
+    private StatusBarIcon mIcon;
+    private WifiIconState mWifiState;
+    private MobileIconState mMobileState;
+    private int mType = TYPE_ICON;
+    private int mTag = 0;
+    private boolean mVisible = true;
+
+    public static StatusBarIconHolder fromIcon(StatusBarIcon icon) {
+        StatusBarIconHolder wrapper = new StatusBarIconHolder();
+        wrapper.mIcon = icon;
+
+        return wrapper;
+    }
+
+    public static StatusBarIconHolder fromResId(Context context, int resId,
+            CharSequence contentDescription) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(),
+                Icon.createWithResource( context, resId), 0, 0, contentDescription);
+        return holder;
+    }
+
+    public static StatusBarIconHolder fromWifiIconState(WifiIconState state) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mWifiState = state;
+        holder.mType = TYPE_WIFI;
+        return holder;
+    }
+
+    public static StatusBarIconHolder fromMobileIconState(MobileIconState state) {
+        StatusBarIconHolder holder = new StatusBarIconHolder();
+        holder.mMobileState = state;
+        holder.mType = TYPE_MOBILE;
+        holder.mTag = state.subId;
+        return holder;
+    }
+
+    public int getType() {
+        return mType;
+    }
+
+    @Nullable
+    public StatusBarIcon getIcon() {
+        return mIcon;
+    }
+
+    @Nullable
+    public WifiIconState getWifiState() {
+        return mWifiState;
+    }
+
+    public void setWifiState(WifiIconState state) {
+        mWifiState = state;
+    }
+
+    @Nullable
+    public MobileIconState getMobileState() {
+        return mMobileState;
+    }
+
+    public void setMobileState(MobileIconState state) {
+        mMobileState = state;
+    }
+
+    public boolean isVisible() {
+        switch (mType) {
+            case TYPE_ICON:
+                return mIcon.visible;
+            case TYPE_WIFI:
+                return mWifiState.visible;
+            case TYPE_MOBILE:
+                return mMobileState.visible;
+
+            default: return true;
+        }
+    }
+
+    public void setVisible(boolean visible) {
+        switch (mType) {
+            case TYPE_ICON:
+                mIcon.visible = visible;
+                break;
+
+            case TYPE_WIFI:
+                mWifiState.visible = visible;
+                break;
+
+            case TYPE_MOBILE:
+                mMobileState.visible = visible;
+                break;
+        }
+    }
+
+    public int getTag() {
+        return mTag;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index 1aa3a43..c773170 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -16,64 +16,78 @@
 
 package com.android.systemui.statusbar.phone;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.StatusBarIcon;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.List;
+
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 
 public class StatusBarIconList {
-    protected ArrayList<String> mSlots = new ArrayList<>();
-    protected ArrayList<StatusBarIcon> mIcons = new ArrayList<>();
+    private ArrayList<Slot> mSlots = new ArrayList<>();
 
     public StatusBarIconList(String[] slots) {
         final int N = slots.length;
         for (int i=0; i < N; i++) {
-            mSlots.add(slots[i]);
-            mIcons.add(null);
+            mSlots.add(new Slot(slots[i], null));
         }
     }
 
     public int getSlotIndex(String slot) {
         final int N = mSlots.size();
         for (int i=0; i<N; i++) {
-            if (slot.equals(mSlots.get(i))) {
+            Slot item = mSlots.get(i);
+            if (item.getName().equals(slot)) {
                 return i;
             }
         }
         // Auto insert new items at the beginning.
-        mSlots.add(0, slot);
-        mIcons.add(0, null);
+        mSlots.add(0, new Slot(slot, null));
         return 0;
     }
 
+    protected ArrayList<Slot> getSlots() {
+        return new ArrayList<>(mSlots);
+    }
+
+    protected Slot getSlot(String name) {
+        return mSlots.get(getSlotIndex(name));
+    }
+
     public int size() {
         return mSlots.size();
     }
 
-    public void setIcon(int index, StatusBarIcon icon) {
-        mIcons.set(index, icon);
+    public void setIcon(int index, @NonNull StatusBarIconHolder holder) {
+        mSlots.get(index).addHolder(holder);
     }
 
-    public void removeIcon(int index) {
-        mIcons.set(index, null);
+    public void removeIcon(int index, int tag) {
+        mSlots.get(index).removeForTag(tag);
     }
 
-    public String getSlot(int index) {
-        return mSlots.get(index);
+    public String getSlotName(int index) {
+        return mSlots.get(index).getName();
     }
 
-    public StatusBarIcon getIcon(int index) {
-        return mIcons.get(index);
+    public StatusBarIconHolder getIcon(int index, int tag) {
+        return mSlots.get(index).getHolderForTag(tag);
     }
 
-    public int getViewIndex(int index) {
+    public int getViewIndex(int slotIndex, int tag) {
         int count = 0;
-        for (int i = 0; i < index; i++) {
-            if (mIcons.get(i) != null) {
-                count++;
+        for (int i = 0; i < slotIndex; i++) {
+            Slot item = mSlots.get(i);
+            if (item.hasIconsInSlot()) {
+                count += item.numberOfIcons();
             }
         }
-        return count;
+
+        Slot viewItem = mSlots.get(slotIndex);
+        return count + viewItem.viewIndexOffsetForTag(tag);
     }
 
     public void dump(PrintWriter pw) {
@@ -81,7 +95,163 @@
         final int N = mSlots.size();
         pw.println("  icon slots: " + N);
         for (int i=0; i<N; i++) {
-            pw.printf("    %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i));
+            pw.printf("    %2d:%s\n", i, mSlots.get(i).toString());
+        }
+    }
+
+    public static class Slot {
+        private final String mName;
+        private StatusBarIconHolder mHolder;
+        /**
+         * Only used if multiple icons are added to the same slot.
+         *
+         * If there are mSubSlots, then these are structured like:
+         *      [ First item | (the rest) ]
+         *
+         * The tricky thing to keep in mind here is that the list [mHolder, mSubSlots] is ordered
+         * ascending, but for view logic we should go backwards through the list. I.e., the first
+         * element (mHolder) should be the highest index, because higher priority items go to the
+         * right of lower priority items
+         */
+        private ArrayList<StatusBarIconHolder> mSubSlots;
+
+        public Slot(String name, StatusBarIconHolder iconHolder) {
+            mName = name;
+            mHolder = iconHolder;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        @Nullable
+        public StatusBarIconHolder getHolderForTag(int tag) {
+            if (tag == TAG_PRIMARY) {
+                return mHolder;
+            }
+
+            if (mSubSlots != null) {
+                for (StatusBarIconHolder holder : mSubSlots) {
+                    if (holder.getTag() == tag) {
+                        return holder;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        public void addHolder(StatusBarIconHolder holder) {
+            int tag = holder.getTag();
+            if (tag == TAG_PRIMARY) {
+                mHolder = holder;
+            } else {
+                setSubSlot(holder, tag);
+            }
+        }
+
+        public void removeForTag(int tag) {
+            if (tag == TAG_PRIMARY) {
+                mHolder = null;
+            } else {
+                int index = getIndexForTag(tag);
+                if (index != -1) {
+                    mSubSlots.remove(index);
+                }
+            }
+        }
+
+        @VisibleForTesting
+        public void clear() {
+            mHolder = null;
+            if (mSubSlots != null) {
+                mSubSlots = null;
+            }
+        }
+
+        private void setSubSlot(StatusBarIconHolder holder, int tag) {
+            if (mSubSlots == null) {
+                mSubSlots = new ArrayList<>();
+                mSubSlots.add(holder);
+                return;
+            }
+
+            if (getIndexForTag(tag) != -1) {
+                // Holder exists for tag; no-op
+                return;
+            }
+
+            // These holders get added to the end. Confused yet?
+            mSubSlots.add(holder);
+        }
+
+        private int getIndexForTag(int tag) {
+            for (int i = 0; i < mSubSlots.size(); i++) {
+                StatusBarIconHolder h = mSubSlots.get(i);
+                if (h.getTag() == tag) {
+                    return i;
+                }
+            }
+
+            return -1;
+        }
+
+        public boolean hasIconsInSlot() {
+            if (mHolder != null) return true;
+            if (mSubSlots == null) return false;
+
+            return mSubSlots.size() > 0;
+        }
+
+        public int numberOfIcons() {
+            int num = mHolder == null ? 0 : 1;
+            if (mSubSlots == null) return num;
+
+            return num + mSubSlots.size();
+        }
+
+        /**
+         * View index is backwards from regular index
+         * @param tag the tag of the holder being viewed
+         * @return (1 + mSubSlots.size() - indexOfTag)
+         */
+        public int viewIndexOffsetForTag(int tag) {
+            if (mSubSlots == null) {
+                return 0;
+            }
+
+            int subSlots = mSubSlots.size();
+            if (tag == TAG_PRIMARY) {
+                return subSlots;
+            }
+
+            return subSlots - getIndexForTag(tag) - 1;
+        }
+
+        public List<StatusBarIconHolder> getHolderList() {
+            ArrayList<StatusBarIconHolder> holders = new ArrayList<>();
+            if (mHolder != null) {
+                holders.add(mHolder);
+            }
+
+            if (mSubSlots != null) {
+                holders.addAll(mSubSlots);
+            }
+
+            return holders;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("(%s) %s", mName, subSlotsString());
+        }
+
+        private String subSlotsString() {
+            if (mSubSlots == null) {
+                return "";
+            }
+
+            return "" + mSubSlots.size() + " subSlots";
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 1a64b00..6b6ea10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -135,26 +135,34 @@
         mFingerprintUnlockController = fingerprintUnlockController;
         mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
                 mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry);
+        mContainer.addOnLayoutChangeListener(this::onContainerLayout);
         mNotificationPanelView = notificationPanelView;
         notificationPanelView.setExpansionListener(this::onPanelExpansionChanged);
     }
 
+    private void onContainerLayout(View v, int left, int top, int right, int bottom,
+            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+        mNotificationPanelView.setBouncerTop(mBouncer.getTop());
+    }
+
     private void onPanelExpansionChanged(float expansion, boolean tracking) {
         // We don't want to translate the bounce when:
         // • Keyguard is occluded, because we're in a FLAG_SHOW_WHEN_LOCKED activity and need to
         //   conserve the original animation.
         // • The user quickly taps on the display and we show "swipe up to unlock."
         // • Keyguard will be dismissed by an action. a.k.a: FLAG_DISMISS_KEYGUARD_ACTIVITY
+        // • Full-screen user switcher is displayed.
         final boolean noLongerTracking = mLastTracking != tracking && !tracking;
         if (mOccluded || mNotificationPanelView.isUnlockHintRunning()
-                || mBouncer.willDismissWithAction()) {
+                || mBouncer.willDismissWithAction()
+                || mStatusBar.isFullScreenUserSwitcherState()) {
             mBouncer.setExpansion(0);
         } else if (mShowing && mStatusBar.isKeyguardCurrentlySecure() && !mDozing) {
             mBouncer.setExpansion(expansion);
             if (expansion == 1) {
                 mBouncer.onFullyHidden();
             } else if (!mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
-                mBouncer.show(true /* resetSecuritySelection */, false /* notifyFalsing */);
+                mBouncer.show(false /* resetSecuritySelection */, false /* animated */);
             } else if (noLongerTracking) {
                 // Notify that falsing manager should stop its session when user stops touching,
                 // even before the animation ends, to guarantee that we're not recording sensitive
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
new file mode 100644
index 0000000..c5a3a0d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.telephony.SubscriptionInfo;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ImageView;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.systemui.Dependency;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.policy.DarkIconDispatcher;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkControllerImpl;
+import com.android.systemui.statusbar.policy.SecurityController;
+import com.android.systemui.tuner.TunerService.Tunable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+
+public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback,
+        SecurityController.SecurityControllerCallback, Tunable {
+    private static final String TAG = "StatusBarSignalPolicy";
+
+    private final String mSlotAirplane;
+    private final String mSlotMobile;
+    private final String mSlotWifi;
+    private final String mSlotEthernet;
+    private final String mSlotVpn;
+
+    private final Context mContext;
+    private final StatusBarIconController mIconController;
+    private final NetworkController mNetworkController;
+    private final SecurityController mSecurityController;
+    private final Handler mHandler = Handler.getMain();
+
+    private boolean mBlockAirplane;
+    private boolean mBlockMobile;
+    private boolean mBlockWifi;
+    private boolean mBlockEthernet;
+    private boolean mActivityEnabled;
+    private boolean mForceBlockWifi;
+
+    // Track as little state as possible, and only for padding purposes
+    private boolean mIsAirplaneMode = false;
+    private boolean mWifiVisible = false;
+
+    private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>();
+    private WifiIconState mWifiIconState = new WifiIconState();
+
+    public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) {
+        mContext = context;
+
+        mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
+        mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
+        mSlotWifi     = mContext.getString(com.android.internal.R.string.status_bar_wifi);
+        mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
+        mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
+
+        mIconController = iconController;
+        mNetworkController = Dependency.get(NetworkController.class);
+        mSecurityController = Dependency.get(SecurityController.class);
+
+        mNetworkController.addCallback(this);
+        mSecurityController.addCallback(this);
+    }
+
+    public void destroy() {
+        mNetworkController.removeCallback(this);
+        mSecurityController.removeCallback(this);
+    }
+
+    private void updateVpn() {
+        boolean vpnVisible = mSecurityController.isVpnEnabled();
+        int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded());
+
+        mIconController.setIcon(mSlotVpn, vpnIconId, null);
+        mIconController.setIconVisibility(mSlotVpn, vpnVisible);
+    }
+
+    private int currentVpnIconId(boolean isBranded) {
+        return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic;
+    }
+
+    private void updateActivityEnabled() {
+        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
+    }
+
+    /**
+     * From SecurityController
+     */
+    @Override
+    public void onStateChanged() {
+        mHandler.post(this::updateVpn);
+    }
+
+    @Override
+    public void onTuningChanged(String key, String newValue) {
+        if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+            return;
+        }
+        ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue);
+        boolean blockAirplane = blockList.contains(mSlotAirplane);
+        boolean blockMobile = blockList.contains(mSlotMobile);
+        boolean blockWifi = blockList.contains(mSlotWifi);
+        boolean blockEthernet = blockList.contains(mSlotEthernet);
+
+        if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
+                || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
+            mBlockAirplane = blockAirplane;
+            mBlockMobile = blockMobile;
+            mBlockEthernet = blockEthernet;
+            mBlockWifi = blockWifi || mForceBlockWifi;
+            // Re-register to get new callbacks.
+            mNetworkController.removeCallback(this);
+        }
+    }
+
+    @Override
+    public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
+            boolean activityIn, boolean activityOut, String description, boolean isTransient,
+            String statusLabel) {
+
+        boolean visible = statusIcon.visible && !mBlockWifi;
+        boolean in = activityIn && mActivityEnabled && visible;
+        boolean out = activityOut && mActivityEnabled && visible;
+
+        mWifiIconState.visible = visible;
+        mWifiIconState.resId = statusIcon.icon;
+        mWifiIconState.activityIn = in;
+        mWifiIconState.activityOut = out;
+        mWifiIconState.slot = mSlotWifi;
+        mWifiIconState.airplaneSpacerVisible = mIsAirplaneMode;
+        mWifiIconState.contentDescription = statusIcon.contentDescription;
+
+        if (mWifiIconState.visible && mWifiIconState.resId > 0) {
+            mIconController.setSignalIcon(mSlotWifi, mWifiIconState.copy());
+            mIconController.setIconVisibility(mSlotWifi, true);
+        } else {
+            mIconController.setIconVisibility(mSlotWifi, false);
+        }
+    }
+
+    @Override
+    public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+            int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
+            String description, boolean isWide, int subId, boolean roaming) {
+        MobileIconState state = getState(subId);
+        if (state == null) {
+            return;
+        }
+
+        state.visible = statusIcon.visible && !mBlockMobile;
+        state.strengthId = statusIcon.icon;
+        state.typeId = statusType;
+        state.contentDescription = statusIcon.contentDescription;
+        state.typeContentDescription = typeContentDescription;
+        state.roaming = roaming;
+        state.activityIn = activityIn && mActivityEnabled;
+        state.activityOut = activityOut && mActivityEnabled;
+
+        // Always send a copy to maintain value type semantics
+        mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates));
+    }
+
+    private MobileIconState getState(int subId) {
+        for (MobileIconState state : mMobileStates) {
+            if (state.subId == subId) {
+                return state;
+            }
+        }
+        Log.e(TAG, "Unexpected subscription " + subId);
+        return null;
+    }
+
+
+    /**
+     * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators
+     * so we don't have to update the icon manager at this point, just remove the old ones
+     * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8)
+     */
+    @Override
+    public void setSubs(List<SubscriptionInfo> subs) {
+        if (hasCorrectSubs(subs)) {
+            return;
+        }
+
+        mIconController.removeAllIconsForSlot(mSlotMobile);
+        mMobileStates.clear();
+        final int n = subs.size();
+        for (int i = 0; i < n; i++) {
+            mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId()));
+        }
+    }
+
+    private boolean hasCorrectSubs(List<SubscriptionInfo> subs) {
+        final int N = subs.size();
+        if (N != mMobileStates.size()) {
+            return false;
+        }
+        for (int i = 0; i < N; i++) {
+            if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void setNoSims(boolean show, boolean simDetected) {
+        // Noop yay!
+    }
+
+
+    @Override
+    public void setEthernetIndicators(IconState state) {
+        boolean visible = state.visible && !mBlockEthernet;
+        int resId = state.icon;
+        String description = state.contentDescription;
+
+        if (resId > 0) {
+            mIconController.setIcon(mSlotEthernet, resId, description);
+            mIconController.setIconVisibility(mSlotEthernet, true);
+        } else {
+            mIconController.setIconVisibility(mSlotEthernet, false);
+        }
+    }
+
+    @Override
+    public void setIsAirplaneMode(IconState icon) {
+        mIsAirplaneMode = icon.visible && !mBlockAirplane;
+        int resId = icon.icon;
+        String description = icon.contentDescription;
+
+        if (mIsAirplaneMode && resId > 0) {
+            mIconController.setIcon(mSlotAirplane, resId, description);
+            mIconController.setIconVisibility(mSlotAirplane, true);
+        } else {
+            mIconController.setIconVisibility(mSlotAirplane, false);
+        }
+    }
+
+    @Override
+    public void setMobileDataEnabled(boolean enabled) {
+        // Don't care.
+    }
+
+    private static abstract class SignalIconState {
+        public boolean visible;
+        public boolean activityOut;
+        public boolean activityIn;
+        public String slot;
+        public String contentDescription;
+
+        @Override
+        public boolean equals(Object o) {
+            // Skipping reference equality bc this should be more of a value type
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            SignalIconState that = (SignalIconState) o;
+            return visible == that.visible &&
+                    activityOut == that.activityOut &&
+                    activityIn == that.activityIn &&
+                    Objects.equals(contentDescription, that.contentDescription) &&
+                    Objects.equals(slot, that.slot);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(visible, activityOut, slot);
+        }
+
+        protected void copyTo(SignalIconState other) {
+            other.visible = visible;
+            other.activityIn = activityIn;
+            other.activityOut = activityOut;
+            other.slot = slot;
+            other.contentDescription = contentDescription;
+        }
+    }
+
+    public static class WifiIconState extends SignalIconState{
+        public int resId;
+        public boolean airplaneSpacerVisible;
+        public boolean signalSpacerVisible;
+
+        @Override
+        public boolean equals(Object o) {
+            // Skipping reference equality bc this should be more of a value type
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            if (!super.equals(o)) {
+                return false;
+            }
+            WifiIconState that = (WifiIconState) o;
+            return resId == that.resId &&
+                    airplaneSpacerVisible == that.airplaneSpacerVisible &&
+                    signalSpacerVisible == that.signalSpacerVisible;
+        }
+
+        public void copyTo(WifiIconState other) {
+            super.copyTo(other);
+            other.resId = resId;
+            other.airplaneSpacerVisible = airplaneSpacerVisible;
+            other.signalSpacerVisible = signalSpacerVisible;
+        }
+
+        public WifiIconState copy() {
+            WifiIconState newState = new WifiIconState();
+            copyTo(newState);
+            return newState;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(super.hashCode(),
+                    resId, airplaneSpacerVisible, signalSpacerVisible);
+        }
+
+        @Override public String toString() {
+            return "WifiIconState(resId=" + resId + ", visible=" + visible + ")";
+        }
+    }
+
+    /**
+     * A little different. This one delegates to SignalDrawable instead of a specific resId
+     */
+    public static class MobileIconState extends SignalIconState {
+        public int subId;
+        public int strengthId;
+        public int typeId;
+        public boolean roaming;
+        public boolean needsLeadingPadding;
+        public String typeContentDescription;
+
+        private MobileIconState(int subId) {
+            super();
+            this.subId = subId;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+            if (!super.equals(o)) {
+                return false;
+            }
+            MobileIconState that = (MobileIconState) o;
+            return subId == that.subId &&
+                    strengthId == that.strengthId &&
+                    typeId == that.typeId &&
+                    roaming == that.roaming &&
+                    needsLeadingPadding == that.needsLeadingPadding &&
+                    Objects.equals(typeContentDescription, that.typeContentDescription);
+        }
+
+        @Override
+        public int hashCode() {
+
+            return Objects
+                    .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
+                            typeContentDescription);
+        }
+
+        public void copyTo(MobileIconState other) {
+            super.copyTo(other);
+            other.subId = subId;
+            other.strengthId = strengthId;
+            other.typeId = typeId;
+            other.roaming = roaming;
+            other.needsLeadingPadding = needsLeadingPadding;
+            other.typeContentDescription = typeContentDescription;
+        }
+
+        private static List<MobileIconState> copyStates(List<MobileIconState> inStates) {
+            ArrayList<MobileIconState> outStates = new ArrayList<>();
+            for (MobileIconState state : inStates) {
+                MobileIconState copy = new MobileIconState(state.subId);
+                state.copyTo(copy);
+                outStates.add(copy);
+            }
+
+            return outStates;
+        }
+
+        @Override public String toString() {
+            return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
+                    + roaming + ", visible=" + visible + ")";
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 309a1a7..2437c51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -143,7 +143,9 @@
 
         final boolean scrimsOccludingWallpaper =
                 state.scrimsVisibility == ScrimController.VISIBILITY_FULLY_OPAQUE;
-        if (state.keyguardShowing && !state.backdropShowing && !scrimsOccludingWallpaper) {
+        final boolean keyguardOrAod = state.keyguardShowing
+                || (state.dozing && mDozeParameters.getAlwaysOn());
+        if (keyguardOrAod && !state.backdropShowing && !scrimsOccludingWallpaper) {
             mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         } else {
             mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index dab28d6..255e10e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -18,13 +18,13 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
-import android.util.ArrayMap;
 import android.util.AttributeSet;
+import android.util.Log;
 
 import android.view.View;
 import com.android.keyguard.AlphaOptimizedLinearLayout;
 import com.android.systemui.R;
-import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.stack.ViewState;
 
 /**
@@ -97,7 +97,7 @@
      * Layout is happening from end -> start
      */
     private void calculateIconTranslations() {
-        float width = getWidth();
+        float width = getWidth() - getPaddingEnd();
         float translationX = width;
         float contentStart = getPaddingStart();
         int childCount = getChildCount();
@@ -109,18 +109,22 @@
         //TODO: Dots
         for (int i = childCount - 1; i >= 0; i--) {
             View child = getChildAt(i);
-            if (!(child instanceof StatusBarIconView)) {
+            if (!(child instanceof StatusIconDisplayable)) {
+                if (DEBUG) Log.d(TAG, "skipping child (wrong type)");
                 continue;
             }
 
+            StatusIconDisplayable iconView = (StatusIconDisplayable) child;
+
             ViewState childState = getViewStateFromChild(child);
             if (childState == null ) {
+                if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState");
                 continue;
             }
 
-            // Rely on StatusBarIcon for truth about visibility
-            if (!((StatusBarIconView) child).getStatusBarIcon().visible) {
+            if (!iconView.isIconVisible() || iconView.isIconBlocked()) {
                 childState.hidden = true;
+                if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible");
                 continue;
             }
 
@@ -175,8 +179,8 @@
 
             vs.initFrom(child);
             vs.alpha = 1.0f;
-            if (child instanceof StatusBarIconView) {
-                vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible;
+            if (child instanceof StatusIconDisplayable) {
+                vs.hidden = !((StatusIconDisplayable)child).isIconVisible();
             } else {
                 vs.hidden = false;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
index 58944c6..945ed76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java
@@ -35,7 +35,7 @@
 
     // Used to reapply darkness on an object, must have previously been added through
     // addDarkReceiver.
-    void applyDark(ImageView object);
+    void applyDark(DarkReceiver object);
 
     int DEFAULT_ICON_TINT = Color.WHITE;
     Rect sTmpRect = new Rect();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
index cc7943b..8e32a0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
@@ -26,9 +26,12 @@
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.SystemProperties;
 import android.view.DisplayListCanvas;
 import android.view.RenderNodeAnimator;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.animation.Interpolator;
 
 import com.android.systemui.Interpolators;
@@ -56,14 +59,17 @@
     private float mGlowAlpha = 0f;
     private float mGlowScale = 1f;
     private boolean mPressed;
+    private boolean mVisible;
     private boolean mDrawingHardwareGlow;
     private int mMaxWidth;
     private boolean mLastDark;
     private boolean mDark;
+    private boolean mDelayTouchFeedback;
 
     private final Interpolator mInterpolator = new LogInterpolator();
     private boolean mSupportHardware;
     private final View mTargetView;
+    private final Handler mHandler = new Handler();
 
     private final HashSet<Animator> mRunningAnimations = new HashSet<>();
     private final ArrayList<Animator> mTmpArray = new ArrayList<>();
@@ -77,6 +83,10 @@
         mDark = darkIntensity >= 0.5f;
     }
 
+    public void setDelayTouchFeedback(boolean delay) {
+        mDelayTouchFeedback = delay;
+    }
+
     private Paint getRipplePaint() {
         if (mRipplePaint == null) {
             mRipplePaint = new Paint();
@@ -211,7 +221,16 @@
         }
     }
 
+    /**
+     * Abort the ripple while it is delayed and before shown used only when setShouldDelayStartTouch
+     * is enabled.
+     */
+    public void abortDelayedRipple() {
+        mHandler.removeCallbacksAndMessages(null);
+    }
+
     private void cancelAnimations() {
+        mVisible = false;
         mTmpArray.addAll(mRunningAnimations);
         int size = mTmpArray.size();
         for (int i = 0; i < size; i++) {
@@ -220,11 +239,21 @@
         }
         mTmpArray.clear();
         mRunningAnimations.clear();
+        mHandler.removeCallbacksAndMessages(null);
     }
 
     private void setPressedSoftware(boolean pressed) {
         if (pressed) {
-            enterSoftware();
+            if (mDelayTouchFeedback) {
+                if (mRunningAnimations.isEmpty()) {
+                    mHandler.removeCallbacksAndMessages(null);
+                    mHandler.postDelayed(this::enterSoftware, ViewConfiguration.getTapTimeout());
+                } else if (mVisible) {
+                    enterSoftware();
+                }
+            } else {
+                enterSoftware();
+            }
         } else {
             exitSoftware();
         }
@@ -232,6 +261,7 @@
 
     private void enterSoftware() {
         cancelAnimations();
+        mVisible = true;
         mGlowAlpha = getMaxGlowAlpha();
         ObjectAnimator scaleAnimator = ObjectAnimator.ofFloat(this, "glowScale",
                 0f, GLOW_MAX_SCALE_FACTOR);
@@ -240,6 +270,12 @@
         scaleAnimator.addListener(mAnimatorListener);
         scaleAnimator.start();
         mRunningAnimations.add(scaleAnimator);
+
+        // With the delay, it could eventually animate the enter animation with no pressed state,
+        // then immediately show the exit animation. If this is skipped there will be no ripple.
+        if (mDelayTouchFeedback && !mPressed) {
+            exitSoftware();
+        }
     }
 
     private void exitSoftware() {
@@ -253,7 +289,16 @@
 
     private void setPressedHardware(boolean pressed) {
         if (pressed) {
-            enterHardware();
+            if (mDelayTouchFeedback) {
+                if (mRunningAnimations.isEmpty()) {
+                    mHandler.removeCallbacksAndMessages(null);
+                    mHandler.postDelayed(this::enterHardware, ViewConfiguration.getTapTimeout());
+                } else if (mVisible) {
+                    enterHardware();
+                }
+            } else {
+                enterHardware();
+            }
         } else {
             exitHardware();
         }
@@ -302,6 +347,7 @@
 
     private void enterHardware() {
         cancelAnimations();
+        mVisible = true;
         mDrawingHardwareGlow = true;
         setExtendStart(CanvasProperty.createFloat(getExtendSize() / 2));
         final RenderNodeAnimator startAnim = new RenderNodeAnimator(getExtendStart(),
@@ -343,6 +389,12 @@
         mRunningAnimations.add(endAnim);
 
         invalidateSelf();
+
+        // With the delay, it could eventually animate the enter animation with no pressed state,
+        // then immediately show the exit animation. If this is skipped there will be no ripple.
+        if (mDelayTouchFeedback && !mPressed) {
+            exitHardware();
+        }
     }
 
     private void exitHardware() {
@@ -366,6 +418,7 @@
         public void onAnimationEnd(Animator animation) {
             mRunningAnimations.remove(animation);
             if (mRunningAnimations.isEmpty() && !mPressed) {
+                mVisible = false;
                 mDrawingHardwareGlow = false;
                 invalidateSelf();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index e5fefd3..5d7e938 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -65,7 +65,6 @@
     private int mTouchSlop;
     private int mTouchDownX;
     private int mTouchDownY;
-    private boolean mIsPressed;
     private boolean mSupportsLongpress = true;
     private AudioManager mAudioManager;
     private boolean mGestureAborted;
@@ -78,7 +77,7 @@
 
     private final Runnable mCheckLongPress = new Runnable() {
         public void run() {
-            if (mIsPressed) {
+            if (isPressed()) {
                 // Log.d("KeyButtonView", "longpressed: " + this);
                 if (isLongClickable()) {
                     // Just an old-fashioned ImageView
@@ -89,12 +88,6 @@
                     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
                     mLongClicked = true;
                 }
-
-                // Only when quick step is enabled, ripple will not be shown on touch down, then
-                // show the ripple on touch up or on long press
-                if (mLongClicked && mOverviewProxyService.getProxy() != null) {
-                    setPressed(true);
-                }
             }
         }
     };
@@ -214,9 +207,7 @@
             mGestureAborted = false;
         }
         if (mGestureAborted) {
-            if (mIsPressed) {
-                setPressed(false);
-            }
+            setPressed(false);
             return false;
         }
 
@@ -224,6 +215,7 @@
             case MotionEvent.ACTION_DOWN:
                 mDownTime = SystemClock.uptimeMillis();
                 mLongClicked = false;
+                setPressed(true);
 
                 // Use raw X and Y to detect gestures in case a parent changes the x and y values
                 mTouchDownX = (int) ev.getRawX();
@@ -234,10 +226,8 @@
                     // Provide the same haptic feedback that the system offers for virtual keys.
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
                 }
-                mIsPressed = true;
                 if (!isProxyConnected) {
                     playSoundEffect(SoundEffectConstants.CLICK);
-                    setPressed(mIsPressed);
                 }
                 removeCallbacks(mCheckLongPress);
                 postDelayed(mCheckLongPress, ViewConfiguration.getLongPressTimeout());
@@ -250,10 +240,7 @@
                 if (exceededTouchSlopX || exceededTouchSlopY) {
                     // When quick step is enabled, prevent animating the ripple triggered by
                     // setPressed and decide to run it on touch up
-                    mIsPressed = false;
-                    if (!isProxyConnected) {
-                        setPressed(mIsPressed);
-                    }
+                    setPressed(false);
                     removeCallbacks(mCheckLongPress);
                 }
                 break;
@@ -265,12 +252,11 @@
                 removeCallbacks(mCheckLongPress);
                 break;
             case MotionEvent.ACTION_UP:
-                final boolean doIt = mIsPressed && !mLongClicked;
+                final boolean doIt = isPressed() && !mLongClicked;
+                setPressed(false);
                 final boolean doHapticFeedback = (SystemClock.uptimeMillis() - mDownTime) > 150;
                 if (isProxyConnected) {
                     if (doIt) {
-                        // Animate the ripple in on touch up with setPressed and then out later
-                        setPressed(true);
                         if (doHapticFeedback) {
                             mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
                         }
@@ -281,7 +267,6 @@
                     // and it feels weird to sometimes get a release haptic and other times not.
                     performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
                 }
-                setPressed(false);
                 if (mCode != 0) {
                     if (doIt) {
                         // If there was a pending remote recents animation, then we need to
@@ -311,12 +296,6 @@
         mAudioManager.playSoundEffect(soundConstant, ActivityManager.getCurrentUser());
     }
 
-    @Override
-    public void setPressed(boolean pressed) {
-        mIsPressed = pressed;
-        super.setPressed(pressed);
-    }
-
     public void sendEvent(int action, int flags) {
         sendEvent(action, flags, SystemClock.uptimeMillis());
     }
@@ -339,6 +318,7 @@
     @Override
     public void abortCurrentGesture() {
         setPressed(false);
+        mRipple.abortDelayedRipple();
         mGestureAborted = true;
     }
 
@@ -357,6 +337,7 @@
 
     @Override
     public void setDelayTouchFeedback(boolean shouldDelay) {
+        mRipple.setDelayTouchFeedback(shouldDelay);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 4533aa5..4c100cd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -54,6 +54,7 @@
 import com.android.systemui.settings.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
 
+import com.android.systemui.statusbar.policy.MobileSignalController.MobileState;
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -848,6 +849,11 @@
                         subs.add(addSignalController(i, i));
                     }
                     mCallbackHandler.setSubs(subs);
+                    for (int i = 0; i < mMobileSignalControllers.size(); i++) {
+                        int key = mMobileSignalControllers.keyAt(i);
+                        MobileSignalController controller = mMobileSignalControllers.get(key);
+                        controller.notifyListeners();
+                    }
                 }
             }
             String nosim = args.getString("nosim");
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 8f6fa49..375e860 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -70,6 +70,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.settingslib.Utils;
+import com.android.systemui.Dependency;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -83,6 +84,7 @@
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.ExpandableView;
 import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationGuts;
 import com.android.systemui.statusbar.NotificationListContainer;
@@ -448,6 +450,13 @@
         mRoundnessManager.setOnRoundingChangedCallback(this::invalidate);
         addOnExpandedHeightListener(mRoundnessManager::setExpanded);
 
+        // Blocking helper manager wants to know the expanded state, update as well.
+        NotificationBlockingHelperManager blockingHelperManager =
+                Dependency.get(NotificationBlockingHelperManager.class);
+        addOnExpandedHeightListener((height, unused) -> {
+            blockingHelperManager.setNotificationShadeExpanded(height);
+        });
+
         updateWillNotDraw();
         mBackgroundPaint.setAntiAlias(true);
         if (DEBUG) {
@@ -1038,45 +1047,63 @@
         mQsContainer = qsContainer;
     }
 
+    /**
+     * Handles cleanup after the given {@code view} has been fully swiped out (including
+     * re-invoking dismiss logic in case the notification has not made its way out yet).
+     */
     @Override
-    public void onChildDismissed(View v) {
-        ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+    public void onChildDismissed(View view) {
+        ExpandableNotificationRow row = (ExpandableNotificationRow) view;
         if (!row.isDismissed()) {
-            handleChildDismissed(v);
+            handleChildViewDismissed(view);
         }
         ViewGroup transientContainer = row.getTransientContainer();
         if (transientContainer != null) {
-            transientContainer.removeTransientView(v);
+            transientContainer.removeTransientView(view);
         }
     }
 
-    private void handleChildDismissed(View v) {
+    /**
+     * Starts up notification dismiss and tells the notification, if any, to remove itself from
+     * layout.
+     *
+     * @param view view (e.g. notification) to dismiss from the layout
+     */
+    private void handleChildViewDismissed(View view) {
         if (mDismissAllInProgress) {
             return;
         }
+
+        boolean isBlockingHelperShown = false;
+
         setSwipingInProgress(false);
-        if (mDragAnimPendingChildren.contains(v)) {
-            // We start the swipe and finish it in the same frame, we don't want any animation
-            // for the drag
-            mDragAnimPendingChildren.remove(v);
+        if (mDragAnimPendingChildren.contains(view)) {
+            // We start the swipe and finish it in the same frame; we don't want a drag animation.
+            mDragAnimPendingChildren.remove(view);
         }
-        mSwipedOutViews.add(v);
-        mAmbientState.onDragFinished(v);
+        mAmbientState.onDragFinished(view);
         updateContinuousShadowDrawing();
-        if (v instanceof ExpandableNotificationRow) {
-            ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+
+        if (view instanceof ExpandableNotificationRow) {
+            ExpandableNotificationRow row = (ExpandableNotificationRow) view;
             if (row.isHeadsUp()) {
                 mHeadsUpManager.addSwipedOutNotification(row.getStatusBarNotification().getKey());
             }
-        }
-        if (v instanceof ExpandableNotificationRow) {
-            ((ExpandableNotificationRow) v).performDismiss(false /* fromAccessibility */);
+            isBlockingHelperShown =
+                    row.performDismissWithBlockingHelper(false /* fromAccessibility */);
         }
 
+        if (!isBlockingHelperShown) {
+            mSwipedOutViews.add(view);
+        }
         mFalsingManager.onNotificationDismissed();
         if (mFalsingManager.shouldEnforceBouncer()) {
-            mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
-                    false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
+            mStatusBar.executeRunnableDismissingKeyguard(
+                    null,
+                    null /* cancelAction */,
+                    false /* dismissShade */,
+                    true /* afterKeyguardGone */,
+                    false /* deferred */);
         }
     }
 
@@ -2729,9 +2756,8 @@
         updateScrollStateForRemovedChild(expandableView);
         boolean animationGenerated = generateRemoveAnimation(child);
         if (animationGenerated) {
-            if (!mSwipedOutViews.contains(child)) {
-                container.getOverlay().add(child);
-            } else if (Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
+            if (!mSwipedOutViews.contains(child)
+                    || Math.abs(expandableView.getTranslation()) != expandableView.getWidth()) {
                 container.addTransientView(child, 0);
                 expandableView.setTransientContainer(container);
             }
@@ -4618,7 +4644,7 @@
             if (mIsExpanded) {
                 // We don't want to quick-dismiss when it's a heads up as this might lead to closing
                 // of the panel early.
-                handleChildDismissed(view);
+                handleChildViewDismissed(view);
             }
             mStatusBar.getGutsManager().closeAndSaveGuts(true /* removeLeavebehind */,
                     false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index d48ae76..c80bdc6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -51,6 +51,7 @@
             = (int) (ANIMATION_DURATION_HEADS_UP_APPEAR
                     * HeadsUpAppearInterpolator.getFractionUntilOvershoot());
     public static final int ANIMATION_DURATION_HEADS_UP_DISAPPEAR = 300;
+    public static final int ANIMATION_DURATION_BLOCKING_HELPER_FADE = 240;
     public static final int ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING = 80;
     public static final int ANIMATION_DELAY_PER_ELEMENT_MANUAL = 32;
     public static final int ANIMATION_DELAY_PER_ELEMENT_GO_TO_FULL_SHADE = 48;
@@ -79,6 +80,7 @@
     private ValueAnimator mBottomOverScrollAnimator;
     private int mHeadsUpAppearHeightBottom;
     private boolean mShadeExpanded;
+    private ArrayList<ExpandableView> mTransientViewsToRemove = new ArrayList<>();
     private NotificationShelf mShelf;
     private float mStatusBarIconLocation;
     private int[] mTmpLocation = new int[2];
@@ -333,6 +335,12 @@
 
     private void onAnimationFinished() {
         mHostLayout.onChildAnimationFinished();
+
+        for (ExpandableView transientViewsToRemove : mTransientViewsToRemove) {
+            transientViewsToRemove.getTransientContainer()
+                    .removeTransientView(transientViewsToRemove);
+        }
+        mTransientViewsToRemove.clear();
     }
 
     /**
@@ -362,7 +370,7 @@
             } else if (event.animationType ==
                     NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE) {
                 if (changingView.getVisibility() != View.VISIBLE) {
-                    removeFromOverlay(changingView);
+                    removeTransientView(changingView);
                     continue;
                 }
 
@@ -402,8 +410,7 @@
                         0, new Runnable() {
                     @Override
                     public void run() {
-                        // remove the temporary overlay
-                        removeFromOverlay(changingView);
+                        removeTransientView(changingView);
                     }
                 }, null);
             } else if (event.animationType ==
@@ -494,6 +501,12 @@
         }
     }
 
+    private static void removeTransientView(ExpandableView viewToRemove) {
+        if (viewToRemove.getTransientContainer() != null) {
+            viewToRemove.getTransientContainer().removeTransientView(viewToRemove);
+        }
+    }
+
     public static void removeFromOverlay(View changingView) {
         ViewGroup parent = (ViewGroup) changingView.getParent();
         if (parent != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index 7ffca173..d527465 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -323,10 +323,10 @@
 
         if (notif != null) {
             mNotificationManager.notifyAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
-                    notif, UserHandle.ALL);
+                    notif, UserHandle.of(vol.getMountUserId()));
         } else {
             mNotificationManager.cancelAsUser(vol.getId(), SystemMessage.NOTE_STORAGE_PUBLIC,
-                    UserHandle.ALL);
+                    UserHandle.of(vol.getMountUserId()));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
index fc932c3..7a9cdfd 100644
--- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
+++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java
@@ -37,9 +37,9 @@
     public static String STORAGE     = "DSK";
     public static String TVPIP       = "TPP";
     public static String BATTERY     = "BAT";
+    public static String HINTS       = "HNT";
 
-    @VisibleForTesting
-    static void createAll(Context context) {
+    public static void createAll(Context context) {
         final NotificationManager nm = context.getSystemService(NotificationManager.class);
         final NotificationChannel batteryChannel = new NotificationChannel(BATTERY,
                 context.getString(R.string.notification_channel_battery),
@@ -73,6 +73,12 @@
                         : NotificationManager.IMPORTANCE_LOW);
         storage.setBypassDnd(true);
 
+        final NotificationChannel hint = new NotificationChannel(
+                HINTS,
+                context.getString(R.string.notification_channel_hints),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        // No need to bypass DND.
+
         nm.createNotificationChannels(Arrays.asList(
                 alerts,
                 general,
@@ -80,7 +86,8 @@
                 createScreenshotChannel(
                         context.getString(R.string.notification_channel_screenshot),
                         nm.getNotificationChannel(SCREENSHOTS_LEGACY)),
-                batteryChannel
+                batteryChannel,
+                hint
         ));
 
         // Delete older SS channel if present.
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index ee6748e..a97effd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.volume;
 
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -29,6 +31,7 @@
 import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.IAudioService;
 import android.media.IVolumeController;
 import android.media.VolumePolicy;
 import android.media.session.MediaController.PlaybackInfo;
@@ -39,6 +42,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -73,9 +77,11 @@
 public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable {
     private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class);
 
+
+    private static final int TOUCH_FEEDBACK_TIMEOUT_MS = 1000;
     private static final int DYNAMIC_STREAM_START_INDEX = 100;
     private static final int VIBRATE_HINT_DURATION = 50;
-    private static final AudioAttributes SONFICIATION_VIBRATION_ATTRIBUTES =
+    private static final AudioAttributes SONIFICIATION_VIBRATION_ATTRIBUTES =
             new AudioAttributes.Builder()
                     .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
                     .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -100,6 +106,7 @@
     private final W mWorker;
     private final Context mContext;
     private AudioManager mAudio;
+    private IAudioService mAudioService;
     protected StatusBar mStatusBar;
     private final NotificationManager mNoMan;
     private final SettingObserver mObserver;
@@ -113,6 +120,7 @@
     private boolean mShowA11yStream;
     private boolean mShowVolumeDialog;
     private boolean mShowSafetyWarning;
+    private long mLastToggledRingerOn;
     private final NotificationManager mNotificationManager;
 
     private boolean mDestroyed;
@@ -140,6 +148,8 @@
         mReceiver.init();
         mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+        mAudioService = IAudioService.Stub.asInterface(
+                ServiceManager.getService(Context.AUDIO_SERVICE));
         updateStatusBar();
 
         boolean accessibilityVolumeStreamActive = context.getSystemService(
@@ -300,10 +310,24 @@
       mShowSafetyWarning = safetyWarning;
     }
 
-    public void vibrate() {
+    @Override
+    public void scheduleTouchFeedback() {
+        mLastToggledRingerOn = System.currentTimeMillis();
+    }
+
+    private void playTouchFeedback() {
+        if (System.currentTimeMillis() - mLastToggledRingerOn < TOUCH_FEEDBACK_TIMEOUT_MS) {
+            try {
+                mAudioService.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD);
+            } catch (RemoteException e) {
+                // ignore
+            }
+        }
+    }
+
+    public void vibrate(VibrationEffect effect) {
         if (mHasVibrator) {
-            mVibrator.vibrate(VibrationEffect.createOneShot(VIBRATE_HINT_DURATION,
-                    VibrationEffect.DEFAULT_AMPLITUDE), SONFICIATION_VIBRATION_ATTRIBUTES);
+            mVibrator.vibrate(effect, SONIFICIATION_VIBRATION_ATTRIBUTES);
         }
     }
 
@@ -425,7 +449,7 @@
         for (int stream : STREAMS.keySet()) {
             updateStreamLevelW(stream, getAudioManagerStreamVolume(stream));
             streamStateW(stream).levelMin = getAudioManagerStreamMinVolume(stream);
-            streamStateW(stream).levelMax = getAudioManagerStreamMaxVolume(stream);
+            streamStateW(stream).levelMax = Math.max(1, getAudioManagerStreamMaxVolume(stream));
             updateStreamMuteW(stream, mAudio.isStreamMute(stream));
             final StreamState ss = streamStateW(stream);
             ss.muteSupported = mAudio.isStreamAffectedByMute(stream);
@@ -556,6 +580,11 @@
         if (rm == mState.ringerModeInternal) return false;
         mState.ringerModeInternal = rm;
         Events.writeEvent(mContext, Events.EVENT_INTERNAL_RINGER_MODE_CHANGED, rm);
+
+        if (mState.ringerModeInternal == RINGER_MODE_NORMAL) {
+            playTouchFeedback();
+        }
+
         return true;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1c8a26c..6f71e55 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -18,11 +18,14 @@
 
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
 import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC;
-import static android.media.AudioManager.RINGER_MODE_MAX;
 import static android.media.AudioManager.RINGER_MODE_NORMAL;
 import static android.media.AudioManager.RINGER_MODE_SILENT;
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
 import static android.view.View.GONE;
 import static android.view.View.VISIBLE;
 
@@ -37,17 +40,22 @@
 import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.res.ColorStateList;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.drawable.ColorDrawable;
+import android.media.AudioAttributes;
 import android.media.AudioManager;
 import android.media.AudioSystem;
+import android.media.MediaPlayer;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
 import android.provider.Settings;
 import android.provider.Settings.Global;
 import android.text.InputFilter;
@@ -61,6 +69,7 @@
 import android.view.View.AccessibilityDelegate;
 import android.view.View.OnAttachStateChangeListener;
 import android.view.ViewGroup;
+import android.view.ViewPropertyAnimator;
 import android.view.Window;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
@@ -125,6 +134,7 @@
     private final Accessibility mAccessibility = new Accessibility();
     private final ColorStateList mActiveSliderTint;
     private final ColorStateList mInactiveSliderTint;
+    private final Vibrator mVibrator;
 
     private boolean mShowing;
     private boolean mShowA11yStream;
@@ -145,6 +155,7 @@
         mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
         mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
     }
 
     public void init(int windowType, Callback callback) {
@@ -193,7 +204,7 @@
         mDialog.setCanceledOnTouchOutside(true);
         mDialog.setContentView(R.layout.volume_dialog);
         mDialog.setOnShowListener(dialog -> {
-            mDialogView.setTranslationX(mDialogView.getWidth() / 2);
+            if (!isLandscape()) mDialogView.setTranslationX(mDialogView.getWidth() / 2);
             mDialogView.setAlpha(0);
             mDialogView.animate()
                     .alpha(1)
@@ -202,6 +213,9 @@
                     .setInterpolator(new SystemUIInterpolators.LogDecelerateInterpolator())
                     .withEndAction(() -> {
                         mWindow.getDecorView().requestAccessibilityFocus();
+                        if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true)) {
+                            mRingerIcon.postOnAnimationDelayed(mSinglePress, 1500);
+                        }
                     })
                     .start();
         });
@@ -222,12 +236,16 @@
         mSettingsIcon = mDialog.findViewById(R.id.settings);
 
         if (mRows.isEmpty()) {
+            if (!AudioSystem.isSingleVolume(mContext)) {
+                addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
+                        R.drawable.ic_volume_accessibility, true, false);
+            }
             addRow(AudioManager.STREAM_MUSIC,
                     R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
             if (!AudioSystem.isSingleVolume(mContext)) {
                 addRow(AudioManager.STREAM_RING,
                         R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
-                addRow(AudioManager.STREAM_ALARM,
+                addRow(STREAM_ALARM,
                         R.drawable.ic_volume_alarm, R.drawable.ic_volume_alarm_mute, true, false);
                 addRow(AudioManager.STREAM_VOICE_CALL,
                         R.drawable.ic_volume_voice, R.drawable.ic_volume_voice, false, false);
@@ -235,8 +253,6 @@
                         R.drawable.ic_volume_bt_sco, R.drawable.ic_volume_bt_sco, false, false);
                 addRow(AudioManager.STREAM_SYSTEM, R.drawable.ic_volume_system,
                         R.drawable.ic_volume_system_mute, false, false);
-                addRow(STREAM_ACCESSIBILITY, R.drawable.ic_volume_accessibility,
-                        R.drawable.ic_volume_accessibility, true, false);
             }
         } else {
             addExistingRows();
@@ -255,6 +271,11 @@
         return ColorStateList.valueOf(mContext.getColor(colorResId));
     }
 
+    private boolean isLandscape() {
+        return mContext.getResources().getConfiguration().orientation ==
+                Configuration.ORIENTATION_LANDSCAPE;
+    }
+
     public void setStreamImportant(int stream, boolean important) {
         mHandler.obtainMessage(H.SET_STREAM_IMPORTANT, stream, important ? 1 : 0).sendToTarget();
     }
@@ -281,11 +302,11 @@
         if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream);
         VolumeRow row = new VolumeRow();
         initRow(row, stream, iconRes, iconMuteRes, important, defaultStream);
-        int rowSize;
-        if (mShowA11yStream && dynamic && (rowSize = mRows.size()) > 1) {
-            // A11y Stream should be the first in the list, so it's shown to start of other rows
-            mDialogRowsView.addView(row.view, 0);
-            mRows.add(rowSize - 2, row);
+        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);
@@ -309,6 +330,11 @@
                 return row;
             }
         }
+        for (VolumeRow row : mRows) {
+            if (row.stream == STREAM_MUSIC) {
+                return row;
+            }
+        }
         return mRows.get(0);
     }
 
@@ -408,6 +434,7 @@
         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) {
                 return;
@@ -418,7 +445,6 @@
             final boolean hasVibrator = mController.hasVibrator();
             if (mState.ringerModeInternal == AudioManager.RINGER_MODE_NORMAL) {
                 if (hasVibrator) {
-                    mController.vibrate();
                     newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
                 } else {
                     newRingerMode = AudioManager.RINGER_MODE_SILENT;
@@ -432,30 +458,56 @@
                 }
             }
             updateRingerH();
-
+            provideTouchFeedbackH(newRingerMode);
             mController.setRingerMode(newRingerMode, false);
             maybeShowToastH(newRingerMode);
         });
         updateRingerH();
     }
 
+
+    private void provideTouchFeedbackH(int newRingerMode) {
+        VibrationEffect effect = null;
+        switch (newRingerMode) {
+            case RINGER_MODE_NORMAL:
+                mController.scheduleTouchFeedback();
+                break;
+            case RINGER_MODE_SILENT:
+                effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+                break;
+            case RINGER_MODE_VIBRATE:
+            default:
+                effect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+        }
+        if (effect != null) {
+            mController.vibrate(effect);
+        }
+    }
+
     private void maybeShowToastH(int newRingerMode) {
         int seenToastCount = Prefs.getInt(mContext, Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT, 0);
 
         if (seenToastCount > VolumePrefs.SHOW_RINGER_TOAST_COUNT) {
             return;
         }
-        int toastText;
+        CharSequence toastText = null;
         switch (newRingerMode) {
             case RINGER_MODE_NORMAL:
-                toastText = R.string.volume_dialog_ringer_guidance_ring;
+                final StreamState ss = mState.states.get(AudioManager.STREAM_RING);
+                if (ss != null) {
+                    toastText = mContext.getString(
+                            R.string.volume_dialog_ringer_guidance_ring,
+                            Utils.formatPercentage(ss.level, ss.levelMax));
+                }
                 break;
             case RINGER_MODE_SILENT:
-                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_silent;
+                toastText = mContext.getString(
+                        com.android.internal.R.string.volume_dialog_ringer_guidance_silent);
                 break;
             case RINGER_MODE_VIBRATE:
             default:
-                toastText = com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate;
+                toastText = mContext.getString(
+                        com.android.internal.R.string.volume_dialog_ringer_guidance_vibrate);
         }
 
         Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT).show();
@@ -510,16 +562,16 @@
 
         mDialogView.setTranslationX(0);
         mDialogView.setAlpha(1);
-        mDialogView.animate()
+        ViewPropertyAnimator animator = mDialogView.animate()
                 .alpha(0)
-                .translationX(mDialogView.getWidth() / 2)
                 .setDuration(250)
                 .setInterpolator(new SystemUIInterpolators.LogAccelerateInterpolator())
                 .withEndAction(() -> mHandler.postDelayed(() -> {
                     if (D.BUG) Log.d(TAG, "mDialog.dismiss()");
                     mDialog.dismiss();
-                }, 50))
-                .start();
+                }, 50));
+        if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2);
+        animator.start();
 
         Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason);
         mController.notifyVisible(false);
@@ -532,7 +584,7 @@
     }
 
     private boolean shouldBeVisibleH(VolumeRow row, VolumeRow activeRow) {
-        boolean isActive = row == activeRow;
+        boolean isActive = row.stream == activeRow.stream;
         if (row.stream == AudioSystem.STREAM_ACCESSIBILITY) {
             return mShowA11yStream;
         }
@@ -544,7 +596,18 @@
             return true;
         }
 
-        return row.defaultStream || isActive;
+        if (isActive) {
+            return true;
+        }
+
+        if (row.defaultStream) {
+            return activeRow.stream == STREAM_RING
+                    || activeRow.stream == STREAM_ALARM
+                    || activeRow.stream == STREAM_VOICE_CALL
+                    || activeRow.stream == STREAM_ACCESSIBILITY;
+        }
+
+        return false;
     }
 
     private void updateRowsH(final VolumeRow activeRow) {
@@ -570,7 +633,11 @@
                 return;
             }
 
-            enableRingerViewsH(mState.zenMode == Global.ZEN_MODE_OFF || !mState.disallowRinger);
+            boolean isZenMuted = mState.zenMode == Global.ZEN_MODE_ALARMS
+                    || mState.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS
+                    || (mState.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+                        && mState.disallowRinger);
+            enableRingerViewsH(!isZenMuted);
             switch (mState.ringerModeInternal) {
                 case AudioManager.RINGER_MODE_VIBRATE:
                     mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_vibrate);
@@ -586,7 +653,7 @@
                 case AudioManager.RINGER_MODE_NORMAL:
                 default:
                     boolean muted = (mAutomute && ss.level == 0) || ss.muted;
-                    if (muted) {
+                    if (!isZenMuted && muted) {
                         mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
                         mRingerIcon.setContentDescription(mContext.getString(
                                 R.string.volume_stream_content_description_unmute,
@@ -660,7 +727,8 @@
         if (mActiveStream != state.activeStream) {
             mPrevActiveStream = mActiveStream;
             mActiveStream = state.activeStream;
-            updateRowsH(getActiveRow());
+            VolumeRow activeRow = getActiveRow();
+            updateRowsH(activeRow);
             rescheduleTimeoutH();
         }
         for (VolumeRow row : mRows) {
@@ -686,7 +754,7 @@
         final boolean isA11yStream = row.stream == STREAM_ACCESSIBILITY;
         final boolean isRingStream = row.stream == AudioManager.STREAM_RING;
         final boolean isSystemStream = row.stream == AudioManager.STREAM_SYSTEM;
-        final boolean isAlarmStream = row.stream == AudioManager.STREAM_ALARM;
+        final boolean isAlarmStream = row.stream == STREAM_ALARM;
         final boolean isMusicStream = row.stream == AudioManager.STREAM_MUSIC;
         final boolean isRingVibrate = isRingStream
                 && mState.ringerModeInternal == AudioManager.RINGER_MODE_VIBRATE;
@@ -911,6 +979,21 @@
         }
     }
 
+    private Runnable mSinglePress = new Runnable() {
+        @Override
+        public void run() {
+            mRingerIcon.setPressed(true);
+            mRingerIcon.postOnAnimationDelayed(mSingleUnpress, 200);
+        }
+    };
+
+    private Runnable mSingleUnpress = new Runnable() {
+        @Override
+        public void run() {
+            mRingerIcon.setPressed(false);
+        }
+    };
+
     private final VolumeDialogController.Callbacks mControllerCallbackH
             = new VolumeDialogController.Callbacks() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
index 173400f..434327c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePrefs.java
@@ -43,7 +43,7 @@
     public static final String PREF_ADJUST_ALARMS = "pref_adjust_alarms";
     public static final String PREF_ADJUST_NOTIFICATION = "pref_adjust_notification";
 
-    public static final int SHOW_RINGER_TOAST_COUNT = 9;
+    public static final int SHOW_RINGER_TOAST_COUNT = 12;
 
     public static final boolean DEFAULT_SHOW_HEADERS = true;
     public static final boolean DEFAULT_ENABLE_AUTOMUTE = true;
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index 107ce1e..a6b09ce 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -75,7 +75,7 @@
     android.test.runner \
     telephony-common \
     android.test.base \
-
+    android.car
 
 LOCAL_AAPT_FLAGS := --extra-packages com.android.systemui:com.android.keyguard
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 4b455ba..149f2de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -58,6 +58,7 @@
     public static final int BELOW_WARNING_BUCKET = -1;
     public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
     public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
+    private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
     private HardwarePropertiesManager mHardProps;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
@@ -198,6 +199,7 @@
         when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
         when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
         when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+        mPowerUI.mBatteryLevel = 10;
         mPowerUI.start();
 
         // unplugged device that would show the non-hybrid notification and the hybrid
@@ -213,6 +215,7 @@
         when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
         when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
         when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+        mPowerUI.mBatteryLevel = 10;
         mPowerUI.start();
 
         // unplugged device that would show the non-hybrid but not the hybrid
@@ -254,13 +257,14 @@
    }
 
     @Test
-    public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnkown_returnsNoShow() {
+    public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnknown_returnsNoShow() {
         when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
         when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
         when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
         mPowerUI.start();
 
-        // Unknown battery status device that would show the neither due
+        // Unknown battery status device that would show the neither due to the battery status being
+        // unknown
         boolean shouldShow =
                 mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                         BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
@@ -295,6 +299,9 @@
 
         mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                 ABOVE_WARNING_BUCKET);
+
+        // reduce battery level to handle time based trigger -> level trigger interactions
+        mPowerUI.mBatteryLevel = 10;
         boolean shouldShow =
                 mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
                         ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 231a1866..ab042d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -16,15 +16,22 @@
 
 package com.android.systemui.statusbar;
 
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.AppOpsManager;
+import android.app.NotificationChannel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.util.ArraySet;
@@ -32,66 +39,80 @@
 import android.view.View;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
+import java.util.List;
 import java.util.function.Consumer;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class ExpandableNotificationRowTest extends SysuiTestCase {
 
-    private ExpandableNotificationRow mGroup;
+    private ExpandableNotificationRow mGroupRow;
+
     private NotificationTestHelper mNotificationTestHelper;
     boolean mHeadsUpAnimatingAway = false;
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
     @Before
     public void setUp() throws Exception {
         mNotificationTestHelper = new NotificationTestHelper(mContext);
-        mGroup = mNotificationTestHelper.createGroup();
-        mGroup.setHeadsUpAnimatingAwayListener(
+        mGroupRow = mNotificationTestHelper.createGroup();
+        mGroupRow.setHeadsUpAnimatingAwayListener(
                 animatingAway -> mHeadsUpAnimatingAway = animatingAway);
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
     }
 
     @Test
     public void testGroupSummaryNotShowingIconWhenPublic() {
-        mGroup.setSensitive(true, true);
-        mGroup.setHideSensitiveForIntrinsicHeight(true);
-        Assert.assertTrue(mGroup.isSummaryWithChildren());
-        Assert.assertFalse(mGroup.isShowingIcon());
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitiveForIntrinsicHeight(true);
+        assertTrue(mGroupRow.isSummaryWithChildren());
+        assertFalse(mGroupRow.isShowingIcon());
     }
 
     @Test
     public void testNotificationHeaderVisibleWhenAnimating() {
-        mGroup.setSensitive(true, true);
-        mGroup.setHideSensitive(true, false, 0, 0);
-        mGroup.setHideSensitive(false, true, 0, 0);
-        Assert.assertTrue(mGroup.getChildrenContainer().getVisibleHeader().getVisibility()
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setHideSensitive(true, false, 0, 0);
+        mGroupRow.setHideSensitive(false, true, 0, 0);
+        assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility()
                 == View.VISIBLE);
     }
 
     @Test
     public void testUserLockedResetEvenWhenNoChildren() {
-        mGroup.setUserLocked(true);
-        mGroup.removeAllChildren();
-        mGroup.setUserLocked(false);
-        Assert.assertFalse("The childrencontainer should not be userlocked but is, the state "
-                + "seems out of sync.", mGroup.getChildrenContainer().isUserLocked());
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
+        assertFalse("The childrencontainer should not be userlocked but is, the state "
+                + "seems out of sync.", mGroupRow.getChildrenContainer().isUserLocked());
     }
 
     @Test
     public void testReinflatedOnDensityChange() {
-        mGroup.setUserLocked(true);
-        mGroup.removeAllChildren();
-        mGroup.setUserLocked(false);
+        mGroupRow.setUserLocked(true);
+        mGroupRow.removeAllChildren();
+        mGroupRow.setUserLocked(false);
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
-        mGroup.setChildrenContainer(mockContainer);
-        mGroup.onDensityOrFontScaleChanged();
+        mGroupRow.setChildrenContainer(mockContainer);
+        mGroupRow.onDensityOrFontScaleChanged();
         verify(mockContainer).reInflateViews(any(), any());
     }
 
@@ -148,22 +169,38 @@
 
     @Test
     public void testClickSound() throws Exception {
-        Assert.assertTrue("Should play sounds by default.", mGroup.isSoundEffectsEnabled());
-        mGroup.setDark(true /* dark */, false /* fade */, 0 /* delay */);
-        mGroup.setSecureStateProvider(()-> false);
-        Assert.assertFalse("Shouldn't play sounds when dark and trusted.",
-                mGroup.isSoundEffectsEnabled());
-        mGroup.setSecureStateProvider(()-> true);
-        Assert.assertTrue("Should always play sounds when not trusted.",
-                mGroup.isSoundEffectsEnabled());
+        assertTrue("Should play sounds by default.", mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setDark(true /* dark */, false /* fade */, 0 /* delay */);
+        mGroupRow.setSecureStateProvider(()-> false);
+        assertFalse("Shouldn't play sounds when dark and trusted.",
+                mGroupRow.isSoundEffectsEnabled());
+        mGroupRow.setSecureStateProvider(()-> true);
+        assertTrue("Should always play sounds when not trusted.",
+                mGroupRow.isSoundEffectsEnabled());
+    }
+
+    @Test
+    public void testSetDismissed_longPressListenerRemoved() {
+        ExpandableNotificationRow.LongPressListener listener =
+                mock(ExpandableNotificationRow.LongPressListener.class);
+        mGroupRow.setLongPressListener(listener);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(1)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
+        reset(listener);
+
+        mGroupRow.setDismissed(true);
+        mGroupRow.doLongClickCallback(0,0);
+        verify(listener, times(0)).onLongPress(eq(mGroupRow), eq(0), eq(0),
+                any(NotificationMenuRowPlugin.MenuItem.class));
     }
 
     @Test
     public void testShowAppOps_noHeader() {
         // public notification is custom layout - no header
-        mGroup.setSensitive(true, true);
-        mGroup.setAppOpsOnClickListener(null);
-        mGroup.showAppOpsIcons(null);
+        mGroupRow.setSensitive(true, true);
+        mGroupRow.setAppOpsOnClickListener(null);
+        mGroupRow.showAppOpsIcons(null);
     }
 
     @Test
@@ -171,17 +208,17 @@
         NotificationHeaderView mockHeader = mock(NotificationHeaderView.class);
 
         NotificationContentView publicLayout = mock(NotificationContentView.class);
-        mGroup.setPublicLayout(publicLayout);
+        mGroupRow.setPublicLayout(publicLayout);
         NotificationContentView privateLayout = mock(NotificationContentView.class);
-        mGroup.setPrivateLayout(privateLayout);
+        mGroupRow.setPrivateLayout(privateLayout);
         NotificationChildrenContainer mockContainer = mock(NotificationChildrenContainer.class);
         when(mockContainer.getNotificationChildCount()).thenReturn(1);
         when(mockContainer.getHeaderView()).thenReturn(mockHeader);
-        mGroup.setChildrenContainer(mockContainer);
+        mGroupRow.setChildrenContainer(mockContainer);
 
         ArraySet<Integer> ops = new ArraySet<>();
         ops.add(AppOpsManager.OP_ANSWER_PHONE_CALLS);
-        mGroup.showAppOpsIcons(ops);
+        mGroupRow.showAppOpsIcons(ops);
 
         verify(mockHeader, times(1)).showAppOpsIcons(ops);
         verify(privateLayout, times(1)).showAppOpsIcons(ops);
@@ -195,17 +232,70 @@
                 ExpandableNotificationRow.OnAppOpsClickListener.class);
         View view = mock(View.class);
 
-        mGroup.setAppOpsOnClickListener(l);
+        mGroupRow.setAppOpsOnClickListener(l);
 
-        mGroup.getAppOpsOnClickListener().onClick(view);
+        mGroupRow.getAppOpsOnClickListener().onClick(view);
         verify(l, times(1)).onClick(any(), anyInt(), anyInt(), any());
     }
 
     @Test
     public void testHeadsUpAnimatingAwayListener() {
-        mGroup.setHeadsUpAnimatingAway(true);
+        mGroupRow.setHeadsUpAnimatingAway(true);
         Assert.assertEquals(true, mHeadsUpAnimatingAway);
-        mGroup.setHeadsUpAnimatingAway(false);
+        mGroupRow.setHeadsUpAnimatingAway(false);
         Assert.assertEquals(false, mHeadsUpAnimatingAway);
     }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_falseWhenBlockingHelperIsntShown() {
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(
+                eq(mGroupRow), any(NotificationMenuRowPlugin.class))).thenReturn(false);
+
+        assertFalse(
+                mGroupRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+    }
+
+    @Test
+    public void testPerformDismissWithBlockingHelper_doesntPerformOnGroupSummary() {
+        ExpandableNotificationRow childRow = mGroupRow.getChildrenContainer().getViewAtPosition(0);
+        when(mBlockingHelperManager.perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class)))
+                .thenReturn(true);
+
+        assertTrue(
+                childRow.performDismissWithBlockingHelper(false /* fromAccessibility */));
+
+        verify(mBlockingHelperManager, times(1))
+                .perhapsShowBlockingHelper(eq(childRow), any(NotificationMenuRowPlugin.class));
+        verify(mBlockingHelperManager, times(0))
+                .perhapsShowBlockingHelper(eq(mGroupRow), any(NotificationMenuRowPlugin.class));
+    }
+
+    @Test
+    public void testIsBlockingHelperShowing_isCorrectlyUpdated() {
+        mGroupRow.setBlockingHelperShowing(true);
+        assertTrue(mGroupRow.isBlockingHelperShowing());
+
+        mGroupRow.setBlockingHelperShowing(false);
+        assertFalse(mGroupRow.isBlockingHelperShowing());
+    }
+
+    @Test
+    public void testGetNumUniqueChildren_defaultChannel() {
+        assertEquals(1, mGroupRow.getNumUniqueChannels());
+    }
+
+    @Test
+    public void testGetNumUniqueChildren_multiChannel() {
+        List<ExpandableNotificationRow> childRows =
+                mGroupRow.getChildrenContainer().getNotificationChildren();
+        // Give each child a unique channel id/name.
+        int i = 0;
+        for (ExpandableNotificationRow childRow : childRows) {
+            childRow.getEntry().channel =
+                    new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT);
+            i++;
+        }
+
+        assertEquals(3, mGroupRow.getNumUniqueChannels());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
new file mode 100644
index 0000000..78cceeb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java
@@ -0,0 +1,238 @@
+/*
+ * 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 com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+
+import android.content.Context;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.View;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Tests for {@link NotificationBlockingHelperManager}.
+ */
+@SmallTest
+@org.junit.runner.RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationBlockingHelperManagerTest extends SysuiTestCase {
+
+    private NotificationBlockingHelperManager mBlockingHelperManager;
+
+    private NotificationTestHelper mHelper;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private NotificationGutsManager mGutsManager;
+    @Mock private NotificationEntryManager mEntryManager;
+    @Mock private NotificationMenuRow mMenuRow;
+    @Mock private NotificationMenuRowPlugin.MenuItem mMenuItem;
+
+    @Before
+    public void setUp() {
+        mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
+
+        mHelper = new NotificationTestHelper(mContext);
+        when(mGutsManager.openGuts(
+                any(View.class),
+                anyInt(),
+                anyInt(),
+                any(NotificationMenuRowPlugin.MenuItem.class)))
+                .thenReturn(true);
+        mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
+        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_nullBlockingHelperRow() {
+        // By default, this shouldn't dismiss (no pointers/vars set up!)
+        assertFalse(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = spy(createBlockableRowSpy());
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(false);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager, times(0)).updateNotifications();
+    }
+
+    @Test
+    public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception {
+        ExpandableNotificationRow row = spy(createBlockableRowSpy());
+        row.setBlockingHelperShowing(true);
+        when(row.isAttachedToWindow()).thenReturn(true);
+        mBlockingHelperManager.setBlockingHelperRowForTest(row);
+
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shown() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+    }
+
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
+        groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(groupRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(groupRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification()
+            throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+
+        verify(mGutsManager).openGuts(childRow, 0, 0, mMenuItem);
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment()
+            throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        // Hide the shade
+        mBlockingHelperManager.setNotificationShadeExpanded(0f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception {
+        ExpandableNotificationRow row = createBlockableRowSpy();
+        when(row.getIsNonblockable()).thenReturn(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+    }
+
+    @Test
+    public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup()
+            throws Exception {
+        ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2);
+        // Explicitly get the children container & call getViewAtPosition on it instead of the row
+        // as other factors such as view expansion may cause us to get the parent row back instead
+        // of the child row.
+        ExpandableNotificationRow childRow = groupRow.getChildrenContainer().getViewAtPosition(0);
+        childRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(childRow, mMenuRow));
+    }
+
+    @Test
+    public void testBlockingHelperShowAndDismiss() throws Exception{
+        ExpandableNotificationRow row = spy(createBlockableRowSpy());
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.isAttachedToWindow()).thenReturn(true);
+        mBlockingHelperManager.setNotificationShadeExpanded(1f);
+
+        // Show check
+        assertTrue(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow));
+
+        verify(mGutsManager).openGuts(row, 0, 0, mMenuItem);
+
+        // Dismiss check
+        assertTrue(mBlockingHelperManager.dismissCurrentBlockingHelper());
+        assertTrue(mBlockingHelperManager.isBlockingHelperRowNull());
+
+        verify(mEntryManager).updateNotifications();
+    }
+
+    private ExpandableNotificationRow createBlockableRowSpy() throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        when(row.getIsNonblockable()).thenReturn(false);
+        return row;
+    }
+
+    private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception {
+        ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren));
+        when(row.getIsNonblockable()).thenReturn(false);
+        return row;
+    }
+
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
index 2000bff..5e27fde 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationDataTest.java
@@ -35,6 +35,7 @@
 import android.app.NotificationChannel;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
+import android.media.session.MediaSession;
 import android.os.Bundle;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.StatusBarNotification;
@@ -61,6 +62,7 @@
     private static final int UID_NORMAL = 123;
     private static final int UID_ALLOW_DURING_SETUP = 456;
     private static final String TEST_HIDDEN_NOTIFICATION_KEY = "testHiddenNotificationKey";
+    private static final String TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY = "exempt";
 
     private final StatusBarNotification mMockStatusBarNotification =
             mock(StatusBarNotification.class);
@@ -226,12 +228,21 @@
     public void testSuppressSystemAlertNotification() {
         when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
         when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
+        sbn.getNotification().extras = bundle;
 
         assertTrue(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
     }
 
     @Test
     public void testDoNotSuppressSystemAlertNotification() {
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {"something"});
+        sbn.getNotification().extras = bundle;
+
         when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
         when(mFsc.isSystemAlertNotification(any())).thenReturn(true);
 
@@ -249,7 +260,24 @@
     }
 
     @Test
+    public void testDoNotSuppressMalformedSystemAlertNotification() {
+        when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true);
+
+        // missing extra
+        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+
+        StatusBarNotification sbn = mRow.getEntry().notification;
+        Bundle bundle = new Bundle();
+        bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {});
+        sbn.getNotification().extras = bundle;
+
+        // extra missing values
+        assertFalse(mNotificationData.shouldFilterOut(mRow.getEntry().notification));
+    }
+
+    @Test
     public void testShouldFilterHiddenNotifications() {
+        initStatusBarNotification(false);
         // setup
         when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false);
         when(mFsc.isSystemAlertNotification(any())).thenReturn(false);
@@ -264,6 +292,33 @@
         assertFalse(mNotificationData.shouldFilterOut(mMockStatusBarNotification));
     }
 
+    @Test
+    public void testIsExemptFromDndVisualSuppression_foreground() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        n.flags = Notification.FLAG_FOREGROUND_SERVICE;
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+        assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+    }
+
+    @Test
+    public void testIsExemptFromDndVisualSuppression_media() {
+        initStatusBarNotification(false);
+        when(mMockStatusBarNotification.getKey()).thenReturn(
+                TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY);
+        Notification n = mMockStatusBarNotification.getNotification();
+        Notification.Builder nb = Notification.Builder.recoverBuilder(mContext, n);
+        nb.setStyle(new Notification.MediaStyle().setMediaSession(mock(MediaSession.Token.class)));
+        n = nb.build();
+        when(mMockStatusBarNotification.getNotification()).thenReturn(n);
+
+        assertTrue(mNotificationData.isExemptFromDndVisualSuppression(mMockStatusBarNotification));
+        assertFalse(mNotificationData.shouldSuppressAmbient(mMockStatusBarNotification));
+    }
+
     private void initStatusBarNotification(boolean allowDuringSetup) {
         Bundle bundle = new Bundle();
         bundle.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, allowDuringSetup);
@@ -293,6 +348,13 @@
                         outRanking.getImportance(), outRanking.getImportanceExplanation(),
                         outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
                         outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
+            } else if (key.equals(TEST_EXEMPT_DND_VISUAL_SUPPRESSION_KEY)) {
+                outRanking.populate(key, outRanking.getRank(),
+                        outRanking.matchesInterruptionFilter(),
+                        outRanking.getVisibilityOverride(), 255,
+                        outRanking.getImportance(), outRanking.getImportanceExplanation(),
+                        outRanking.getOverrideGroupKey(), outRanking.getChannel(), null, null,
+                        outRanking.canShowBadge(), outRanking.getUserSentiment(), true);
             } else {
                 outRanking.populate(key, outRanking.getRank(),
                         outRanking.matchesInterruptionFilter(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
index 6209d59..0ef2d051 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java
@@ -20,6 +20,7 @@
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static junit.framework.Assert.assertNotNull;
 
 import static org.junit.Assert.assertEquals;
@@ -27,6 +28,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -34,31 +37,24 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.times;
 
+import android.app.INotificationManager;
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
-import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Handler;
-import android.os.Looper;
-import android.os.UserHandle;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.ArraySet;
-import android.view.LayoutInflater;
 import android.view.View;
 
-import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.statusbar.NotificationData;
-import com.android.systemui.statusbar.NotificationGuts;
-import com.android.systemui.statusbar.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.NotificationInflater;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
@@ -70,15 +66,18 @@
 import org.mockito.junit.MockitoRule;
 import org.mockito.junit.MockitoJUnit;
 
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Tests for {@link NotificationGutsManager}.
+ */
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper
 public class NotificationGutsManagerTest extends SysuiTestCase {
     private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
 
-    private final String mPackageName = mContext.getPackageName();
-    private final int mUid = Binder.getCallingUid();
-
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
             TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
     private TestableLooper mTestableLooper;
@@ -87,7 +86,6 @@
     private NotificationGutsManager mGutsManager;
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule();
-    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
     @Mock private NotificationPresenter mPresenter;
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private NotificationStackScrollLayout mStackScroller;
@@ -118,7 +116,12 @@
         });
 
         // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         ExpandableNotificationRow realRow = createTestNotificationRow();
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
@@ -130,7 +133,12 @@
         mGutsManager.openGuts(row, 0, 0, menuItem);
         assertEquals(View.INVISIBLE, guts.getVisibility());
         mTestableLooper.processAllMessages();
-        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         assertEquals(View.VISIBLE, guts.getVisibility());
         mGutsManager.closeAndSaveGuts(false, false, false, 0, 0, false);
@@ -148,7 +156,12 @@
         });
 
         // Test doesn't support animation since the guts view is not attached.
-        doNothing().when(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        doNothing().when(guts).openControls(
+                eq(true) /* shouldDoCircularReveal */,
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         ExpandableNotificationRow realRow = createTestNotificationRow();
         NotificationMenuRowPlugin.MenuItem menuItem = createTestMenuItem(realRow);
@@ -160,7 +173,12 @@
 
         mGutsManager.openGuts(row, 0, 0, menuItem);
         mTestableLooper.processAllMessages();
-        verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any(Runnable.class));
+        verify(guts).openControls(
+                eq(true),
+                anyInt(),
+                anyInt(),
+                anyBoolean(),
+                any(Runnable.class));
 
         row.onDensityOrFontScaleChanged();
         mGutsManager.onDensityOrFontScaleChanged(row);
@@ -247,6 +265,58 @@
         assertEquals(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, captor.getValue().getAction());
     }
 
+    @Test
+    public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(true);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                isNull(),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(true) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
+    @Test
+    public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception {
+        NotificationInfo notificationInfoView = mock(NotificationInfo.class);
+        ExpandableNotificationRow row = spy(mHelper.createRow());
+        row.setBlockingHelperShowing(false);
+        row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
+        when(row.getIsNonblockable()).thenReturn(false);
+        StatusBarNotification statusBarNotification = row.getStatusBarNotification();
+
+        mGutsManager.initializeNotificationInfo(row, notificationInfoView);
+
+        verify(notificationInfoView).bindNotification(
+                any(PackageManager.class),
+                any(INotificationManager.class),
+                eq(statusBarNotification.getPackageName()),
+                isNull(),
+                anyInt(),
+                eq(statusBarNotification),
+                any(NotificationInfo.CheckSaveListener.class),
+                any(NotificationInfo.OnSettingsClickListener.class),
+                any(NotificationInfo.OnAppSettingsClickListener.class),
+                eq(false),
+                eq(false) /* isForBlockingHelper */,
+                eq(true) /* isUserSentimentNegative */);
+    }
+
     ////////////////////////////////////////////////////////////////////////////////////////////////
     // Utility methods:
 
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 01664b2..d86e947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java
@@ -67,12 +67,15 @@
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 
@@ -83,18 +86,26 @@
     private static final String TEST_PACKAGE_NAME = "test_package";
     private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME;
     private static final int TEST_UID = 1;
+    private static final int MULTIPLE_CHANNEL_COUNT = 2;
     private static final String TEST_CHANNEL = "test_channel";
     private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME";
 
     private NotificationInfo mNotificationInfo;
-    private final INotificationManager mMockINotificationManager = mock(INotificationManager.class);
-    private final PackageManager mMockPackageManager = mock(PackageManager.class);
     private NotificationChannel mNotificationChannel;
     private NotificationChannel mDefaultNotificationChannel;
     private StatusBarNotification mSbn;
 
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private INotificationManager mMockINotificationManager;
+    @Mock private PackageManager mMockPackageManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+
     @Before
     public void setUp() throws Exception {
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+
         // Inflate the layout
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
         mNotificationInfo = (NotificationInfo) layoutInflater.inflate(R.layout.notification_info,
@@ -146,7 +157,7 @@
     public void testBindNotification_SetsTextApplicationName() throws Exception {
         when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkgname);
         assertTrue(textView.getText().toString().contains("App Name"));
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -158,7 +169,7 @@
         when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
                 .thenReturn(iconDrawable);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon);
         assertEquals(iconDrawable, iconView.getDrawable());
     }
@@ -166,7 +177,7 @@
     @Test
     public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(GONE, groupNameView.getVisibility());
         final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider);
@@ -182,7 +193,7 @@
                 eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID)))
                 .thenReturn(notificationChannelGroup);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(View.VISIBLE, groupNameView.getVisibility());
         assertEquals("Test Group Name", groupNameView.getText());
@@ -193,7 +204,7 @@
     @Test
     public void testBindNotification_SetsTextChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(TEST_CHANNEL_NAME, textView.getText());
     }
@@ -201,16 +212,27 @@
     @Test
     public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, textView.getVisibility());
     }
 
     @Test
+    public void testBindNotification_DefaultChannelUsesChannelNameIfMoreChannelsExist()
+            throws Exception {
+        // Package has one channel by default.
+        when(mMockINotificationManager.getNumNotificationChannelsForPackage(
+                eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10);
+        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
+                TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false);
+        final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
+        assertEquals(VISIBLE, textView.getVisibility());
+    }
+
+    @Test
     public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         final TextView textView = mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(VISIBLE, textView.getVisibility());
     }
@@ -218,7 +240,7 @@
     @Test
     public void testBindNotification_BlockButton() throws Exception {
        mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(VISIBLE, block.getVisibility());
@@ -229,7 +251,7 @@
     public void testBindNotification_MinButton() throws Exception {
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final View block = mNotificationInfo.findViewById(R.id.block);
         final View minimize = mNotificationInfo.findViewById(R.id.minimize);
         assertEquals(GONE, block.getVisibility());
@@ -244,7 +266,7 @@
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(mNotificationChannel, c);
                     latch.countDown();
-                }, null, null);
+                }, null, false);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         settingsButton.performClick();
@@ -255,7 +277,7 @@
     @Test
     public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
     }
@@ -263,11 +285,11 @@
     @Test
     public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
-                }, null, null);
+                }, null, false);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertEquals(View.VISIBLE, settingsButton.getVisibility());
     }
@@ -276,11 +298,11 @@
     public void testOnClickListenerPassesNullChannelForBundle() throws Exception {
         final CountDownLatch latch = new CountDownLatch(1);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null,
                 (View v, NotificationChannel c, int appUid) -> {
                     assertEquals(null, c);
                     latch.countDown();
-                }, null, null);
+                }, null, true);
 
         mNotificationInfo.findViewById(R.id.info).performClick();
         // Verify that listener was triggered.
@@ -292,7 +314,8 @@
     public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels()
             throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+                null, true);
         final TextView channelNameView =
                 mNotificationInfo.findViewById(R.id.channel_name);
         assertEquals(GONE, channelNameView.getVisibility());
@@ -302,7 +325,8 @@
     @UiThreadTest
     public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null,
+                null, true);
         final TextView blockView = mNotificationInfo.findViewById(R.id.block);
         assertEquals(GONE, blockView.getVisibility());
     }
@@ -310,8 +334,8 @@
     @Test
     public void testbindNotification_BlockingHelper() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
-                null, null, true);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false,
+                true);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText());
@@ -320,8 +344,7 @@
     @Test
     public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
-                null, Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         final TextView view = mNotificationInfo.findViewById(R.id.block_prompt);
         assertEquals(View.VISIBLE, view.getVisibility());
         assertEquals(mContext.getString(R.string.notification_unblockable_desc),
@@ -331,7 +354,7 @@
     @Test
     public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception {
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
                 anyString(), eq(TEST_UID), any());
     }
@@ -340,7 +363,7 @@
     public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -352,7 +375,7 @@
             throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -364,7 +387,7 @@
             throws Exception {
         int originalImportance = mNotificationChannel.getImportance();
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.handleCloseControls(true, false);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -377,7 +400,7 @@
             throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.handleCloseControls(true, false);
         verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage(
@@ -386,11 +409,31 @@
     }
 
     @Test
+    public void testCloseControls_blockingHelperDismissedIfShown() throws Exception {
+        mNotificationInfo.bindNotification(
+                mMockPackageManager,
+                mMockINotificationManager,
+                TEST_PACKAGE_NAME,
+                mNotificationChannel,
+                1 /* numChannels */,
+                mSbn,
+                null /* checkSaveListener */,
+                null /* onSettingsClick */,
+                null /* onAppSettingsClick */,
+                false /* isNonblockable */,
+                true /* isForBlockingHelper */,
+                false /* isUserSentimentNegative */);
+
+        mNotificationInfo.closeControls(mNotificationInfo);
+
+        verify(mBlockingHelperManager).dismissCurrentBlockingHelper();
+    }
+
+    @Test
     public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
-                null, Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -401,7 +444,7 @@
     public void testBlockChangedCallsUpdateNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -420,8 +463,7 @@
     public void testNonBlockableAppDoesNotBecomeMin() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null,
-                null, Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
         verify(mMockINotificationManager, never()).updateNotificationChannelForPackage(
@@ -433,7 +475,7 @@
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -452,7 +494,7 @@
     public void testKeepUpdatesNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.handleCloseControls(true, false);
 
@@ -468,7 +510,7 @@
     public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -488,7 +530,7 @@
     public void testMinUndoDoesNotMinNotificationChannel() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -508,8 +550,7 @@
     public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -522,8 +563,7 @@
     public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -538,7 +578,7 @@
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
-                }, null, null, Collections.singleton(TEST_PACKAGE_NAME));
+                }, null, null, true);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -554,7 +594,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn,
                 (Runnable saveImportance, StatusBarNotification sbn) -> {
                     saveImportance.run();
-                }, null, null, null);
+                }, null, null, false);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -584,7 +624,7 @@
                 TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, null);
+                }, false);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -609,10 +649,10 @@
                 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null,
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
                 (View v, Intent intent) -> {
                     latch.countDown();
-                }, null);
+                }, false);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(View.VISIBLE, settingsLink.getVisibility());
         settingsLink.performClick();
@@ -630,7 +670,8 @@
                 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null,
+                null, false);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -650,7 +691,7 @@
                 0, null, 0, 0, n, UserHandle.CURRENT, null, 0);
 
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null);
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false);
         final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings);
         assertEquals(GONE, settingsLink.getVisibility());
     }
@@ -666,8 +707,7 @@
         mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE;
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.minimize).performClick();
         waitForUndoButton();
@@ -679,8 +719,7 @@
     public void testUndoText_block() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -692,8 +731,7 @@
     public void testNoHeaderOnConfirmation() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
@@ -704,8 +742,7 @@
     public void testHeaderOnUndo() throws Exception {
         mNotificationChannel.setImportance(IMPORTANCE_LOW);
         mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager,
-                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null,
-                Collections.singleton(TEST_PACKAGE_NAME));
+                TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true);
 
         mNotificationInfo.findViewById(R.id.block).performClick();
         waitForUndoButton();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 2764254..de2e6bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.Notification;
@@ -23,33 +24,32 @@
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
 import android.support.test.InstrumentationRegistry;
+import android.text.TextUtils;
 import android.view.LayoutInflater;
-import android.widget.FrameLayout;
 import android.widget.RemoteViews;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
-import com.android.systemui.statusbar.notification.AboveShelfObserver;
-import com.android.systemui.statusbar.notification.InflationException;
 import com.android.systemui.statusbar.notification.NotificationInflaterTest;
 import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
 /**
- * A helper class to create {@link ExpandableNotificationRow}
+ * A helper class to create {@link ExpandableNotificationRow} (for both individual and group
+ * notifications).
  */
 public class NotificationTestHelper {
 
+    static final String PKG = "com.android.systemui";
+    static final int UID = 1000;
+    private static final String GROUP_KEY = "gruKey";
+
     private final Context mContext;
     private final Instrumentation mInstrumentation;
     private int mId;
     private final NotificationGroupManager mGroupManager = new NotificationGroupManager();
     private ExpandableNotificationRow mRow;
-    private InflationException mException;
     private HeadsUpManager mHeadsUpManager;
-    protected static final String PKG = "com.android.systemui";
-    protected static final int UID = 1000;
 
     public NotificationTestHelper(Context context) {
         mContext = context;
@@ -57,57 +57,125 @@
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, null, mGroupManager, null, null);
     }
 
+    public ExpandableNotificationRow createRow() throws Exception {
+        return createRow(PKG, UID);
+    }
+
     public ExpandableNotificationRow createRow(String pkg, int uid) throws Exception {
+        return createRow(pkg, uid, false /* isGroupSummary */, null /* groupKey */);
+    }
+
+    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
+        return generateRow(notification, PKG, UID, false /* isGroupRow */);
+    }
+
+    /**
+     * Returns an {@link ExpandableNotificationRow} group with the given number of child
+     * notifications.
+     */
+    public ExpandableNotificationRow createGroup(int numChildren) throws Exception {
+        ExpandableNotificationRow row = createGroupSummary(GROUP_KEY);
+        for (int i = 0; i < numChildren; i++) {
+            ExpandableNotificationRow childRow = createGroupChild(GROUP_KEY);
+            row.addChildNotification(childRow);
+        }
+        return row;
+    }
+
+    /** Returns a group notification with 2 child notifications. */
+    public ExpandableNotificationRow createGroup() throws Exception {
+        return createGroup(2);
+    }
+
+    private ExpandableNotificationRow createGroupSummary(String groupkey) throws Exception {
+        return createRow(PKG, UID, true /* isGroupSummary */, groupkey);
+    }
+
+    private ExpandableNotificationRow createGroupChild(String groupkey) throws Exception {
+        return createRow(PKG, UID, false /* isGroupSummary */, groupkey);
+    }
+
+    /**
+     * Creates a notification row with the given details.
+     *
+     * @param pkg package used for creating a {@link StatusBarNotification}
+     * @param uid uid used for creating a {@link StatusBarNotification}
+     * @param isGroupSummary whether the notification row is a group summary
+     * @param groupKey the group key for the notification group used across notifications
+     * @return a row with that's either a standalone notification or a group notification if the
+     *         groupKey is non-null
+     * @throws Exception
+     */
+    private ExpandableNotificationRow createRow(
+            String pkg,
+            int uid,
+            boolean isGroupSummary,
+            @Nullable String groupKey)
+            throws Exception {
         Notification publicVersion = new Notification.Builder(mContext).setSmallIcon(
                 R.drawable.ic_person)
                 .setCustomContentView(new RemoteViews(mContext.getPackageName(),
                         R.layout.custom_view_dark))
                 .build();
-        Notification notification = new Notification.Builder(mContext).setSmallIcon(
-                R.drawable.ic_person)
-                .setContentTitle("Title")
-                .setContentText("Text")
-                .setPublicVersion(publicVersion)
-                .build();
-        return createRow(notification, pkg, uid);
+        Notification.Builder notificationBuilder =
+                new Notification.Builder(mContext)
+                        .setSmallIcon(R.drawable.ic_person)
+                        .setContentTitle("Title")
+                        .setContentText("Text")
+                        .setPublicVersion(publicVersion);
+
+        // Group notification setup
+        if (isGroupSummary) {
+            notificationBuilder.setGroupSummary(true);
+        }
+        if (!TextUtils.isEmpty(groupKey)) {
+            notificationBuilder.setGroup(groupKey);
+        }
+
+        return generateRow(notificationBuilder.build(), pkg, uid, !TextUtils.isEmpty(groupKey));
     }
 
-    public ExpandableNotificationRow createRow() throws Exception {
-        return createRow(PKG, UID);
-    }
-
-    public ExpandableNotificationRow createRow(Notification notification) throws Exception {
-        return createRow(notification, PKG, UID);
-    }
-
-    public ExpandableNotificationRow createRow(Notification notification, String pkg, int uid)
+    private ExpandableNotificationRow generateRow(
+            Notification notification,
+            String pkg,
+            int uid,
+            boolean isGroupRow)
             throws Exception {
         LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
                 mContext.LAYOUT_INFLATER_SERVICE);
-        mInstrumentation.runOnMainSync(() -> {
-            mRow = (ExpandableNotificationRow) inflater.inflate(
-                    R.layout.status_bar_notification_row,
-                    null, false);
-        });
+        mInstrumentation.runOnMainSync(() ->
+                mRow = (ExpandableNotificationRow) inflater.inflate(
+                        R.layout.status_bar_notification_row,
+                        null /* root */,
+                        false /* attachToRoot */)
+        );
         ExpandableNotificationRow row = mRow;
         row.setGroupManager(mGroupManager);
         row.setHeadsUpManager(mHeadsUpManager);
         row.setAboveShelfChangedListener(aboveShelf -> {});
         UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
-        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, mId++, null, uid,
-                2000, notification, mUser, null, System.currentTimeMillis());
+        StatusBarNotification sbn = new StatusBarNotification(
+                pkg,
+                pkg,
+                mId++,
+                null /* tag */,
+                uid,
+                2000 /* initialPid */,
+                notification,
+                mUser,
+                null /* overrideGroupKey */,
+                System.currentTimeMillis());
         NotificationData.Entry entry = new NotificationData.Entry(sbn);
         entry.row = row;
         entry.createIcons(mContext, sbn);
-        NotificationInflaterTest.runThenWaitForInflation(() -> row.updateNotification(entry),
+        NotificationInflaterTest.runThenWaitForInflation(
+                () -> row.updateNotification(entry),
                 row.getNotificationInflater());
-        return row;
-    }
 
-    public ExpandableNotificationRow createGroup() throws Exception {
-        ExpandableNotificationRow row = createRow();
-        row.addChildNotification(createRow());
-        row.addChildNotification(createRow());
+        // This would be done as part of onAsyncInflationFinished, but we skip large amounts of
+        // the callback chain, so we need to make up for not adding it to the group manager
+        // here.
+        mGroupManager.onEntryAdded(entry);
         return row;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
index 2792d8c..07ac11b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
@@ -1,17 +1,23 @@
 package com.android.systemui.statusbar;
 
+import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.StatusBarIconHolder;
 import com.android.systemui.statusbar.phone.StatusBarIconList;
+import com.android.systemui.statusbar.phone.StatusBarIconList.Slot;
 
+import java.util.ArrayList;
+import java.util.List;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -41,45 +47,125 @@
     @Test
     public void testAddSlotSlidesIcons() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIcon);
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolder);
         statusBarIconList.getSlotIndex("zzz"); // new content added in front
-        assertNull(statusBarIconList.getIcon(0));
-        assertEquals(sbIcon, statusBarIconList.getIcon(1));
+        assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY));
+        assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY));
     }
 
     @Test
     public void testGetAndSetIcon() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
-        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIconA);
-        statusBarIconList.setIcon(1, sbIconB);
-        assertEquals(sbIconA, statusBarIconList.getIcon(0));
-        assertEquals(sbIconB, statusBarIconList.getIcon(1));
-        assertNull(statusBarIconList.getIcon(2)); // icon not set
+        StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolderA);
+        statusBarIconList.setIcon(1, sbHolderB);
+        assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY));
+        assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY));
+        assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set
     }
 
     @Test
     public void testRemoveIcon() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
-        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(0, sbIconA);
-        statusBarIconList.setIcon(1, sbIconB);
-        statusBarIconList.removeIcon(0);
-        assertNull(statusBarIconList.getIcon(0)); // icon not set
+        StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(0, sbHolderA);
+        statusBarIconList.setIcon(1, sbHolderB);
+        statusBarIconList.removeIcon(0, TAG_PRIMARY);
+        assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set
     }
 
     @Test
-    public void testGetViewIndex() {
+    public void testGetViewIndex_NoMultiples() {
         StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
-        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
-        statusBarIconList.setIcon(2, sbIcon);
-        assertEquals(0, statusBarIconList.getViewIndex(2)); // Icon for item 2 is 0th child view.
-        statusBarIconList.setIcon(0, sbIcon);
-        assertEquals(0, statusBarIconList.getViewIndex(0)); // Icon for item 0 is 0th child view,
-        assertEquals(1, statusBarIconList.getViewIndex(2)); // and item 2 is now 1st child view.
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+        statusBarIconList.setIcon(2, sbHolder);
+        // Icon for item 2 is 0th child view.
+        assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+        statusBarIconList.setIcon(0, sbHolder);
+        // Icon for item 0 is 0th child view,
+        assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY));
+        // and item 2 is now 1st child view.
+        assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
     }
 
+    @Test
+    public void testGetViewIndex_MultipleIconsPerSlot() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class);
+
+        statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child
+
+        // All of these can be added to the same slot
+        // no tag bc it defaults to 0
+        StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class);
+        int sb3Tag = 1;
+        when(sbHolder3.getTag()).thenReturn(sb3Tag);
+        StatusBarIconHolder sbHolder4 = mock(StatusBarIconHolder.class);
+        int sb4Tag = 2;
+        when(sbHolder4.getTag()).thenReturn(sb4Tag);
+
+        // Put a holder at slot 1, verify that it is first
+        statusBarIconList.setIcon(1, sbHolder2);
+        assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+
+        // Put another holder at slot 1, verify it's index 0 and the rest come after
+        statusBarIconList.setIcon(1, sbHolder3);
+        assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag));
+        assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+        // First icon should be at the end
+        assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+
+        // Put another one in there just for good measure
+        statusBarIconList.setIcon(1, sbHolder4);
+        assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag));
+        assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag));
+        assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY));
+        assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY));
+    }
+
+    /**
+     * StatusBarIconList.Slot tests
+     */
+
+    @Test
+    public void testSlot_OrderIsPreserved() {
+        Slot testSlot = new Slot("test_name", null);
+
+        // no tag bc it defaults to 0
+        StatusBarIconHolder sbHolder1 = mock(StatusBarIconHolder.class);
+        StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class);
+        int sb2Tag = 1;
+        when(sbHolder2.getTag()).thenReturn(sb2Tag);
+        StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class);
+        int sb3Tag = 2;
+        when(sbHolder3.getTag()).thenReturn(sb3Tag);
+
+        ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
+        expected.add(sbHolder1);
+        expected.add(sbHolder2);
+        expected.add(sbHolder3);
+
+
+        // Add 3 icons in the same slot, and verify that the list we get is equal to what we gave
+        for (StatusBarIconHolder holder : expected) {
+            testSlot.addHolder(holder);
+        }
+        assertTrue(listsEqual(expected, testSlot.getHolderList()));
+    }
+
+    private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) {
+        if (list1.size() != list2.size())  return false;
+
+        for (int i = 0; i < list1.size(); i++) {
+            if (!list1.get(i).equals(list2.get(i))) {
+                return false;
+            }
+        }
+
+        return true;
+    }
 }
\ No newline at end of file
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 7743c6b..a88f31a 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
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
@@ -73,7 +74,7 @@
     @Before
     public void setup() {
         mLightBarController = mock(LightBarController.class);
-        mScrimBehind = new ScrimView(getContext());
+        mScrimBehind = spy(new ScrimView(getContext()));
         mScrimInFront = new ScrimView(getContext());
         mWakeLock = mock(WakeLock.class);
         mAlarmManager = mock(AlarmManager.class);
@@ -210,8 +211,10 @@
         mScrimController.finishAnimationsImmediately();
 
         final float scrimAlpha = mScrimBehind.getViewAlpha();
+        reset(mScrimBehind);
         mScrimController.setExpansionAffectsAlpha(false);
         mScrimController.setPanelExpansion(0.8f);
+        verifyZeroInteractions(mScrimBehind);
         Assert.assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
                 + "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
index 5db7792..72b0156 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java
@@ -14,10 +14,17 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE;
+import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
+import android.graphics.Rect;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -25,9 +32,14 @@
 import android.widget.LinearLayout;
 
 import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.StatusBarIconView;
+import com.android.systemui.statusbar.StatusBarMobileView;
+import com.android.systemui.statusbar.StatusBarWifiView;
 import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
 import com.android.systemui.statusbar.policy.DarkIconDispatcher;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -50,50 +62,119 @@
     public void testSetCalledOnAdd_IconManager() {
         LinearLayout layout = new LinearLayout(mContext);
         TestIconManager manager = new TestIconManager(layout);
-        StatusBarIcon icon = mock(StatusBarIcon.class);
-
-        manager.onIconAdded(0, "test_slot", false, icon);
-        verify(manager.mMock).set(eq(icon));
+        testCallOnAdd_forManager(manager);
     }
 
     @Test
     public void testSetCalledOnAdd_DarkIconManager() {
         LinearLayout layout = new LinearLayout(mContext);
         TestDarkIconManager manager = new TestDarkIconManager(layout);
-        StatusBarIcon icon = mock(StatusBarIcon.class);
-
-        manager.onIconAdded(0, "test_slot", false, icon);
-        verify(manager.mMock).set(eq(icon));
+        testCallOnAdd_forManager(manager);
     }
 
-    private static class TestDarkIconManager extends DarkIconManager {
 
-        private final StatusBarIconView mMock;
+    private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) {
+        StatusBarIconHolder holder = holderForType(TYPE_ICON);
+        manager.onIconAdded(0, "test_slot", false, holder);
+        assertTrue("Expected StatusBarIconView",
+                (manager.getViewAt(0) instanceof StatusBarIconView));
+
+        holder = holderForType(TYPE_WIFI);
+        manager.onIconAdded(1, "test_wifi", false, holder);
+        assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView);
+
+        holder = holderForType(TYPE_MOBILE);
+        manager.onIconAdded(2, "test_mobile", false, holder);
+        assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView);
+    }
+
+    private StatusBarIconHolder holderForType(int type) {
+        switch (type) {
+            case TYPE_MOBILE:
+                return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class));
+
+            case TYPE_WIFI:
+                return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class));
+
+            case TYPE_ICON:
+            default:
+                return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class));
+        }
+    }
+
+    private static class TestDarkIconManager extends DarkIconManager
+            implements TestableIconManager {
 
         public TestDarkIconManager(LinearLayout group) {
             super(group);
-            mMock = mock(StatusBarIconView.class);
         }
 
         @Override
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
-            return mMock;
+        public StatusIconDisplayable getViewAt(int index) {
+            return (StatusIconDisplayable) mGroup.getChildAt(index);
+        }
+
+        @Override
+        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
+                StatusBarIcon icon) {
+            StatusBarIconView mock = mock(StatusBarIconView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
+        }
+
+        @Override
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView mock = mock(StatusBarWifiView.class);
+            mGroup.addView(mock, index);
+            return mock;
+        }
+
+        @Override
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView mock = mock(StatusBarMobileView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
         }
     }
 
-    private static class TestIconManager extends IconManager {
-
-        private final StatusBarIconView mMock;
-
+    private static class TestIconManager extends IconManager implements TestableIconManager {
         public TestIconManager(ViewGroup group) {
             super(group);
-            mMock = mock(StatusBarIconView.class);
         }
 
         @Override
-        protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) {
-            return mMock;
+        public StatusIconDisplayable getViewAt(int index) {
+            return (StatusIconDisplayable) mGroup.getChildAt(index);
+        }
+
+        @Override
+        protected StatusBarIconView addIcon(int index, String slot, boolean blocked,
+                StatusBarIcon icon) {
+            StatusBarIconView mock = mock(StatusBarIconView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
+        }
+
+        @Override
+        protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) {
+            StatusBarWifiView mock = mock(StatusBarWifiView.class);
+            mGroup.addView(mock, index);
+            return mock;
+        }
+
+        @Override
+        protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
+            StatusBarMobileView mock = mock(StatusBarMobileView.class);
+            mGroup.addView(mock, index);
+
+            return mock;
         }
     }
 
+    private interface TestableIconManager {
+        StatusIconDisplayable getViewAt(int index);
+    }
 }
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 f81ffd8..b31a2dc 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
@@ -41,6 +41,7 @@
 import android.app.StatusBarManager;
 import android.app.trust.TrustManager;
 import android.content.Context;
+import android.content.pm.UserInfo;
 import android.hardware.fingerprint.FingerprintManager;
 import android.metrics.LogMaker;
 import android.os.Binder;
@@ -70,6 +71,8 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
@@ -90,6 +93,7 @@
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.ActivityLaunchAnimator;
@@ -97,6 +101,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 import com.android.systemui.statusbar.policy.KeyguardMonitorImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
 
 import org.junit.Before;
@@ -132,6 +137,8 @@
     @Mock private NotificationViewHierarchyManager mViewHierarchyManager;
     @Mock private VisualStabilityManager mVisualStabilityManager;
     @Mock private NotificationListener mNotificationListener;
+    @Mock private KeyguardViewMediator mKeyguardViewMediator;
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
 
     private TestableStatusBar mStatusBar;
     private FakeMetricsLogger mMetricsLogger;
@@ -144,9 +151,7 @@
         MockitoAnnotations.initMocks(this);
         mDependency.injectMockDependency(AssistManager.class);
         mDependency.injectMockDependency(DeviceProvisionedController.class);
-        mDependency.injectMockDependency(NotificationGroupManager.class);
         mDependency.injectMockDependency(NotificationGutsManager.class);
-        mDependency.injectMockDependency(NotificationRemoteInputManager.class);
         mDependency.injectMockDependency(NotificationMediaManager.class);
         mDependency.injectMockDependency(ForegroundServiceController.class);
         mDependency.injectTestDependency(NotificationViewHierarchyManager.class,
@@ -200,7 +205,12 @@
                 mPowerManager, mNotificationPanelView, mBarService, mNotificationListener,
                 mNotificationLogger, mVisualStabilityManager, mViewHierarchyManager,
                 mEntryManager, mScrimController, mFingerprintUnlockController,
-                mock(ActivityLaunchAnimator.class));
+                mock(ActivityLaunchAnimator.class), mKeyguardViewMediator,
+                mock(NotificationRemoteInputManager.class), mock(NotificationGroupManager.class),
+                mock(FalsingManager.class), mock(StatusBarWindowManager.class),
+                mock(NotificationIconAreaController.class), mock(DozeScrimController.class),
+                mock(NotificationShelf.class), mLockscreenUserManager,
+                mock(CommandQueue.class));
         mStatusBar.mContext = mContext;
         mStatusBar.mComponents = mContext.getComponents();
         mEntryManager.setUpForTest(mStatusBar, mStackScroller, mStatusBar, mHeadsUpManager,
@@ -527,11 +537,7 @@
     @Test
     @RunWithLooper(setAsMainLooper = true)
     public void testUpdateKeyguardState_DoesNotCrash() {
-        mStatusBar.mStatusBarWindow = mock(StatusBarWindowView.class);
         mStatusBar.mState = StatusBarState.KEYGUARD;
-        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
-        mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
-        mStatusBar.mLockscreenUserManager = mock(NotificationLockscreenUserManager.class);
         when(mStatusBar.mLockscreenUserManager.getCurrentProfiles()).thenReturn(
                 new SparseArray<>());
         mStatusBar.updateKeyguardState(false, false);
@@ -539,9 +545,6 @@
 
     @Test
     public void testFingerprintNotification_UpdatesScrims() {
-        mStatusBar.mStatusBarWindowManager = mock(StatusBarWindowManager.class);
-        mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
-        mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
         mStatusBar.notifyFpAuthModeChanged();
         verify(mScrimController).transitionTo(any(), any());
     }
@@ -627,6 +630,32 @@
         verify(mStackScroller).changeViewPosition(any(FooterView.class), eq(-1 /* end */));
     }
 
+    @Test
+    public void testSetState_changesIsFullScreenUserSwitcherState() {
+        mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+        assertFalse(mStatusBar.isFullScreenUserSwitcherState());
+
+        mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER);
+        assertTrue(mStatusBar.isFullScreenUserSwitcherState());
+    }
+
+    @Test
+    public void testShowKeyguardImplementation_setsState() {
+        when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
+
+        mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+
+        // By default, showKeyguardImpl sets state to KEYGUARD.
+        mStatusBar.showKeyguardImpl();
+        assertTrue(mStatusBar.mState == StatusBarState.KEYGUARD);
+
+        // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
+        mStatusBar.mUserSwitcherController = mock(UserSwitcherController.class);
+        when(mStatusBar.mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
+        mStatusBar.showKeyguardImpl();
+        assertTrue(mStatusBar.mState == StatusBarState.FULLSCREEN_USER_SWITCHER);
+    }
+
     static class TestableStatusBar extends StatusBar {
         public TestableStatusBar(StatusBarKeyguardViewManager man,
                 UnlockMethodCache unlock, KeyguardIndicationController key,
@@ -638,7 +667,16 @@
                 NotificationViewHierarchyManager viewHierarchyManager,
                 TestableNotificationEntryManager entryManager, ScrimController scrimController,
                 FingerprintUnlockController fingerprintUnlockController,
-                ActivityLaunchAnimator launchAnimator) {
+                ActivityLaunchAnimator launchAnimator, KeyguardViewMediator keyguardViewMediator,
+                NotificationRemoteInputManager notificationRemoteInputManager,
+                NotificationGroupManager notificationGroupManager,
+                FalsingManager falsingManager,
+                StatusBarWindowManager statusBarWindowManager,
+                NotificationIconAreaController notificationIconAreaController,
+                DozeScrimController dozeScrimController,
+                NotificationShelf notificationShelf,
+                NotificationLockscreenUserManager notificationLockscreenUserManager,
+                CommandQueue commandQueue) {
             mStatusBarKeyguardViewManager = man;
             mUnlockMethodCache = unlock;
             mKeyguardIndicationController = key;
@@ -656,7 +694,17 @@
             mScrimController = scrimController;
             mFingerprintUnlockController = fingerprintUnlockController;
             mActivityLaunchAnimator = launchAnimator;
+            mKeyguardViewMediator = keyguardViewMediator;
             mClearAllEnabled = true;
+            mRemoteInputManager = notificationRemoteInputManager;
+            mGroupManager = notificationGroupManager;
+            mFalsingManager = falsingManager;
+            mStatusBarWindowManager = statusBarWindowManager;
+            mNotificationIconAreaController = notificationIconAreaController;
+            mDozeScrimController = dozeScrimController;
+            mNotificationShelf = notificationShelf;
+            mLockscreenUserManager = notificationLockscreenUserManager;
+            mCommandQueue = commandQueue;
         }
 
         private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
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 42069e2..cd3031b 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
@@ -16,7 +16,12 @@
 
 package com.android.systemui.statusbar.stack;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -25,33 +30,73 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
 
+import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.TestableDependency;
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.FooterView;
+import com.android.systemui.statusbar.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
 
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
+/**
+ * Tests for {@link NotificationStackScrollLayout}.
+ */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class NotificationStackScrollLayoutTest extends SysuiTestCase {
 
     private NotificationStackScrollLayout mStackScroller;
-    private StatusBar mBar;
+
+    @Rule public MockitoRule mockito = MockitoJUnit.rule();
+    @Mock private StatusBar mBar;
+    @Mock private HeadsUpManagerPhone mHeadsUpManager;
+    @Mock private NotificationBlockingHelperManager mBlockingHelperManager;
+    @Mock private NotificationGroupManager mGroupManager;
+    @Mock private ExpandHelper mExpandHelper;
+    @Mock private EmptyShadeView mEmptyShadeView;
 
     @Before
     @UiThreadTest
     public void setUp() throws Exception {
+        // Inject dependencies before initializing the layout
+        mDependency.injectTestDependency(
+                NotificationBlockingHelperManager.class,
+                mBlockingHelperManager);
+
+        NotificationShelf notificationShelf = spy(new NotificationShelf(getContext(), null));
         mStackScroller = new NotificationStackScrollLayout(getContext());
-        mBar = mock(StatusBar.class);
+        mStackScroller.setShelf(notificationShelf);
         mStackScroller.setStatusBar(mBar);
         mStackScroller.setScrimController(mock(ScrimController.class));
+        mStackScroller.setHeadsUpManager(mHeadsUpManager);
+        mStackScroller.setGroupManager(mGroupManager);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+
+        // Stub out functionality that isn't necessary to test.
+        doNothing().when(mBar)
+                .executeRunnableDismissingKeyguard(any(Runnable.class),
+                        any(Runnable.class),
+                        anyBoolean(),
+                        anyBoolean(),
+                        anyBoolean());
+        doNothing().when(mGroupManager).collapseAllGroups();
+        doNothing().when(mExpandHelper).cancelImmediately();
+        doNothing().when(notificationShelf).setAnimationsEnabled(anyBoolean());
     }
 
     @Test
@@ -75,26 +120,34 @@
 
     @Test
     public void updateEmptyView_dndSuppressing() {
-        EmptyShadeView view = mock(EmptyShadeView.class);
-        mStackScroller.setEmptyShadeView(view);
-        when(view.willBeGone()).thenReturn(true);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
         when(mBar.areNotificationsHidden()).thenReturn(true);
 
         mStackScroller.updateEmptyShadeView(true);
 
-        verify(view).setText(R.string.dnd_suppressing_shade_text);
+        verify(mEmptyShadeView).setText(R.string.dnd_suppressing_shade_text);
     }
 
     @Test
     public void updateEmptyView_dndNotSuppressing() {
-        EmptyShadeView view = mock(EmptyShadeView.class);
-        mStackScroller.setEmptyShadeView(view);
-        when(view.willBeGone()).thenReturn(true);
+        mStackScroller.setEmptyShadeView(mEmptyShadeView);
+        when(mEmptyShadeView.willBeGone()).thenReturn(true);
         when(mBar.areNotificationsHidden()).thenReturn(false);
 
         mStackScroller.updateEmptyShadeView(true);
 
-        verify(view).setText(R.string.empty_shade_text);
+        verify(mEmptyShadeView).setText(R.string.empty_shade_text);
+    }
+
+    @Test
+    @UiThreadTest
+    public void testSetExpandedHeight_blockingHelperManagerReceivedCallbacks() {
+        mStackScroller.setExpandedHeight(0f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(0f);
+        reset(mBlockingHelperManager);
+
+        mStackScroller.setExpandedHeight(100f);
+        verify(mBlockingHelperManager).setNotificationShadeExpanded(100f);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
index 80dc2c9..50b4f3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java
@@ -60,7 +60,8 @@
                 NotificationChannels.SCREENSHOTS_HEADSUP,
                 NotificationChannels.STORAGE,
                 NotificationChannels.GENERAL,
-                NotificationChannels.BATTERY
+                NotificationChannels.BATTERY,
+                NotificationChannels.HINTS
         ));
         NotificationChannels.createAll(mContext);
         ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 6b501af..8e34685 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -19,6 +19,9 @@
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
+import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import java.util.List;
 
 public class FakeStatusBarIconController extends BaseLeakChecker<IconManager>
         implements StatusBarIconController {
@@ -53,12 +56,23 @@
     }
 
     @Override
-    public void setIconVisibility(String slotTty, boolean b) {
-
+    public void setSignalIcon(String slot, WifiIconState state) {
     }
 
     @Override
-    public void removeIcon(String slot) {
-
+    public void setMobileIcons(String slot, List<MobileIconState> states) {
     }
+
+    @Override
+    public void setIconVisibility(String slotTty, boolean b) {
+    }
+
+    @Override
+    public void removeIcon(String slot, int tag) {
+    }
+
+    @Override
+    public void removeAllIconsForSlot(String slot) {
+    }
+
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 608970f..f9af31c 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -208,6 +208,10 @@
     // Package: android
     NOTE_ZEN_UPGRADE = 48;
 
+    // Notification to suggest automatic battery saver.
+    // Package: android
+    NOTE_AUTO_SAVER_SUGGESTION = 49;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 9c74188..934ad88 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -249,12 +249,12 @@
   optional int32 num_wificond_crashes = 54;
 
   // Indicates the number of times an error was encountered in
-  // Wifi HAL when wifi was turned on.
-  optional int32 num_wifi_on_failure_due_to_hal = 55;
+  // Wifi HAL on |WifiNative.setupInterfaceForClientMode|.
+  optional int32 num_setup_client_interface_failure_due_to_hal = 55;
 
   // Indicates the number of times an error was encountered in
-  // Wificond when wifi was turned on.
-  optional int32 num_wifi_on_failure_due_to_wificond = 56;
+  // Wificond on |WifiNative.setupInterfaceForClientMode|.
+  optional int32 num_setup_client_interface_failure_due_to_wificond = 56;
 
   // Wi-Fi Aware metrics
   optional WifiAwareLog wifi_aware_log = 57;
@@ -385,6 +385,43 @@
 
   // Histogram counting instances of scans with N many 802.11mc (RTT) supporting APs
   repeated NumConnectableNetworksBucket observed_80211mc_supporting_aps_in_scan_histogram = 95;
+
+  // Total number of times supplicant crashed.
+  optional int32 num_supplicant_crashes = 96;
+
+  // Total number of times hostapd crashed.
+  optional int32 num_hostapd_crashes = 97;
+
+  // Indicates the number of times an error was encountered in
+  // supplicant on |WifiNative.setupInterfaceForClientMode|.
+  optional int32 num_setup_client_interface_failure_due_to_supplicant = 98;
+
+  // Indicates the number of times an error was encountered in
+  // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+  optional int32 num_setup_soft_ap_interface_failure_due_to_hal = 99;
+
+  // Indicates the number of times an error was encountered in
+  // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+  optional int32 num_setup_soft_ap_interface_failure_due_to_wificond = 100;
+
+  // Indicates the number of times an error was encountered in
+  // Wifi HAL on |WifiNative.setupInterfaceForSoftApMode|.
+  optional int32 num_setup_soft_ap_interface_failure_due_to_hostapd = 101;
+
+  // Indicates the number of times we got an interface down in client mode.
+  optional int32 num_client_interface_down = 102;
+
+  // Indicates the number of times we got an interface down in softap mode.
+  optional int32 num_soft_ap_interface_down = 103;
+
+  // Indicates the number of scan requests from external apps.
+  optional int32 num_external_app_oneshot_scan_requests = 104;
+
+  // Indicates the number of times a scan request from an external foreground app was throttled.
+  optional int32 num_external_foreground_app_oneshot_scan_requests_throttled = 105;
+
+  // Indicates the number of times a scan request from an external background app was throttled.
+  optional int32 num_external_background_app_oneshot_scan_requests_throttled = 106;
 }
 
 // Information that gets logged for every WiFi connection.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8941b49..de112f9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -853,11 +853,7 @@
             if (mSecurityPolicy.findA11yWindowInfoById(windowId) == null) {
                 return null;
             }
-            IBinder token = mGlobalWindowTokens.get(windowId);
-            if (token != null) {
-                return token;
-            }
-            return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+            return findWindowTokenLocked(windowId);
         }
     }
 
@@ -2527,6 +2523,14 @@
         getInteractionBridge().clearAccessibilityFocusNotLocked(windowId);
     }
 
+    private IBinder findWindowTokenLocked(int windowId) {
+        IBinder token = mGlobalWindowTokens.get(windowId);
+        if (token != null) {
+            return token;
+        }
+        return getCurrentUserStateLocked().mWindowTokens.get(windowId);
+    }
+
     private int findWindowIdLocked(IBinder token) {
         final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
         if (globalIndex >= 0) {
@@ -2986,7 +2990,7 @@
                 // the accessibility layer reports which are windows
                 // that a sighted user can touch.
                 default: {
-                    return isRetrievalAllowingWindow(event.getWindowId());
+                    return isRetrievalAllowingWindowLocked(event.getWindowId());
                 }
             }
         }
@@ -3438,7 +3442,8 @@
 
         public boolean canGetAccessibilityNodeInfoLocked(
                 AbstractAccessibilityServiceConnection service, int windowId) {
-            return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
+            return canRetrieveWindowContentLocked(service)
+                    && isRetrievalAllowingWindowLocked(windowId);
         }
 
         public boolean canRetrieveWindowsLocked(AbstractAccessibilityServiceConnection service) {
@@ -3523,17 +3528,40 @@
                     || userId == UserHandle.USER_CURRENT_OR_SELF);
         }
 
-        private boolean isRetrievalAllowingWindow(int windowId) {
+        private boolean isRetrievalAllowingWindowLocked(int windowId) {
             // The system gets to interact with any window it wants.
             if (Binder.getCallingUid() == Process.SYSTEM_UID) {
                 return true;
             }
+            if (Binder.getCallingUid() == Process.SHELL_UID) {
+                if (!isShellAllowedToRetrieveWindowLocked(windowId)) {
+                    return false;
+                }
+            }
             if (windowId == mActiveWindowId) {
                 return true;
             }
             return findA11yWindowInfoById(windowId) != null;
         }
 
+        private boolean isShellAllowedToRetrieveWindowLocked(int windowId) {
+            long token = Binder.clearCallingIdentity();
+            try {
+                IBinder windowToken = findWindowTokenLocked(windowId);
+                if (windowToken == null) {
+                    return false;
+                }
+                int userId = mWindowManagerService.getWindowOwnerUserId(windowToken);
+                if (userId == UserHandle.USER_NULL) {
+                    return false;
+                }
+                return !mUserManager.hasUserRestriction(
+                        UserManager.DISALLOW_DEBUGGING_FEATURES, UserHandle.of(userId));
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
         public AccessibilityWindowInfo findA11yWindowInfoById(int windowId) {
             return mA11yWindowInfoById.get(windowId);
         }
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 009e723..3253f2e 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -19,8 +19,8 @@
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
 
-import static com.android.server.autofill.Helper.bundleToString;
 import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sPartitionMaxCount;
 import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 import static com.android.server.autofill.Helper.sVerbose;
@@ -80,7 +80,6 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
-import com.android.server.autofill.AutofillManagerService.PackageCompatState;
 import com.android.server.autofill.ui.AutoFillUI;
 
 import java.io.FileDescriptor;
@@ -193,8 +192,7 @@
                 if (disabledBefore == disabledNow) {
                     // Nothing changed, do nothing.
                     if (sDebug) {
-                        Slog.d(TAG, "Autofill restriction did not change for user " + userId + ": "
-                                + bundleToString(newRestrictions));
+                        Slog.d(TAG, "Autofill restriction did not change for user " + userId);
                         return;
                     }
                 }
@@ -449,7 +447,7 @@
     }
 
     // Called by Shell command.
-    public int getMaxPartitions() {
+    int getMaxPartitions() {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
@@ -458,7 +456,7 @@
     }
 
     // Called by Shell command.
-    public void setMaxPartitions(int max) {
+    void setMaxPartitions(int max) {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxPartitions(): " + max);
         synchronized (mLock) {
@@ -467,7 +465,7 @@
     }
 
     // Called by Shell command.
-    public int getMaxVisibleDatasets() {
+    int getMaxVisibleDatasets() {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
         synchronized (mLock) {
@@ -476,7 +474,7 @@
     }
 
     // Called by Shell command.
-    public void setMaxVisibleDatasets(int max) {
+    void setMaxVisibleDatasets(int max) {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
         Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
         synchronized (mLock) {
@@ -485,7 +483,7 @@
     }
 
     // Called by Shell command.
-    public void getScore(@Nullable String algorithmName, @NonNull String value1,
+    void getScore(@Nullable String algorithmName, @NonNull String value1,
             @NonNull String value2, @NonNull RemoteCallback callback) {
         mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
 
@@ -496,6 +494,18 @@
                 Arrays.asList(AutofillValue.forText(value1)), new String[] { value2 });
     }
 
+    // Called by Shell command.
+    Boolean getFullScreenMode() {
+        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        return sFullScreenMode;
+    }
+
+    // Called by Shell command.
+    void setFullScreenMode(@Nullable Boolean mode) {
+        mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+        sFullScreenMode = mode;
+    }
+
     private void setDebugLocked(boolean debug) {
         com.android.server.autofill.Helper.sDebug = debug;
         android.view.autofill.Helper.sDebug = debug;
@@ -1036,6 +1046,18 @@
         }
 
         @Override
+        public void setAutofillFailure(int sessionId, @NonNull List<AutofillId> ids, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+                if (service != null) {
+                    service.setAutofillFailureLocked(sessionId, getCallingUid(), ids);
+                } else if (sVerbose) {
+                    Slog.v(TAG, "setAutofillFailure(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
         public void finishSession(int sessionId, int userId) {
             synchronized (mLock) {
                 final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
@@ -1147,6 +1169,9 @@
                     pw.print("Disabled users: "); pw.println(mDisabledUsers);
                     pw.print("Max partitions per session: "); pw.println(sPartitionMaxCount);
                     pw.print("Max visible datasets: "); pw.println(sVisibleDatasetsMaxCount);
+                    if (sFullScreenMode != null) {
+                        pw.print("Overridden full-screen mode: "); pw.println(sFullScreenMode);
+                    }
                     pw.println("User data constraints: "); UserData.dumpConstraints(prefix, pw);
                     final int size = mServicesCache.size();
                     pw.print("Cached services: ");
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index ef0d7e6..0bb29a7 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -404,6 +404,19 @@
     }
 
     @GuardedBy("mLock")
+    void setAutofillFailureLocked(int sessionId, int uid, @NonNull List<AutofillId> ids) {
+        if (!isEnabledLocked()) {
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "setAutofillFailure(): no session for " + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.setAutofillFailureLocked(ids);
+    }
+
+    @GuardedBy("mLock")
     void finishSessionLocked(int sessionId, int uid) {
         if (!isEnabledLocked()) {
             return;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index 1904061..c76c8ac 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -80,6 +80,12 @@
             pw.println("  get max_visible_datasets");
             pw.println("    Gets the maximum number of visible datasets in the UI.");
             pw.println("");
+            pw.println("  get full_screen_mode");
+            pw.println("    Gets the Fill UI full screen mode");
+            pw.println("");
+            pw.println("  get fc_score [--algorithm ALGORITHM] value1 value2");
+            pw.println("    Gets the field classification score for 2 fields.");
+            pw.println("");
             pw.println("  set log_level [off | debug | verbose]");
             pw.println("    Sets the Autofill log level.");
             pw.println("");
@@ -89,6 +95,9 @@
             pw.println("  set max_visible_datasets number");
             pw.println("    Sets the maximum number of visible datasets in the UI.");
             pw.println("");
+            pw.println("  set full_screen_mode [true | false | default]");
+            pw.println("    Sets the Fill UI full screen mode");
+            pw.println("");
             pw.println("  list sessions [--user USER_ID]");
             pw.println("    Lists all pending sessions.");
             pw.println("");
@@ -98,9 +107,6 @@
             pw.println("  reset");
             pw.println("    Resets all pending sessions and cached service connections.");
             pw.println("");
-            pw.println("  get fc_score [--algorithm ALGORITHM] value1 value2");
-            pw.println("    Gets the field classification score for 2 fields.");
-            pw.println("");
         }
     }
 
@@ -115,6 +121,8 @@
                 return getMaxVisibileDatasets(pw);
             case "fc_score":
                 return getFieldClassificationScore(pw);
+            case "full_screen_mode":
+                return getFullScreenMode(pw);
             default:
                 pw.println("Invalid set: " + what);
                 return -1;
@@ -131,6 +139,8 @@
                 return setMaxPartitions();
             case "max_visible_datasets":
                 return setMaxVisibileDatasets();
+            case "full_screen_mode":
+                return setFullScreenMode(pw);
             default:
                 pw.println("Invalid set: " + what);
                 return -1;
@@ -219,6 +229,36 @@
         return waitForLatch(pw, latch);
     }
 
+    private int getFullScreenMode(PrintWriter pw) {
+        final Boolean mode = mService.getFullScreenMode();
+        if (mode == null) {
+            pw.println("default");
+        } else if (mode) {
+            pw.println("true");
+        } else {
+            pw.println("false");
+        }
+        return 0;
+    }
+
+    private int setFullScreenMode(PrintWriter pw) {
+        final String mode = getNextArgRequired();
+        switch (mode.toLowerCase()) {
+            case "true":
+                mService.setFullScreenMode(Boolean.TRUE);
+                return 0;
+            case "false":
+                mService.setFullScreenMode(Boolean.FALSE);
+                return 0;
+            case "default":
+                mService.setFullScreenMode(null);
+                return 0;
+            default:
+                pw.println("Invalid mode: " + mode);
+                return -1;
+        }
+    }
+
     private int requestDestroy(PrintWriter pw) {
         if (!isNextArgSessions(pw)) {
             return -1;
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 58f78ec..78526f5 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -70,32 +70,15 @@
      */
     public static int sVisibleDatasetsMaxCount = 3;
 
+    /**
+     * When non-null, overrides whether the UI should be shown on full-screen mode.
+     */
+    public static Boolean sFullScreenMode = null;
+
     private Helper() {
         throw new UnsupportedOperationException("contains static members only");
     }
 
-    static void append(StringBuilder builder, Bundle bundle) {
-        if (bundle == null || !sVerbose) {
-            builder.append("null");
-            return;
-        }
-        final Set<String> keySet = bundle.keySet();
-        builder.append("[Bundle with ").append(keySet.size()).append(" extras:");
-        for (String key : keySet) {
-            final Object value = bundle.get(key);
-            builder.append(' ').append(key).append('=');
-            builder.append((value instanceof Object[])
-                    ? Arrays.toString((Objects[]) value) : value);
-        }
-        builder.append(']');
-    }
-
-    static String bundleToString(Bundle bundle) {
-        final StringBuilder builder = new StringBuilder();
-        append(builder, bundle);
-        return builder.toString();
-    }
-
     @Nullable
     static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
         if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index bcb0bc4..e14584f 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1827,6 +1827,11 @@
             }
             viewState.setState(ViewState.STATE_STARTED_PARTITION);
             requestNewFillResponseLocked(flags);
+        } else {
+            if (sVerbose) {
+                Slog.v(TAG, "Not starting new partition for view " + id + ": "
+                        + viewState.getStateAsString());
+            }
         }
     }
 
@@ -2194,6 +2199,27 @@
         }
     }
 
+    /**
+     * Sets the state of views that failed to autofill.
+     */
+    @GuardedBy("mLock")
+    void setAutofillFailureLocked(@NonNull List<AutofillId> ids) {
+        for (int i = 0; i < ids.size(); i++) {
+            final AutofillId id = ids.get(i);
+            final ViewState viewState = mViewStates.get(id);
+            if (viewState == null) {
+                Slog.w(TAG, "setAutofillFailure(): no view for id " + id);
+                continue;
+            }
+            viewState.resetState(ViewState.STATE_AUTOFILLED);
+            final int state = viewState.getState();
+            viewState.setState(state | ViewState.STATE_AUTOFILL_FAILED);
+            if (sVerbose) {
+                Slog.v(TAG, "Changed state of " + id + " to " + viewState.getStateAsString());
+            }
+        }
+    }
+
     @GuardedBy("mLock")
     private void replaceResponseLocked(@NonNull FillResponse oldResponse,
             @NonNull FillResponse newResponse, @Nullable Bundle newClientState) {
@@ -2476,8 +2502,10 @@
         }
 
         pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
-        pw.print(prefix); pw.print("mClientState: "); pw.println(
-                Helper.bundleToString(mClientState));
+        if (mClientState != null) {
+            pw.print(prefix); pw.print("mClientState: "); pw.print(mClientState.getSize()); pw
+                .println(" bytes");
+        }
         pw.print(prefix); pw.print("mCompatMode: "); pw.println(mCompatMode);
         pw.print(prefix); pw.print("mUrlBar: ");
         if (mUrlBar == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ViewState.java b/services/autofill/java/com/android/server/autofill/ViewState.java
index 97ab97b..9210de2 100644
--- a/services/autofill/java/com/android/server/autofill/ViewState.java
+++ b/services/autofill/java/com/android/server/autofill/ViewState.java
@@ -69,6 +69,8 @@
     public static final int STATE_RESTARTED_SESSION = 0x100;
     /** View is the URL bar of a package on compat mode. */
     public  static final int STATE_URL_BAR = 0x200;
+    /** View was asked to autofil but failed to do so. */
+    public static final int STATE_AUTOFILL_FAILED = 0x400;
 
     public final AutofillId id;
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index edfc412..7c0671f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -17,7 +17,9 @@
 
 import static com.android.server.autofill.Helper.paramsToString;
 import static com.android.server.autofill.Helper.sDebug;
+import static com.android.server.autofill.Helper.sFullScreenMode;
 import static com.android.server.autofill.Helper.sVerbose;
+import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
 
 import android.annotation.AttrRes;
 import android.annotation.NonNull;
@@ -51,6 +53,7 @@
 import android.widget.Filter;
 import android.widget.Filterable;
 import android.widget.FrameLayout;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.RemoteViews;
 
@@ -58,8 +61,6 @@
 import com.android.server.UiThread;
 import com.android.server.autofill.Helper;
 
-import static com.android.server.autofill.Helper.sVisibleDatasetsMaxCount;
-
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -118,7 +119,9 @@
 
     private final @NonNull Callback mCallback;
 
+    private final @Nullable View mHeader;
     private final @NonNull ListView mListView;
+    private final @Nullable View mFooter;
 
     private final @Nullable ItemsAdapter mAdapter;
 
@@ -127,12 +130,17 @@
     private @Nullable AnnounceFilterResult mAnnounceFilterResult;
 
     private final boolean mFullScreen;
+    private final int mVisibleDatasetsMaxCount;
     private int mContentWidth;
     private int mContentHeight;
 
     private boolean mDestroyed;
 
     public static boolean isFullScreen(Context context) {
+        if (sFullScreenMode != null) {
+            if (sVerbose) Slog.v(TAG, "forcing full-screen mode to " + sFullScreenMode);
+            return sFullScreenMode;
+        }
         return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
     }
 
@@ -145,9 +153,18 @@
 
         final LayoutInflater inflater = LayoutInflater.from(context);
 
-        final ViewGroup decor = (ViewGroup) inflater.inflate(
-                mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
-                        : R.layout.autofill_dataset_picker, null);
+        final RemoteViews headerPresentation = response.getHeader();
+        final RemoteViews footerPresentation = response.getFooter();
+        final ViewGroup decor;
+        if (headerPresentation != null || footerPresentation != null) {
+            decor = (ViewGroup) inflater.inflate(
+                    mFullScreen ? R.layout.autofill_dataset_picker_header_footer_fullscreen
+                            : R.layout.autofill_dataset_picker_header_footer, null);
+        } else {
+            decor = (ViewGroup) inflater.inflate(
+                    mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
+                            : R.layout.autofill_dataset_picker, null);
+        }
 
         // if autofill ui is not fullscreen, send unhandled keyevent to app window.
         if (!mFullScreen) {
@@ -174,6 +191,16 @@
             }
         }
 
+        if (sVisibleDatasetsMaxCount > 0) {
+            mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
+            if (sVerbose) {
+                Slog.v(TAG, "overriding maximum visible datasets to " + mVisibleDatasetsMaxCount);
+            }
+        } else {
+            mVisibleDatasetsMaxCount = mContext.getResources()
+                    .getInteger(com.android.internal.R.integer.autofill_max_visible_datasets);
+        }
+
         final RemoteViews.OnClickHandler interceptionHandler = new RemoteViews.OnClickHandler() {
             @Override
             public boolean onClickHandler(View view, PendingIntent pendingIntent,
@@ -186,7 +213,9 @@
         };
 
         if (response.getAuthentication() != null) {
+            mHeader = null;
             mListView = null;
+            mFooter = null;
             mAdapter = null;
 
             // insert authentication item under autofill_dataset_container or decor
@@ -207,7 +236,7 @@
             decor.setFocusable(true);
             decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
 
-            Point maxSize = mTempPoint;
+            final Point maxSize = mTempPoint;
             resolveMaxWindowSize(context, maxSize);
             // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
             content.getLayoutParams().width = mFullScreen ? maxSize.x
@@ -226,38 +255,39 @@
             requestShowFillUi();
         } else {
             final int datasetCount = response.getDatasets().size();
-
-            // Total items include the (optional) header and footer - we cannot use listview's
-            // addHeader() and addFooter() because it would complicate the scrolling logic.
-            int totalItems = datasetCount;
-
-            RemoteViews.OnClickHandler clickBlocker = null;
-            final RemoteViews headerPresentation = response.getHeader();
-            View header = null;
-            if (headerPresentation != null) {
-                clickBlocker = newClickBlocker();
-                header = headerPresentation.apply(context, null, clickBlocker);
-                totalItems++;
+            if (sVerbose) {
+                Slog.v(TAG, "Number datasets: " + datasetCount + " max visible: "
+                        + mVisibleDatasetsMaxCount);
             }
 
-            final RemoteViews footerPresentation = response.getFooter();
-            View footer = null;
+            RemoteViews.OnClickHandler clickBlocker = null;
+            if (headerPresentation != null) {
+                clickBlocker = newClickBlocker();
+                mHeader = headerPresentation.apply(context, null, clickBlocker);
+                final LinearLayout headerContainer =
+                        decor.findViewById(R.id.autofill_dataset_header);
+                if (sVerbose) Slog.v(TAG, "adding header");
+                headerContainer.addView(mHeader);
+                headerContainer.setVisibility(View.VISIBLE);
+            } else {
+                mHeader = null;
+            }
+
             if (footerPresentation != null) {
                 if (clickBlocker == null) { // already set for header
                     clickBlocker = newClickBlocker();
                 }
-                footer = footerPresentation.apply(context, null, clickBlocker);
-                totalItems++;
-            }
-            if (sVerbose) {
-                Slog.v(TAG, "Number datasets: " + datasetCount + " Total items: " + totalItems);
+                mFooter = footerPresentation.apply(context, null, clickBlocker);
+                final LinearLayout footerContainer =
+                        decor.findViewById(R.id.autofill_dataset_footer);
+                if (sVerbose) Slog.v(TAG, "adding footer");
+                footerContainer.addView(mFooter);
+                footerContainer.setVisibility(View.VISIBLE);
+            } else {
+                mFooter = null;
             }
 
-            final ArrayList<ViewItem> items = new ArrayList<>(totalItems);
-            if (header != null) {
-                if (sVerbose) Slog.v(TAG, "adding header");
-                items.add(new ViewItem(null, null, false, null, header));
-            }
+            final ArrayList<ViewItem> items = new ArrayList<>(datasetCount);
             for (int i = 0; i < datasetCount; i++) {
                 final Dataset dataset = response.getDatasets().get(i);
                 final int index = dataset.getFieldIds().indexOf(focusedViewId);
@@ -299,10 +329,6 @@
                     items.add(new ViewItem(dataset, filterPattern, filterable, valueText, view));
                 }
             }
-            if (footer != null) {
-                if (sVerbose) Slog.v(TAG, "adding footer");
-                items.add(new ViewItem(null, null, false, null, footer));
-            }
 
             mAdapter = new ItemsAdapter(items);
 
@@ -311,11 +337,6 @@
             mListView.setVisibility(View.VISIBLE);
             mListView.setOnItemClickListener((adapter, view, position, id) -> {
                 final ViewItem vi = mAdapter.getItem(position);
-                if (vi.dataset == null) {
-                    // Clicked on header or footer; ignore.
-                    if (sDebug) Slog.d(TAG, "Ignoring click on item " + position + ": " + view);
-                    return;
-                }
                 mCallback.onDatasetPicked(vi.dataset);
             });
 
@@ -375,7 +396,7 @@
                     }
                     requestShowFillUi();
                 }
-                if (mAdapter.getCount() > sVisibleDatasetsMaxCount) {
+                if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
                     mListView.setVerticalScrollBarEnabled(true);
                     mListView.onVisibilityAggregated(true);
                 } else {
@@ -460,6 +481,13 @@
             changed = true;
             mContentWidth = maxSize.x;
         }
+
+        if (mHeader != null) {
+            mHeader.measure(widthMeasureSpec, heightMeasureSpec);
+            changed |= updateWidth(mHeader, maxSize);
+            changed |= updateHeight(mHeader, maxSize);
+        }
+
         for (int i = 0; i < itemCount; i++) {
             final View view = mAdapter.getItem(i).view;
             view.measure(widthMeasureSpec, heightMeasureSpec);
@@ -473,23 +501,40 @@
                     break;
                 }
             } else {
-                final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
-                final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
-                if (newContentWidth != mContentWidth) {
-                    mContentWidth = newContentWidth;
-                    changed = true;
-                }
-                // Update the width to fit only the first items up to max count
-                if (i < sVisibleDatasetsMaxCount) {
-                    final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
-                    final int newContentHeight = mContentHeight + clampedMeasuredHeight;
-                    if (newContentHeight != mContentHeight) {
-                        mContentHeight = newContentHeight;
-                        changed = true;
-                    }
+                changed |= updateWidth(view, maxSize);
+                if (i < mVisibleDatasetsMaxCount) {
+                    changed |= updateHeight(view, maxSize);
                 }
             }
         }
+
+        if (mFooter != null) {
+            mFooter.measure(widthMeasureSpec, heightMeasureSpec);
+            changed |= updateWidth(mFooter, maxSize);
+            changed |= updateHeight(mFooter, maxSize);
+        }
+        return changed;
+    }
+
+    private boolean updateWidth(View view, Point maxSize) {
+        boolean changed = false;
+        final int clampedMeasuredWidth = Math.min(view.getMeasuredWidth(), maxSize.x);
+        final int newContentWidth = Math.max(mContentWidth, clampedMeasuredWidth);
+        if (newContentWidth != mContentWidth) {
+            mContentWidth = newContentWidth;
+            changed = true;
+        }
+        return changed;
+    }
+
+    private boolean updateHeight(View view, Point maxSize) {
+        boolean changed = false;
+        final int clampedMeasuredHeight = Math.min(view.getMeasuredHeight(), maxSize.y);
+        final int newContentHeight = mContentHeight + clampedMeasuredHeight;
+        if (newContentHeight != mContentHeight) {
+            mContentHeight = newContentHeight;
+            changed = true;
+        }
         return changed;
     }
 
@@ -501,7 +546,7 @@
 
     private static void resolveMaxWindowSize(Context context, Point outPoint) {
         context.getDisplay().getSize(outPoint);
-        TypedValue typedValue = sTempTypedValue;
+        final TypedValue typedValue = sTempTypedValue;
         context.getTheme().resolveAttribute(R.attr.autofillDatasetPickerMaxWidth,
                 typedValue, true);
         outPoint.x = (int) typedValue.getFraction(outPoint.x, outPoint.x);
@@ -688,17 +733,29 @@
     public void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("mCallback: "); pw.println(mCallback != null);
         pw.print(prefix); pw.print("mFullScreen: "); pw.println(mFullScreen);
-        pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
-        pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
-        pw.print(prefix); pw.print("mFilterText: ");
-        Helper.printlnRedactedText(pw, mFilterText);
+        pw.print(prefix); pw.print("mVisibleDatasetsMaxCount: "); pw.println(
+                mVisibleDatasetsMaxCount);
+        if (mHeader != null) {
+            pw.print(prefix); pw.print("mHeader: "); pw.println(mHeader);
+        }
+        if (mListView != null) {
+            pw.print(prefix); pw.print("mListView: "); pw.println(mListView);
+        }
+        if (mFooter != null) {
+            pw.print(prefix); pw.print("mFooter: "); pw.println(mFooter);
+        }
+        if (mAdapter != null) {
+            pw.print(prefix); pw.print("mAdapter: "); pw.println(mAdapter);
+        }
+        if (mFilterText != null) {
+            pw.print(prefix); pw.print("mFilterText: ");
+            Helper.printlnRedactedText(pw, mFilterText);
+        }
         pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
         pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
         pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
-        pw.print(prefix); pw.print("mWindow: ");
-        if (mWindow == null) {
-            pw.println("N/A");
-        } else {
+        if (mWindow != null) {
+            pw.print(prefix); pw.print("mWindow: ");
             final String prefix2 = prefix + "  ";
             pw.println();
             pw.print(prefix2); pw.print("showing: "); pw.println(mWindow.mShowing);
diff --git a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
index 4de2c9b..49fa1cc 100644
--- a/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
+++ b/services/backup/java/com/android/server/backup/BackupAgentTimeoutParameters.java
@@ -21,6 +21,7 @@
 import android.provider.Settings;
 import android.util.KeyValueListParser;
 import android.util.KeyValueSettingObserver;
+import android.util.Slog;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -31,6 +32,8 @@
  * are represented as a comma-delimited key value list.
  */
 public class BackupAgentTimeoutParameters extends KeyValueSettingObserver {
+    private static final String TAG = "BackupAgentTimeout";
+
     @VisibleForTesting
     public static final String SETTING = Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS;
 
@@ -120,30 +123,50 @@
 
     public long getKvBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getKvBackupAgentTimeoutMillis(): " + mKvBackupAgentTimeoutMillis);
+            }
             return mKvBackupAgentTimeoutMillis;
         }
     }
 
     public long getFullBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getFullBackupAgentTimeoutMillis(): " + mFullBackupAgentTimeoutMillis);
+            }
             return mFullBackupAgentTimeoutMillis;
         }
     }
 
     public long getSharedBackupAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(
+                        TAG,
+                        "getSharedBackupAgentTimeoutMillis(): " + mSharedBackupAgentTimeoutMillis);
+            }
             return mSharedBackupAgentTimeoutMillis;
         }
     }
 
     public long getRestoreAgentTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(TAG, "getRestoreAgentTimeoutMillis(): " + mRestoreAgentTimeoutMillis);
+            }
             return mRestoreAgentTimeoutMillis;
         }
     }
 
     public long getRestoreAgentFinishedTimeoutMillis() {
         synchronized (mLock) {
+            if (BackupManagerService.DEBUG_SCHEDULING) {
+                Slog.v(
+                        TAG,
+                        "getRestoreAgentFinishedTimeoutMillis(): "
+                                + mRestoreAgentFinishedTimeoutMillis);
+            }
             return mRestoreAgentFinishedTimeoutMillis;
         }
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d6f6c6c..bd51af2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -215,13 +215,6 @@
     // Timeout interval for deciding that a bind or clear-data has taken too long
     private static final long TIMEOUT_INTERVAL = 10 * 1000;
 
-    // Timeout intervals for agent backup & restore operations
-    public static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
-    public static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
-    public static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
-    public static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
-    public static final long TIMEOUT_RESTORE_FINISHED_INTERVAL = 30 * 1000;
-
     // User confirmation timeout for a full backup/restore operation.  It's this long in
     // order to give them time to enter the backup password.
     private static final long TIMEOUT_FULL_CONFIRMATION = 60 * 1000;
@@ -232,6 +225,7 @@
     private static final int BUSY_BACKOFF_FUZZ = 1000 * 60 * 60 * 2;  // two hours
 
     private BackupManagerConstants mConstants;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
     private Context mContext;
     private PackageManager mPackageManager;
     private IPackageManager mPackageManagerBinder;
@@ -315,6 +309,10 @@
         return mConstants;
     }
 
+    public BackupAgentTimeoutParameters getAgentTimeoutParameters() {
+        return mAgentTimeoutParameters;
+    }
+
     public Context getContext() {
         return mContext;
     }
@@ -799,6 +797,10 @@
 
         mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
 
+        mAgentTimeoutParameters = new
+                BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
+        mAgentTimeoutParameters.start();
+
         // spin up the backup/restore handler thread
         mBackupHandler = new BackupHandler(this, backupThread.getLooper());
 
@@ -3407,7 +3409,7 @@
             }
             mActiveRestoreSession = new ActiveRestoreSession(this, packageName, transport);
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
         }
         return mActiveRestoreSession;
     }
diff --git a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
index 7b021c6..aabe7f6 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerServiceInterface.java
@@ -191,4 +191,7 @@
   void dump(FileDescriptor fd, PrintWriter pw, String[] args);
 
   IBackupManager getBackupManagerBinder();
+
+  // Gets access to the backup/restore agent timeout parameters.
+  BackupAgentTimeoutParameters getAgentTimeoutParameters();
 }
diff --git a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
index 4755877..7f0030a 100644
--- a/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/KeyValueAdbBackupEngine.java
@@ -4,8 +4,8 @@
 import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.ParcelFileDescriptor.MODE_TRUNCATE;
+
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -19,6 +19,7 @@
 import android.os.SELinux;
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import libcore.io.IoUtils;
@@ -59,6 +60,7 @@
     private ParcelFileDescriptor mSavedState;
     private ParcelFileDescriptor mBackupData;
     private ParcelFileDescriptor mNewState;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public KeyValueAdbBackupEngine(OutputStream output, PackageInfo packageInfo,
             BackupManagerServiceInterface backupManagerService, PackageManager packageManager,
@@ -81,6 +83,9 @@
                 pkg + BACKUP_KEY_VALUE_NEW_STATE_FILENAME_SUFFIX);
 
         mManifestFile = new File(mDataDir, BackupManagerService.BACKUP_MANIFEST_FILENAME);
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void backupOnePackage() throws IOException {
@@ -148,8 +153,9 @@
     // Return true on backup success, false otherwise
     private boolean invokeAgentForAdbBackup(String packageName, IBackupAgent agent) {
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
         try {
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // Start backup and wait for BackupManagerService to get callback for success or timeout
@@ -231,14 +237,14 @@
     }
 
     private void writeBackupData() throws IOException {
-
         int token = mBackupManagerService.generateRandomIntegerToken();
+        long kvBackupAgentTimeoutMillis = mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
 
         ParcelFileDescriptor[] pipes = null;
         try {
             pipes = ParcelFileDescriptor.createPipe();
 
-            mBackupManagerService.prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL, null,
+            mBackupManagerService.prepareOperationTimeout(token, kvBackupAgentTimeoutMillis, null,
                     OP_TYPE_BACKUP_WAIT);
 
             // We will have to create a runnable that will read the manifest and backup data we
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
index 0582aba..5694659 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupEngine.java
@@ -25,9 +25,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 
 import android.app.ApplicationThreadConstants;
 import android.app.IBackupAgent;
@@ -44,9 +41,11 @@
 import android.util.Slog;
 import android.util.StringBuilderPrinter;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
-import com.android.server.backup.BackupRestoreTask;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
+import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.utils.FullBackupUtils;
 
 import java.io.BufferedOutputStream;
@@ -75,6 +74,7 @@
     private final long mQuota;
     private final int mOpToken;
     private final int mTransportFlags;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     class FullBackupRunner implements Runnable {
 
@@ -137,8 +137,8 @@
                 final boolean isSharedStorage =
                         mPackage.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE);
                 final long timeout = isSharedStorage ?
-                        TIMEOUT_SHARED_BACKUP_INTERVAL :
-                        TIMEOUT_FULL_BACKUP_INTERVAL;
+                        mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                        mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
 
                 if (DEBUG) {
                     Slog.d(TAG, "Calling doFullBackup() on " + mPackage.packageName);
@@ -180,6 +180,9 @@
         mQuota = quota;
         mOpToken = opToken;
         mTransportFlags = transportFlags;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public int preflightCheck() throws RemoteException {
diff --git a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
index 40b6967..bc7d9fc 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/FullBackupObbConnection.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.app.backup.IBackupManager;
 import android.content.ComponentName;
@@ -33,6 +32,8 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IObbBackupService;
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.utils.FullBackupUtils;
 
@@ -46,10 +47,14 @@
 
     private BackupManagerService backupManagerService;
     volatile IObbBackupService mService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public FullBackupObbConnection(BackupManagerService backupManagerService) {
         this.backupManagerService = backupManagerService;
         mService = null;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void establish() {
@@ -75,8 +80,10 @@
         try {
             pipes = ParcelFileDescriptor.createPipe();
             int token = backupManagerService.generateRandomIntegerToken();
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    token, TIMEOUT_FULL_BACKUP_INTERVAL, null, OP_TYPE_BACKUP_WAIT);
+                    token, fullBackupAgentTimeoutMillis, null, OP_TYPE_BACKUP_WAIT);
             mService.backupObbs(pkg.packageName, pipes[1], token,
                     backupManagerService.getBackupManagerBinder());
             FullBackupUtils.routeSocketDataToOutput(pipes[0], out);
diff --git a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
index 2c2dd85..a40afc3 100644
--- a/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
+++ b/services/backup/java/com/android/server/backup/fullbackup/PerformFullTransportBackupTask.java
@@ -22,7 +22,6 @@
 import static com.android.server.backup.BackupManagerService.OP_PENDING;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.annotation.Nullable;
 import android.app.IBackupAgent;
@@ -43,7 +42,9 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FullBackupJob;
 import com.android.server.backup.BackupManagerService;
@@ -146,6 +147,7 @@
     private volatile boolean mIsDoingBackup;
     private volatile boolean mCancelAll;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public PerformFullTransportBackupTask(BackupManagerService backupManagerService,
             TransportClient transportClient,
@@ -167,6 +169,9 @@
         mUserInitiated = userInitiated;
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
         mBackupRunnerOpToken = backupManagerService.generateRandomIntegerToken();
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         if (backupManagerService.isBackupOperationInProgress()) {
             if (DEBUG) {
@@ -698,9 +703,11 @@
         @Override
         public int preflightFullBackup(PackageInfo pkg, IBackupAgent agent) {
             int result;
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
                 backupManagerService.prepareOperationTimeout(
-                        mCurrentOpToken, TIMEOUT_FULL_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                        mCurrentOpToken, fullBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
                 backupManagerService.addBackupTrace("preflighting");
                 if (MORE_DEBUG) {
                     Slog.d(TAG, "Preflighting full payload of " + pkg.packageName);
@@ -713,7 +720,7 @@
                 // timeout had been produced.  In case of a real backstop timeout, mResult
                 // will still contain the value it was constructed with, AGENT_ERROR, which
                 // intentionaly falls into the "just report failure" code.
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
 
                 long totalSize = mResult.get();
                 // If preflight timed out, mResult will contain error code as int.
@@ -769,8 +776,10 @@
 
         @Override
         public long getExpectedSizeOrErrorCode() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 return mResult.get();
             } catch (InterruptedException e) {
                 return BackupTransport.NO_MORE_DATA;
@@ -863,8 +872,10 @@
         // If preflight succeeded, returns positive number - preflight size,
         // otherwise return negative error code.
         long getPreflightResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mPreflightLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mPreflightLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
@@ -879,8 +890,10 @@
         }
 
         int getBackupResultBlocking() {
+            long fullBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
             try {
-                mBackupLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+                mBackupLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
                 if (mIsCancelled) {
                     return BackupManager.ERROR_BACKUP_CANCELLED;
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 136fada..69f08ae 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -19,7 +19,6 @@
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 
 import android.app.backup.RestoreSet;
 import android.content.Intent;
@@ -33,7 +32,9 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
@@ -81,10 +82,14 @@
     public static final int MSG_OP_COMPLETE = 21;
 
     private final BackupManagerService backupManagerService;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public BackupHandler(BackupManagerService backupManagerService, Looper looper) {
         super(looper);
         this.backupManagerService = backupManagerService;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public void handleMessage(Message msg) {
@@ -322,7 +327,8 @@
 
                     // Done: reset the session timeout clock
                     removeMessages(MSG_RESTORE_SESSION_TIMEOUT);
-                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT, TIMEOUT_RESTORE_INTERVAL);
+                    sendEmptyMessageDelayed(MSG_RESTORE_SESSION_TIMEOUT,
+                            mAgentTimeoutParameters.getRestoreAgentTimeoutMillis());
 
                     params.listener.onFinished(callerLogString);
                 }
diff --git a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
index 11394e66..ac605b1 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformBackupTask.java
@@ -24,7 +24,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP;
 import static com.android.server.backup.BackupManagerService.OP_TYPE_BACKUP_WAIT;
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 
@@ -55,8 +54,10 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.DataChangedJournal;
 import com.android.server.backup.KeyValueBackupJob;
@@ -142,6 +143,7 @@
     private boolean mFinished;
     private final boolean mUserInitiated;
     private final boolean mNonIncremental;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     private volatile boolean mCancelAll;
 
@@ -162,6 +164,9 @@
         mPendingFullBackups = pendingFullBackups;
         mUserInitiated = userInitiated;
         mNonIncremental = nonIncremental;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         mStateDir = new File(backupManagerService.getBaseStateDir(), dirName);
         mCurrentOpToken = backupManagerService.generateRandomIntegerToken();
@@ -711,8 +716,10 @@
 
             // Initiate the target's backup pass
             backupManagerService.addBackupTrace("setting timeout");
+            long kvBackupAgentTimeoutMillis =
+                    mAgentTimeoutParameters.getKvBackupAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_BACKUP_INTERVAL, this, OP_TYPE_BACKUP_WAIT);
+                    mEphemeralOpToken, kvBackupAgentTimeoutMillis, this, OP_TYPE_BACKUP_WAIT);
             backupManagerService.addBackupTrace("calling agent doBackup()");
 
             agent.doBackup(
diff --git a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
index e4f3a9d..a8c7ce6 100644
--- a/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
+++ b/services/backup/java/com/android/server/backup/restore/AdbRestoreFinishedLatch.java
@@ -18,10 +18,11 @@
 
 import static com.android.server.backup.BackupManagerService.DEBUG;
 import static com.android.server.backup.BackupManagerService.MORE_DEBUG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
 
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 
@@ -37,18 +38,24 @@
     private BackupManagerService backupManagerService;
     final CountDownLatch mLatch;
     private final int mCurrentOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     public AdbRestoreFinishedLatch(BackupManagerService backupManagerService,
             int currentOpToken) {
         this.backupManagerService = backupManagerService;
         mLatch = new CountDownLatch(1);
         mCurrentOpToken = currentOpToken;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     void await() {
         boolean latched = false;
+        long fullBackupAgentTimeoutMillis =
+                mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
         try {
-            latched = mLatch.await(TIMEOUT_FULL_BACKUP_INTERVAL, TimeUnit.MILLISECONDS);
+            latched = mLatch.await(fullBackupAgentTimeoutMillis, TimeUnit.MILLISECONDS);
         } catch (InterruptedException e) {
             Slog.w(TAG, "Interrupted!");
         }
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index c1a1c1d..6bc7530 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.OP_TYPE_RESTORE_WAIT;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_SHARED_BACKUP_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -40,13 +37,17 @@
 import android.content.pm.Signature;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Slog;
 
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
-import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.fullbackup.FullBackupObbConnection;
 import com.android.server.backup.utils.BytesReadListener;
 import com.android.server.backup.utils.FullBackupRestoreObserverUtils;
@@ -56,8 +57,11 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 /**
  * Full restore engine, used by both adb restore and transport-based full restore.
@@ -121,6 +125,8 @@
 
     final int mEphemeralOpToken;
 
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
+
     public FullRestoreEngine(BackupManagerService backupManagerService,
             BackupRestoreTask monitorTask, IFullBackupRestoreObserver observer,
             IBackupManagerMonitor monitor, PackageInfo onlyPackage, boolean allowApks,
@@ -135,6 +141,9 @@
         mAllowObbs = allowObbs;
         mBuffer = new byte[32 * 1024];
         mBytes = 0;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
     }
 
     public IBackupAgent getAgent() {
@@ -320,12 +329,17 @@
                                             pkg, 0);
 
                             // If we haven't sent any data to this app yet, we probably
-                            // need to clear it first.  Check that.
+                            // need to clear it first. Check that.
                             if (!mClearedPackages.contains(pkg)) {
-                                // apps with their own backup agents are
-                                // responsible for coherently managing a full
-                                // restore.
-                                if (mTargetApp.backupAgentName == null) {
+                                // Apps with their own backup agents are responsible for coherently
+                                // managing a full restore.
+                                // In some rare cases they can't, especially in case of deferred
+                                // restore. In this case check whether this app should be forced to
+                                // clear up.
+                                // TODO: Fix this properly with manifest parameter.
+                                boolean forceClear = shouldForceClearAppDataOnFullRestore(
+                                        mTargetApp.packageName);
+                                if (mTargetApp.backupAgentName == null || forceClear) {
                                     if (DEBUG) {
                                         Slog.d(TAG,
                                                 "Clearing app data preparatory to full restore");
@@ -381,8 +395,8 @@
                         long toCopy = info.size;
                         final boolean isSharedStorage = pkg.equals(SHARED_BACKUP_AGENT_PACKAGE);
                         final long timeout = isSharedStorage ?
-                                TIMEOUT_SHARED_BACKUP_INTERVAL :
-                                TIMEOUT_RESTORE_INTERVAL;
+                                mAgentTimeoutParameters.getSharedBackupAgentTimeoutMillis() :
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(token,
                                     timeout,
@@ -623,6 +637,24 @@
         return true;
     }
 
+    /**
+     * Returns whether the package is in the list of the packages for which clear app data should
+     * be called despite the fact that they have backup agent.
+     *
+     * <p>The list is read from {@link Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE}.
+     */
+    private boolean shouldForceClearAppDataOnFullRestore(String packageName) {
+        String packageListString = Settings.Secure.getString(
+                mBackupManagerService.getContext().getContentResolver(),
+                Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE);
+        if (TextUtils.isEmpty(packageListString)) {
+            return false;
+        }
+
+        List<String> packages = Arrays.asList(packageListString.split(";"));
+        return packages.contains(packageName);
+    }
+
     void sendOnRestorePackage(String name) {
         if (mObserver != null) {
             try {
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 dacde0b..77163d3 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformAdbRestoreTask.java
@@ -16,8 +16,6 @@
 
 package com.android.server.backup.restore;
 
-import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
-import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_HEADER_MAGIC;
 import static com.android.server.backup.BackupManagerService.BACKUP_FILE_VERSION;
 import static com.android.server.backup.BackupManagerService.BACKUP_MANIFEST_FILENAME;
@@ -28,8 +26,8 @@
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.SHARED_BACKUP_AGENT_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_FULL_BACKUP_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_CURRENT;
+import static com.android.server.backup.BackupPasswordManager.PBKDF_FALLBACK;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 
 import android.app.ApplicationThreadConstants;
@@ -48,7 +46,9 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.FileMetadata;
 import com.android.server.backup.KeyValueAdbRestoreEngine;
@@ -101,6 +101,7 @@
     private byte[] mWidgetData = null;
 
     private long mBytes;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // Runner that can be placed on a separate thread to do in-process invocation
     // of the "restore finished" API asynchronously.  Used by adb restore.
@@ -155,6 +156,9 @@
         mAgentPackage = null;
         mTargetApp = null;
         mObbConnection = new FullBackupObbConnection(backupManagerService);
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         // Which packages we've already wiped data on.  We prepopulate this
         // with a whitelist of packages known to be unclearable.
@@ -643,9 +647,11 @@
                     if (okay) {
                         boolean agentSuccess = true;
                         long toCopy = info.size;
+                        long restoreAgentTimeoutMillis =
+                                mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
                         try {
                             mBackupManagerService.prepareOperationTimeout(
-                                    token, TIMEOUT_RESTORE_INTERVAL, null, OP_TYPE_RESTORE_WAIT);
+                                    token, restoreAgentTimeoutMillis, null, OP_TYPE_RESTORE_WAIT);
 
                             if (FullBackup.OBB_TREE_TOKEN.equals(info.domain)) {
                                 if (DEBUG) {
@@ -820,10 +826,12 @@
                 // In the adb restore case, we do restore-finished here
                 if (doRestoreFinished) {
                     final int token = mBackupManagerService.generateRandomIntegerToken();
+                    long fullBackupAgentTimeoutMillis =
+                            mAgentTimeoutParameters.getFullBackupAgentTimeoutMillis();
                     final AdbRestoreFinishedLatch latch = new AdbRestoreFinishedLatch(
                             mBackupManagerService, token);
                     mBackupManagerService.prepareOperationTimeout(
-                            token, TIMEOUT_FULL_BACKUP_INTERVAL, latch, OP_TYPE_RESTORE_WAIT);
+                            token, fullBackupAgentTimeoutMillis, latch, OP_TYPE_RESTORE_WAIT);
                     if (mTargetApp.processName.equals("system")) {
                         if (MORE_DEBUG) {
                             Slog.d(TAG, "system agent - restoreFinished on thread");
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 4b467e5..12d72d8 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -23,9 +23,6 @@
 import static com.android.server.backup.BackupManagerService.PACKAGE_MANAGER_SENTINEL;
 import static com.android.server.backup.BackupManagerService.SETTINGS_PACKAGE;
 import static com.android.server.backup.BackupManagerService.TAG;
-import static com.android.server.backup.BackupManagerService
-        .TIMEOUT_RESTORE_FINISHED_INTERVAL;
-import static com.android.server.backup.BackupManagerService.TIMEOUT_RESTORE_INTERVAL;
 import static com.android.server.backup.internal.BackupHandler.MSG_BACKUP_RESTORE_STEP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
@@ -56,9 +53,11 @@
 import android.util.Slog;
 
 import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.Preconditions;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.PackageManagerBackupAgent;
@@ -160,6 +159,7 @@
     ParcelFileDescriptor mNewState;
 
     private final int mEphemeralOpToken;
+    private final BackupAgentTimeoutParameters mAgentTimeoutParameters;
 
     // This task can assume that the wakelock is properly held for it and doesn't have to worry
     // about releasing it.
@@ -190,6 +190,9 @@
         mFinished = false;
         mDidLaunch = false;
         mListener = listener;
+        mAgentTimeoutParameters = Preconditions.checkNotNull(
+                backupManagerService.getAgentTimeoutParameters(),
+                "Timeout parameters cannot be null");
 
         if (targetPackage != null) {
             // Single package restore
@@ -760,8 +763,9 @@
             // Kick off the restore, checking for hung agents.  The timeout or
             // the operationComplete() callback will schedule the next step,
             // so we do not do that here.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.prepareOperationTimeout(
-                    mEphemeralOpToken, TIMEOUT_RESTORE_INTERVAL, this, OP_TYPE_RESTORE_WAIT);
+                    mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             mAgent.doRestore(mBackupData, appVersionCode, mNewState,
                     mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
         } catch (Exception e) {
@@ -813,9 +817,11 @@
             Slog.d(TAG, "restoreFinished packageName=" + mCurrentPackage.packageName);
         }
         try {
+            long restoreAgentFinishedTimeoutMillis =
+                    mAgentTimeoutParameters.getRestoreAgentFinishedTimeoutMillis();
             backupManagerService
                     .prepareOperationTimeout(mEphemeralOpToken,
-                            TIMEOUT_RESTORE_FINISHED_INTERVAL, this,
+                            restoreAgentFinishedTimeoutMillis, this,
                             OP_TYPE_RESTORE_WAIT);
             mAgent.doRestoreFinished(mEphemeralOpToken,
                     backupManagerService.getBackupManagerBinder());
@@ -1109,9 +1115,10 @@
         } else {
             // We were invoked via an active restore session, not by the Package
             // Manager, so start up the session timeout again.
+            long restoreAgentTimeoutMillis = mAgentTimeoutParameters.getRestoreAgentTimeoutMillis();
             backupManagerService.getBackupHandler().sendEmptyMessageDelayed(
                     MSG_RESTORE_SESSION_TIMEOUT,
-                    TIMEOUT_RESTORE_INTERVAL);
+                    restoreAgentTimeoutMillis);
         }
 
         // Kick off any work that may be needed regarding app widget restores
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index f6ff359..40f9476 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -266,9 +266,9 @@
         @GuardedBy("LOCK")
         private boolean mValue;
 
-        public DebugFlag(String key) {
+        public DebugFlag(String key, boolean defaultValue) {
             mKey = key;
-            refresh();
+            mValue = SystemProperties.getBoolean(key, defaultValue);
         }
 
         void refresh() {
@@ -290,7 +290,7 @@
      */
     private static final class DebugFlags {
         static final DebugFlag FLAG_OPTIMIZE_START_INPUT =
-                new DebugFlag("debug.optimize_startinput");
+                new DebugFlag("debug.optimize_startinput", false);
     }
 
 
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index d09a161..06c1056 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -931,7 +931,7 @@
             return mPort;
         }
 
-        public FileDescriptor getSocket() {
+        public FileDescriptor getFileDescriptor() {
             return mSocket;
         }
 
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 539c001..83fe976 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1753,7 +1753,8 @@
         long token = Binder.clearCallingIdentity();
         try {
             return LocationAccessPolicy.canAccessCellLocation(mContext,
-                    r.callingPackage, r.callerUid, r.callerPid);
+                    r.callingPackage, r.callerUid, r.callerPid,
+                    /*throwOnDeniedPermission*/ false);
         } finally {
             Binder.restoreCallingIdentity(token);
         }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5131a93..f756afb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -820,7 +820,7 @@
 
     public boolean canShowErrorDialogs() {
         return mShowDialogs && !mSleeping && !mShuttingDown
-                && !mKeyguardController.isKeyguardShowing(DEFAULT_DISPLAY)
+                && !mKeyguardController.isKeyguardOrAodShowing(DEFAULT_DISPLAY)
                 && !mUserController.hasUserRestriction(UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS,
                         mUserController.getCurrentUserId())
                 && !(UserManager.isDeviceInDemoMode(mContext)
@@ -1262,8 +1262,12 @@
         }
 
         public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
-            return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
-                    ContentProvider.getUriWithoutUserId(uri), false);
+            if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
+                return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
+                        ContentProvider.getUriWithoutUserId(uri), false);
+            } else {
+                return new GrantUri(defaultSourceUserHandle, uri, false);
+            }
         }
     }
 
@@ -2869,13 +2873,15 @@
     }
 
     /**
-     * Encapsulates the globla setting "hidden_api_blacklist_exemptions", including tracking the
+     * Encapsulates the global setting "hidden_api_blacklist_exemptions", including tracking the
      * latest value via a content observer.
      */
     static class HiddenApiBlacklist extends ContentObserver {
 
         private final Context mContext;
         private boolean mBlacklistDisabled;
+        private String mExemptionsStr;
+        private List<String> mExemptions = Collections.emptyList();
 
         public HiddenApiBlacklist(Handler handler, Context context) {
             super(handler);
@@ -2891,8 +2897,22 @@
         }
 
         private void update() {
-            mBlacklistDisabled = "*".equals(Settings.Global.getString(mContext.getContentResolver(),
-                    Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS));
+            String exemptions = Settings.Global.getString(mContext.getContentResolver(),
+                    Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS);
+            if (!TextUtils.equals(exemptions, mExemptionsStr)) {
+                mExemptionsStr = exemptions;
+                if ("*".equals(exemptions)) {
+                    mBlacklistDisabled = true;
+                    mExemptions = Collections.emptyList();
+                } else {
+                    mBlacklistDisabled = false;
+                    mExemptions = TextUtils.isEmpty(exemptions)
+                            ? Collections.emptyList()
+                            : Arrays.asList(exemptions.split(","));
+                }
+                zygoteProcess.setApiBlacklistExemptions(mExemptions);
+            }
+
         }
 
         boolean isDisabled() {
@@ -4473,8 +4493,8 @@
         StatsLog.write(StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED,
             component.app.uid, component.realActivity.getPackageName(),
             component.realActivity.getShortClassName(), resumed ?
-                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_FOREGROUND :
-                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__ACTIVITY__MOVE_TO_BACKGROUND);
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__FOREGROUND :
+                        StatsLog.ACTIVITY_FOREGROUND_STATE_CHANGED__STATE__BACKGROUND);
         if (resumed) {
             if (mUsageStatsService != null) {
                 mUsageStatsService.reportEvent(component.realActivity, component.userId,
@@ -10601,8 +10621,8 @@
                         intent.addFlags(Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS);
                     }
                 }
-                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp, 0,
-                        UserHandle.getUserId(callingUid));
+                final ActivityInfo ainfo = AppGlobals.getPackageManager().getActivityInfo(comp,
+                        STOCK_PM_FLAGS, UserHandle.getUserId(callingUid));
                 if (ainfo.applicationInfo.uid != callingUid) {
                     throw new SecurityException(
                             "Can't add task for another application: target uid="
@@ -13046,6 +13066,22 @@
         return mSleeping;
     }
 
+    void reportGlobalUsageEventLocked(int event) {
+        mUsageStatsService.reportEvent("android", mUserController.getCurrentUserId(), event);
+        int[] profiles = mUserController.getCurrentProfileIds();
+        if (profiles != null) {
+            for (int i = profiles.length - 1; i >= 0; i--) {
+                mUsageStatsService.reportEvent((String)null, profiles[i], event);
+            }
+        }
+    }
+
+    void reportCurWakefulnessUsageEventLocked() {
+        reportGlobalUsageEventLocked(mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE
+                ? UsageEvents.Event.SCREEN_INTERACTIVE
+                : UsageEvents.Event.SCREEN_NON_INTERACTIVE);
+    }
+
     void onWakefulnessChanged(int wakefulness) {
         synchronized(this) {
             boolean wasAwake = mWakefulness == PowerManagerInternal.WAKEFULNESS_AWAKE;
@@ -13055,6 +13091,7 @@
             if (wasAwake != isAwake) {
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
+                reportCurWakefulnessUsageEventLocked();
                 mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
                         .sendToTarget();
             }
@@ -13142,10 +13179,6 @@
                     + android.Manifest.permission.SHUTDOWN);
         }
 
-        // TODO: Where should the corresponding '1' (start) write go?
-        StatsLog.write(StatsLog.DEVICE_ON_STATUS_CHANGED,
-                StatsLog.DEVICE_ON_STATUS_CHANGED__STATE__OFF);
-
         boolean timedout = false;
 
         synchronized(this) {
@@ -13202,7 +13235,8 @@
     }
 
     @Override
-    public void setLockScreenShown(boolean showing, int secondaryDisplayShowing) {
+    public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing,
+            int secondaryDisplayShowing) {
         if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires permission "
@@ -13212,13 +13246,14 @@
         synchronized(this) {
             long ident = Binder.clearCallingIdentity();
             try {
-                mKeyguardController.setKeyguardShown(showing, secondaryDisplayShowing);
+                mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing,
+                        secondaryDisplayShowing);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
 
-        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, keyguardShowing ? 1 : 0, 0)
                 .sendToTarget();
     }
 
@@ -14029,6 +14064,18 @@
     }
 
     @Override
+    public boolean isUidActive(int uid, String callingPackage) {
+        if (!hasUsageStatsPermission(callingPackage)) {
+            enforceCallingPermission(android.Manifest.permission.PACKAGE_USAGE_STATS,
+                    "getPackageProcessState");
+        }
+        synchronized (this) {
+            final UidRecord uidRecord = mActiveUids.get(uid);
+            return uidRecord != null && !uidRecord.setIdle;
+        }
+    }
+
+    @Override
     public boolean convertFromTranslucent(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -14779,7 +14826,7 @@
                     .setPackage("android")
                     .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             broadcastIntent(null, intent, null, null, 0, null, null, null,
-                    OP_NONE, null, true, false, UserHandle.USER_ALL);
+                    OP_NONE, null, false, false, UserHandle.USER_ALL);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -15146,6 +15193,12 @@
                 crashInfo.throwFileName,
                 crashInfo.throwLineNumber);
 
+        StatsLog.write(StatsLog.APP_CRASH_OCCURRED,
+                Binder.getCallingUid(),
+                eventType,
+                processName,
+                Binder.getCallingPid());
+
         addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
 
         mAppErrors.crashApplication(r, crashInfo);
@@ -15316,6 +15369,9 @@
         EventLog.writeEvent(EventLogTags.AM_WTF, UserHandle.getUserId(callingUid), callingPid,
                 processName, r == null ? -1 : r.info.flags, tag, crashInfo.exceptionMessage);
 
+        StatsLog.write(StatsLog.WTF_OCCURRED, callingUid, tag, processName,
+                callingPid);
+
         addErrorToDropBox("wtf", r, processName, null, null, tag, null, null, crashInfo);
 
         return r;
@@ -15437,19 +15493,6 @@
         final String dropboxTag = processClass(process) + "_" + eventType;
         if (dbox == null || !dbox.isTagEnabled(dropboxTag)) return;
 
-        // Log to StatsLog before the rate-limiting.
-        // The logging below is adapated from appendDropboxProcessHeaders.
-        StatsLog.write(StatsLog.DROPBOX_ERROR_CHANGED,
-                process != null ? process.uid : -1,
-                dropboxTag,
-                processName,
-                process != null ? process.pid : -1,
-                (process != null && process.info != null) ?
-                        (process.info.isInstantApp() ? 1 : 0) : -1,
-                activity != null ? activity.shortComponentName : null,
-                activity != null ? activity.packageName : null,
-                process != null ? (process.isInterestingToUserLocked() ? 1 : 0) : -1);
-
         // Rate-limit how often we're willing to do the heavy lifting below to
         // collect and record logs; currently 5 logs per 10 second period.
         final long now = SystemClock.elapsedRealtime();
@@ -19729,6 +19772,7 @@
             catPw.flush();
         }
         dropBuilder.append(catSw.toString());
+        StatsLog.write(StatsLog.LOW_MEM_REPORTED);
         addErrorToDropBox("lowmem", null, "system_server", null,
                 null, tag.toString(), dropBuilder.toString(), null, null);
         //Slog.i(TAG, "Sent to dropbox:");
@@ -21190,7 +21234,7 @@
                         ApplicationInfo aInfo = null;
                         try {
                             aInfo = AppGlobals.getPackageManager()
-                                    .getApplicationInfo(ssp, 0 /*flags*/, userId);
+                                    .getApplicationInfo(ssp, STOCK_PM_FLAGS, userId);
                         } catch (RemoteException ignore) {}
                         if (aInfo == null) {
                             Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:"
@@ -21215,7 +21259,7 @@
 
                         try {
                             ApplicationInfo ai = AppGlobals.getPackageManager().
-                                    getApplicationInfo(ssp, 0, 0);
+                                    getApplicationInfo(ssp, STOCK_PM_FLAGS, 0);
                             mBatteryStatsService.notePackageInstalled(ssp,
                                     ai != null ? ai.versionCode : 0);
                         } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 724dd3f..47d0423 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -489,7 +489,7 @@
         builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_CANCEL_CHANGED,
+                StatsLog.APP_START_CANCELED,
                 info.launchedActivity.appInfo.uid,
                 info.launchedActivity.packageName,
                 convertAppStartTransitionType(type),
@@ -561,7 +561,7 @@
                 packageOptimizationInfo.getCompilationFilter());
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_CHANGED,
+                StatsLog.APP_START_OCCURRED,
                 info.applicationInfo.uid,
                 info.packageName,
                 convertAppStartTransitionType(info.type),
@@ -582,15 +582,15 @@
 
     private int convertAppStartTransitionType(int tronType) {
         if (tronType == TYPE_TRANSITION_COLD_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__COLD;
+            return StatsLog.APP_START_OCCURRED__TYPE__COLD;
         }
         if (tronType == TYPE_TRANSITION_WARM_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__WARM;
+            return StatsLog.APP_START_OCCURRED__TYPE__WARM;
         }
         if (tronType == TYPE_TRANSITION_HOT_LAUNCH) {
-            return StatsLog.APP_START_CHANGED__TYPE__HOT;
+            return StatsLog.APP_START_OCCURRED__TYPE__HOT;
         }
-        return StatsLog.APP_START_CHANGED__TYPE__APP_START_TRANSITION_TYPE_UNKNOWN;
+        return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN;
      }
 
     void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) {
@@ -611,12 +611,12 @@
                 info.currentTransitionProcessRunning ? 1 : 0);
         mMetricsLogger.write(builder);
         StatsLog.write(
-                StatsLog.APP_START_FULLY_DRAWN_CHANGED,
+                StatsLog.APP_START_FULLY_DRAWN,
                 info.launchedActivity.appInfo.uid,
                 info.launchedActivity.packageName,
                 restoredFromBundle
-                        ? StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITH_BUNDLE
-                        : StatsLog.APP_START_FULLY_DRAWN_CHANGED__TYPE__WITHOUT_BUNDLE,
+                        ? StatsLog.APP_START_FULLY_DRAWN__TYPE__WITH_BUNDLE
+                        : StatsLog.APP_START_FULLY_DRAWN__TYPE__WITHOUT_BUNDLE,
                 info.launchedActivity.info.name,
                 info.currentTransitionProcessRunning,
                 startupTimeMs);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ccc17a3..1af4114 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -236,7 +236,8 @@
     final IApplicationToken.Stub appToken; // window manager token
     AppWindowContainerController mWindowContainerController;
     final ActivityInfo info; // all about me
-    final ApplicationInfo appInfo; // information about activity's app
+    // TODO: This is duplicated state already contained in info.applicationInfo - remove
+    ApplicationInfo appInfo; // information about activity's app
     final int launchedFromPid; // always the pid who started the activity.
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
@@ -605,6 +606,11 @@
         }
     }
 
+    void updateApplicationInfo(ApplicationInfo aInfo) {
+        appInfo = aInfo;
+        info.applicationInfo = aInfo;
+    }
+
     private boolean crossesHorizontalSizeThreshold(int firstDp, int secondDp) {
         return crossesSizeThreshold(mHorizontalSizeConfigurations, firstDp, secondDp);
     }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index aa462ec..00ebcbd 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1324,7 +1324,7 @@
                 final ActivityRecord ar = activities.get(activityNdx);
 
                 if ((userId == ar.userId) && packageName.equals(ar.packageName)) {
-                    ar.info.applicationInfo = aInfo;
+                    ar.updateApplicationInfo(aInfo);
                 }
             }
         }
@@ -2019,8 +2019,9 @@
      * @return true if {@param r} is visible taken Keyguard state into account, false otherwise
      */
     boolean checkKeyguardVisibility(ActivityRecord r, boolean shouldBeVisible, boolean isTop) {
-        final boolean keyguardShowing = mStackSupervisor.getKeyguardController().isKeyguardShowing(
-                mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
+        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
+        final boolean keyguardOrAodShowing = mStackSupervisor.getKeyguardController()
+                .isKeyguardOrAodShowing(displayId);
         final boolean keyguardLocked = mStackSupervisor.getKeyguardController().isKeyguardLocked();
         final boolean showWhenLocked = r.canShowWhenLocked();
         final boolean dismissKeyguard = r.hasDismissKeyguardWindows();
@@ -2041,10 +2042,9 @@
                 return true;
             }
         }
-        if (keyguardShowing) {
-
+        if (keyguardOrAodShowing) {
             // If keyguard is showing, nothing is visible, except if we are able to dismiss Keyguard
-            // right away.
+            // right away and AOD isn't visible.
             return shouldBeVisible && mStackSupervisor.getKeyguardController()
                     .canShowActivityWhileKeyguardShowing(r, dismissKeyguard);
         } else if (keyguardLocked) {
@@ -3987,7 +3987,8 @@
             } else {
                 try {
                     ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
-                            destIntent.getComponent(), 0, srec.userId);
+                            destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
+                            srec.userId);
                     // TODO(b/64750076): Check if calling pid should really be -1.
                     final int res = mService.getActivityStartController()
                             .obtainStarter(destIntent, "navigateUpTo")
@@ -5139,8 +5140,9 @@
                 mService, taskId, info, intent, voiceSession, voiceInteractor);
         // add the task to stack first, mTaskPositioner might need the stack association
         addTask(task, toTop, "createTaskRecord");
+        final int displayId = mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY;
         final boolean isLockscreenShown = mService.mStackSupervisor.getKeyguardController()
-                .isKeyguardShowing(mDisplayId != INVALID_DISPLAY ? mDisplayId : DEFAULT_DISPLAY);
+                .isKeyguardOrAodShowing(displayId);
         if (!mStackSupervisor.getLaunchParamsController()
                 .layoutTask(task, info.windowLayout, activity, source, options)
                 && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 265e4fa..d5dfdcf 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3382,8 +3382,8 @@
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
                     stack.awakeFromSleepingLocked();
-                    if (isFocusedStack(stack)
-                            && !getKeyguardController().isKeyguardShowing(display.mDisplayId)) {
+                    if (isFocusedStack(stack) && !getKeyguardController().isKeyguardOrAodShowing(
+                            display.mDisplayId)) {
                         // If the keyguard is unlocked - resume immediately.
                         // It is possible that the display will not be awake at the time we
                         // process the keyguard going away, which can happen before the sleep token
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 868f90d..fb78838 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -198,9 +198,10 @@
 
             // See if we should be showing the platform update setup UI.
             final Intent intent = new Intent(Intent.ACTION_UPGRADE_SETUP);
-            final List<ResolveInfo> ris = mService.mContext.getPackageManager()
-                    .queryIntentActivities(intent,
-                            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA);
+            final List<ResolveInfo> ris =
+                    mService.mContext.getPackageManager().queryIntentActivities(intent,
+                            PackageManager.MATCH_SYSTEM_ONLY | PackageManager.GET_META_DATA
+                                    | ActivityManagerService.STOCK_PM_FLAGS);
             if (!ris.isEmpty()) {
                 final ResolveInfo ri = ris.get(0);
                 String vers = ri.activityInfo.metaData != null
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index b2872e4..7ee20fa2 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -47,6 +47,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.StatsLog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
@@ -1039,6 +1040,8 @@
             Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
         }
 
+        StatsLog.write(StatsLog.ANR_OCCURRED, app.uid, app.processName,
+                activity == null ? "unknown": activity.shortComponentName, annotation);
         mService.addErrorToDropBox("anr", app, app.processName, activity, parent, annotation,
                 cpuInfo, tracesFile, null);
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3c49ece..8ecd93e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -17,10 +17,15 @@
 package com.android.server.am;
 
 import android.app.ActivityManager;
+import android.app.job.JobProtoEnums;
 import android.bluetooth.BluetoothActivityEnergyInfo;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.hardware.usb.UsbManager;
 import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.PowerManager.ServiceType;
 import android.os.PowerSaveState;
@@ -34,6 +39,7 @@
 import android.os.ParcelFormatException;
 import android.os.PowerManagerInternal;
 import android.os.Process;
+import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -70,7 +76,6 @@
 import java.nio.charset.CharsetDecoder;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.concurrent.ExecutionException;
@@ -332,7 +337,7 @@
         synchronized (mStats) {
             mStats.noteProcessStartLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_STARTED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__STARTED);
         }
     }
 
@@ -340,15 +345,13 @@
         synchronized (mStats) {
             mStats.noteProcessCrashLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_CRASHED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__CRASHED);
         }
     }
 
     void noteProcessAnr(String name, int uid) {
         synchronized (mStats) {
             mStats.noteProcessAnrLocked(name, uid);
-            StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_ANRED);
         }
     }
 
@@ -356,7 +359,7 @@
         synchronized (mStats) {
             mStats.noteProcessFinishLocked(name, uid);
             StatsLog.write(StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED, uid, name,
-                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__EVENT__PROCESS_FINISHED);
+                    StatsLog.PROCESS_LIFE_CYCLE_STATE_CHANGED__STATE__FINISHED);
         }
     }
 
@@ -438,6 +441,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteSyncStartLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, uid, null, name,
+                    StatsLog.SYNC_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -445,6 +450,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteSyncFinishLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SYNC_STATE_CHANGED, uid, null, name,
+                    StatsLog.SYNC_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -452,6 +459,9 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobStartLocked(name, uid);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
+                    name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__STARTED,
+                    JobProtoEnums.STOP_REASON_UNKNOWN);
         }
     }
 
@@ -459,6 +469,9 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteJobFinishLocked(name, uid, stopReason);
+            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED, uid, null,
+                    name, StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
+                    stopReason);
         }
     }
 
@@ -573,6 +586,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStartSensorLocked(uid, sensor);
+            StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, uid, null, sensor,
+                    StatsLog.SENSOR_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -580,6 +595,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStopSensorLocked(uid, sensor);
+            StatsLog.write_non_chained(StatsLog.SENSOR_STATE_CHANGED, uid, null,
+                    sensor, StatsLog.SENSOR_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -684,6 +701,13 @@
         }
     }
 
+    public void noteUsbConnectionState(boolean connected) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteUsbConnectionStateLocked(connected);
+        }
+    }
+
     public void notePhoneSignalStrength(SignalStrength signalStrength) {
         enforceCallingPermission();
         synchronized (mStats) {
@@ -724,6 +748,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -731,6 +757,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteAudioOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, uid, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -738,6 +766,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid, null,
+                    StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -745,6 +775,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteVideoOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, uid,
+                    null, StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -752,6 +784,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetAudioLocked();
+            StatsLog.write_non_chained(StatsLog.AUDIO_STATE_CHANGED, -1, null,
+                    StatsLog.AUDIO_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -759,6 +793,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetVideoLocked();
+            StatsLog.write_non_chained(StatsLog.MEDIA_CODEC_STATE_CHANGED, -1, null,
+                    StatsLog.MEDIA_CODEC_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -766,6 +802,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteFlashlightOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, uid, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__ON);
         }
     }
 
@@ -773,6 +811,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteFlashlightOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, uid, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -781,6 +821,8 @@
         if (DBG) Slog.d(TAG, "begin noteStartCamera");
         synchronized (mStats) {
             mStats.noteCameraOnLocked(uid);
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, uid, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__ON);
         }
         if (DBG) Slog.d(TAG, "end noteStartCamera");
     }
@@ -789,6 +831,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteCameraOffLocked(uid);
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, uid, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__OFF);
         }
     }
 
@@ -796,6 +840,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetCameraLocked();
+            StatsLog.write_non_chained(StatsLog.CAMERA_STATE_CHANGED, -1, null,
+                    StatsLog.CAMERA_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -803,6 +849,8 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteResetFlashlightLocked();
+            StatsLog.write_non_chained(StatsLog.FLASHLIGHT_STATE_CHANGED, -1, null,
+                    StatsLog.FLASHLIGHT_STATE_CHANGED__STATE__RESET);
         }
     }
 
@@ -1116,6 +1164,35 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
 
+    public final static class UsbConnectionReceiver extends BroadcastReceiver {
+        private static final String TAG = UsbConnectionReceiver.class.getSimpleName();
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
+                final Intent usbState = context.registerReceiver(null, new IntentFilter(UsbManager.ACTION_USB_STATE));
+                if (usbState != null) {
+                    handleUsbState(usbState);
+                }
+            } else if (UsbManager.ACTION_USB_STATE.equals(action)) {
+                handleUsbState(intent);
+            }
+        }
+        private void handleUsbState(Intent intent) {
+            IBatteryStats bs = getService();
+            if (bs == null) {
+                Slog.w(TAG, "Could not access batterystats");
+                return;
+            }
+            boolean connected = intent.getExtras().getBoolean(UsbManager.USB_CONNECTED);
+            try {
+                bs.noteUsbConnectionState(connected);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not access batterystats: ", e);
+            }
+        }
+    }
+
     final class WakeupReasonThread extends Thread {
         private static final int MAX_REASON_SIZE = 512;
         private CharsetDecoder mDecoder;
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index b67dd0d..1b7f75b 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -62,6 +62,7 @@
     private final ActivityStackSupervisor mStackSupervisor;
     private WindowManagerService mWindowManager;
     private boolean mKeyguardShowing;
+    private boolean mAodShowing;
     private boolean mKeyguardGoingAway;
     private boolean mOccluded;
     private boolean mDismissalRequested;
@@ -82,6 +83,15 @@
     }
 
     /**
+     * @return true if either Keyguard or AOD are showing, not going away, and not being occluded
+     *         on the given display, false otherwise
+     */
+    boolean isKeyguardOrAodShowing(int displayId) {
+        return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway &&
+                (displayId == DEFAULT_DISPLAY ? !mOccluded : displayId == mSecondaryDisplayShowing);
+    }
+
+    /**
      * @return true if Keyguard is showing, not going away, and not being occluded on the given
      *         display, false otherwise
      */
@@ -108,17 +118,19 @@
     /**
      * Update the Keyguard showing state.
      */
-    void setKeyguardShown(boolean showing, int secondaryDisplayShowing) {
-        boolean showingChanged = showing != mKeyguardShowing;
+    void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
+            int secondaryDisplayShowing) {
+        boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
         if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
             return;
         }
-        mKeyguardShowing = showing;
+        mKeyguardShowing = keyguardShowing;
+        mAodShowing = aodShowing;
         mSecondaryDisplayShowing = secondaryDisplayShowing;
         if (showingChanged) {
             dismissDockedStackIfNeeded();
             setKeyguardGoingAway(false);
-            if (showing) {
+            if (keyguardShowing) {
                 mDismissalRequested = false;
             }
         }
@@ -230,8 +242,8 @@
         // Allow to show it when we are about to dismiss Keyguard. This isn't allowed if r is
         // already the dismissing activity, in which case we don't allow it to repeatedly dismiss
         // Keyguard.
-        return dismissKeyguard && canDismissKeyguard() &&
-                (mDismissalRequested || r != mDismissingKeyguardActivity);
+        return dismissKeyguard && canDismissKeyguard() && !mAodShowing
+                && (mDismissalRequested || r != mDismissingKeyguardActivity);
     }
 
     /**
@@ -369,9 +381,9 @@
     }
 
     private void updateKeyguardSleepToken() {
-        if (mSleepToken == null && isKeyguardShowing(DEFAULT_DISPLAY)) {
+        if (mSleepToken == null && isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
             mSleepToken = mService.acquireSleepToken("Keyguard", DEFAULT_DISPLAY);
-        } else if (mSleepToken != null && !isKeyguardShowing(DEFAULT_DISPLAY)) {
+        } else if (mSleepToken != null && !isKeyguardOrAodShowing(DEFAULT_DISPLAY)) {
             mSleepToken.release();
             mSleepToken = null;
         }
@@ -380,6 +392,7 @@
     void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "KeyguardController:");
         pw.println(prefix + "  mKeyguardShowing=" + mKeyguardShowing);
+        pw.println(prefix + "  mAodShowing=" + mAodShowing);
         pw.println(prefix + "  mKeyguardGoingAway=" + mKeyguardGoingAway);
         pw.println(prefix + "  mOccluded=" + mOccluded);
         pw.println(prefix + "  mDismissingKeyguardActivity=" + mDismissingKeyguardActivity);
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 106b37f..bef650b 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -285,6 +285,16 @@
         return false;
     }
 
+    /**
+     * @return the root task of the lock task.
+     */
+    TaskRecord getRootTask() {
+        if (mLockTaskModeTasks.isEmpty()) {
+            return null;
+        }
+        return mLockTaskModeTasks.get(0);
+    }
+
     private boolean isLockTaskModeViolationInternal(TaskRecord task, boolean isNewClearTask) {
         // TODO: Double check what's going on here. If the task is already in lock task mode, it's
         // likely whitelisted, so will return false below.
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index efd8153..1d305fb 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -600,7 +600,8 @@
                         // activities that are fully runnable based on
                         // current system state.
                         ai = pm.getActivityInfo(task.realActivity,
-                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING
+                                        | ActivityManagerService.STOCK_PM_FLAGS, userId);
                     } catch (RemoteException e) {
                         // Will never happen.
                         continue;
@@ -1154,6 +1155,11 @@
                 }
         }
 
+        // If we're in lock task mode, ignore the root task
+        if (task == mService.mLockTaskController.getRootTask()) {
+            return false;
+        }
+
         return true;
     }
 
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index da56ffd..9df321c 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -42,18 +42,13 @@
 class RecentsAnimation implements RecentsAnimationCallbacks {
     private static final String TAG = RecentsAnimation.class.getSimpleName();
 
-    private static final int RECENTS_ANIMATION_TIMEOUT = 10 * 1000;
-
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mStackSupervisor;
     private final ActivityStartController mActivityStartController;
     private final WindowManagerService mWindowManager;
     private final UserController mUserController;
-    private final Handler mHandler;
     private final int mCallingPid;
 
-    private final Runnable mCancelAnimationRunnable;
-
     // The stack to restore the home stack behind when the animation is finished
     private ActivityStack mRestoreHomeBehindStack;
 
@@ -63,16 +58,9 @@
         mService = am;
         mStackSupervisor = stackSupervisor;
         mActivityStartController = activityStartController;
-        mHandler = new Handler(mStackSupervisor.mLooper);
         mWindowManager = wm;
         mUserController = userController;
         mCallingPid = callingPid;
-
-        mCancelAnimationRunnable = () -> {
-            // The caller has not finished the animation in a predefined amount of time, so
-            // force-cancel the animation
-            mWindowManager.cancelRecentsAnimation();
-        };
     }
 
     void startRecentsActivity(Intent intent, IRecentsAnimationRunner recentsAnimationRunner,
@@ -133,10 +121,6 @@
             // duration of the gesture that is driven by the recents component
             homeActivity.mLaunchTaskBehind = true;
 
-            // Post a timeout for the animation. This needs to happen before initializing the
-            // recents animation on the WM side since we may decide to cancel the animation there
-            mHandler.postDelayed(mCancelAnimationRunnable, RECENTS_ANIMATION_TIMEOUT);
-
             // Fetch all the surface controls and pass them to the client to get the animation
             // started
             mWindowManager.cancelRecentsAnimation();
@@ -157,7 +141,6 @@
 
     @Override
     public void onAnimationFinished(boolean moveHomeToTop) {
-        mHandler.removeCallbacks(mCancelAnimationRunnable);
         synchronized (mService) {
             if (mWindowManager.getRecentsAnimationController() == null) return;
 
@@ -168,7 +151,7 @@
                         "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
                 mWindowManager.deferSurfaceLayout();
                 try {
-                    mWindowManager.cleanupRecentsAnimation();
+                    mWindowManager.cleanupRecentsAnimation(moveHomeToTop);
 
                     // Move the home stack to the front
                     final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0d125e0..f710690 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -47,6 +47,7 @@
 import android.app.IStopUserCallback;
 import android.app.IUserSwitchObserver;
 import android.app.KeyguardManager;
+import android.app.usage.UsageEvents;
 import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
@@ -959,6 +960,8 @@
                 mInjector.getUserManagerInternal().setUserState(userId, uss.state);
             }
             if (foreground) {
+                // Make sure the old user is no longer considering the display to be on.
+                mInjector.reportGlobalUsageEventLocked(UsageEvents.Event.SCREEN_NON_INTERACTIVE);
                 synchronized (mLock) {
                     mCurrentUserId = userId;
                     mTargetUserId = UserHandle.USER_NULL; // reset, mCurrentUserId has caught up
@@ -966,6 +969,7 @@
                 mInjector.updateUserConfiguration();
                 updateCurrentProfileIds();
                 mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
+                mInjector.reportCurWakefulnessUsageEvent();
                 // Once the internal notion of the active user has switched, we lock the device
                 // with the option to show the user switcher on the keyguard.
                 if (mUserSwitchUiEnabled) {
@@ -2183,6 +2187,18 @@
             d.show();
         }
 
+        void reportGlobalUsageEventLocked(int event) {
+            synchronized (mService) {
+                mService.reportGlobalUsageEventLocked(event);
+            }
+        }
+
+        void reportCurWakefulnessUsageEvent() {
+            synchronized (mService) {
+                mService.reportCurWakefulnessUsageEventLocked();
+            }
+        }
+
         void stackSupervisorRemoveUser(int userId) {
             synchronized (mService) {
                 mService.mStackSupervisor.removeUserLocked(userId);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 041764f..c8b6b50 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1422,21 +1422,24 @@
                 direction/*val1*/, flags/*val2*/, new StringBuilder(callingPackage)
                         .append("/").append(caller).append(" uid:").append(uid).toString()));
         final int streamType;
-        if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
-            streamType = mVolumeControlStream;
-        } else {
-            final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
-            final boolean activeForReal;
-            if (maybeActiveStreamType == AudioSystem.STREAM_RING
-                    || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
-                activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
-            } else {
-                activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
-            }
-            if (activeForReal || mVolumeControlStream == -1) {
-                streamType = maybeActiveStreamType;
-            } else {
+        synchronized (mForceControlStreamLock) {
+            // Request lock in case mVolumeControlStream is changed by other thread.
+            if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
                 streamType = mVolumeControlStream;
+            } else {
+                final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
+                final boolean activeForReal;
+                if (maybeActiveStreamType == AudioSystem.STREAM_RING
+                        || maybeActiveStreamType == AudioSystem.STREAM_NOTIFICATION) {
+                    activeForReal = wasStreamActiveRecently(maybeActiveStreamType, 0);
+                } else {
+                    activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+                }
+                if (activeForReal || mVolumeControlStream == -1) {
+                    streamType = maybeActiveStreamType;
+                } else {
+                    streamType = mVolumeControlStream;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 32b1d1a..7a6d964 100644
--- a/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -17,6 +17,7 @@
 package com.android.server.broadcastradio.hal1;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.hardware.radio.ITuner;
 import android.hardware.radio.ITunerCallback;
 import android.hardware.radio.ProgramList;
@@ -87,8 +88,9 @@
         mTuner.close();
     }
 
-    void startProgramListUpdates(@NonNull ProgramList.Filter filter) {
-        mProgramListFilter.set(Objects.requireNonNull(filter));
+    void startProgramListUpdates(@Nullable ProgramList.Filter filter) {
+        if (filter == null) filter = new ProgramList.Filter();
+        mProgramListFilter.set(filter);
         sendProgramListUpdate();
     }
 
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
index 6919282..9730c9a 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -232,6 +232,7 @@
                  * HAL implementation instance. */
                 1,      // numTuners
                 1,      // numAudioSources
+                false,  // isInitializationRequired
                 false,  // isCaptureSupported
 
                 amfmConfigToBands(amfmConfig),
@@ -374,7 +375,9 @@
         );
     }
 
-    static @NonNull ProgramFilter programFilterToHal(@NonNull ProgramList.Filter filter) {
+    static @NonNull ProgramFilter programFilterToHal(@Nullable ProgramList.Filter filter) {
+        if (filter == null) filter = new ProgramList.Filter();
+
         ProgramFilter hwFilter = new ProgramFilter();
 
         filter.getIdentifierTypes().stream().forEachOrdered(hwFilter.identifierTypes::add);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index eee830f..1bc7423 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -56,6 +56,7 @@
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
 import android.net.util.PrefixUtils;
 import android.net.util.SharedLog;
 import android.net.util.VersionedBroadcastListener;
@@ -98,6 +99,7 @@
 import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringConfiguration;
 import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.TetheringInterfaceUtils;
 import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 import com.android.server.net.BaseNetworkObserver;
 
@@ -181,9 +183,10 @@
     private final VersionedBroadcastListener mCarrierConfigChange;
     // TODO: Delete SimChangeListener; it's obsolete.
     private final SimChangeListener mSimChange;
+    private final TetheringDependencies mDeps;
 
     private volatile TetheringConfiguration mConfig;
-    private String mCurrentUpstreamIface;
+    private InterfaceSet mCurrentUpstreamIfaceSet;
     private Notification.Builder mTetheredNotificationBuilder;
     private int mLastNotificationId;
 
@@ -202,12 +205,13 @@
         mPolicyManager = policyManager;
         mLooper = looper;
         mSystemProperties = systemProperties;
+        mDeps = deps;
 
         mPublicSync = new Object();
 
         mTetherStates = new ArrayMap<>();
 
-        mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
+        mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
         mTetherMasterSM.start();
 
         final Handler smHandler = mTetherMasterSM.getHandler();
@@ -215,8 +219,8 @@
                 deps.getOffloadHardwareInterface(smHandler, mLog),
                 mContext.getContentResolver(), mNMService,
                 mLog);
-        mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
-                mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
+        mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
+                TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
         mForwardedDownstreams = new HashSet<>();
 
         IntentFilter filter = new IntentFilter();
@@ -1160,12 +1164,11 @@
     }
 
     // Needed because the canonical source of upstream truth is just the
-    // upstream interface name, |mCurrentUpstreamIface|.  This is ripe for
-    // future simplification, once the upstream Network is canonical.
+    // upstream interface set, |mCurrentUpstreamIfaceSet|.
     private boolean pertainsToCurrentUpstream(NetworkState ns) {
-        if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
+        if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) {
             for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
-                if (mCurrentUpstreamIface.equals(ifname)) {
+                if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) {
                     return true;
                 }
             }
@@ -1241,7 +1244,7 @@
 
         private static final int UPSTREAM_SETTLE_TIME_MS     = 10000;
 
-        TetherMasterSM(String name, Looper looper) {
+        TetherMasterSM(String name, Looper looper, TetheringDependencies deps) {
             super(name, looper);
 
             mInitialState = new InitialState();
@@ -1261,7 +1264,7 @@
             addState(mSetDnsForwardersErrorState);
 
             mNotifyList = new ArrayList<>();
-            mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog);
+            mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog);
             mOffload = new OffloadWrapper();
 
             setInitialState(mInitialState);
@@ -1360,31 +1363,27 @@
         }
 
         protected void setUpstreamNetwork(NetworkState ns) {
-            String iface = null;
+            InterfaceSet ifaces = null;
             if (ns != null) {
                 // Find the interface with the default IPv4 route. It may be the
                 // interface described by linkProperties, or one of the interfaces
                 // stacked on top of it.
                 mLog.i("Looking for default routes on: " + ns.linkProperties);
-                final String iface4 = getIPv4DefaultRouteInterface(ns);
-                final String iface6 = getIPv6DefaultRouteInterface(ns);
-                mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6);
-
-                iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */;
+                ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns);
+                mLog.i("Found upstream interface(s): " + ifaces);
             }
 
-            if (iface != null) {
+            if (ifaces != null) {
                 setDnsForwarders(ns.network, ns.linkProperties);
             }
-            notifyDownstreamsOfNewUpstreamIface(iface);
+            notifyDownstreamsOfNewUpstreamIface(ifaces);
             if (ns != null && pertainsToCurrentUpstream(ns)) {
                 // If we already have NetworkState for this network examine
                 // it immediately, because there likely will be no second
                 // EVENT_ON_AVAILABLE (it was already received).
                 handleNewUpstreamNetworkState(ns);
-            } else if (mCurrentUpstreamIface == null) {
-                // There are no available upstream networks, or none that
-                // have an IPv4 default route (current metric for success).
+            } else if (mCurrentUpstreamIfaceSet == null) {
+                // There are no available upstream networks.
                 handleNewUpstreamNetworkState(null);
             }
         }
@@ -1411,12 +1410,10 @@
             }
         }
 
-        protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
-            mLog.log("Notifying downstreams of upstream=" + ifaceName);
-            mCurrentUpstreamIface = ifaceName;
+        protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) {
+            mCurrentUpstreamIfaceSet = ifaces;
             for (TetherInterfaceStateMachine sm : mNotifyList) {
-                sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                        ifaceName);
+                sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces);
             }
         }
 
@@ -1488,7 +1485,7 @@
                 // For example, after CONNECTIVITY_ACTION listening is removed, here
                 // is where we could observe a Wi-Fi network becoming available and
                 // passing validation.
-                if (mCurrentUpstreamIface == null) {
+                if (mCurrentUpstreamIfaceSet == null) {
                     // If we have no upstream interface, try to run through upstream
                     // selection again.  If, for example, IPv4 connectivity has shown up
                     // after IPv6 (e.g., 464xlat became available) we want the chance to
@@ -1512,8 +1509,7 @@
                     handleNewUpstreamNetworkState(ns);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
-                    setDnsForwarders(ns.network, ns.linkProperties);
-                    handleNewUpstreamNetworkState(ns);
+                    chooseUpstreamType(false);
                     break;
                 case UpstreamNetworkMonitor.EVENT_ON_LOST:
                     // TODO: Re-evaluate possible upstreams. Currently upstream
@@ -1586,7 +1582,7 @@
                         if (VDBG) Log.d(TAG, "Tether Mode requested by " + who);
                         handleInterfaceServingStateActive(message.arg1, who);
                         who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                                mCurrentUpstreamIface);
+                                mCurrentUpstreamIfaceSet);
                         // If there has been a change and an upstream is now
                         // desired, kick off the selection process.
                         final boolean previousUpstreamWanted = updateUpstreamWanted();
@@ -1864,7 +1860,7 @@
                 pw.println(" - lastError = " + tetherState.lastError);
             }
             pw.println("Upstream wanted: " + upstreamWanted());
-            pw.println("Current upstream interface: " + mCurrentUpstreamIface);
+            pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
             pw.decreaseIndent();
         }
 
@@ -1997,7 +1993,7 @@
         final TetherState tetherState = new TetherState(
                 new TetherInterfaceStateMachine(
                     iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
-                    makeControlCallback(iface)));
+                    makeControlCallback(iface), mDeps));
         mTetherStates.put(iface, tetherState);
         tetherState.stateMachine.start();
     }
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index c9bdcf1..2fda08e 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -151,6 +151,13 @@
                 .multiply(BigInteger.valueOf(howManyPercentIsMost))
                 .divide(BigInteger.valueOf(100));
     }
+    // How many routes to evaluate before bailing and declaring this Vpn should provide
+    // the INTERNET capability. This is necessary because computing the adress space is
+    // O(n²) and this is running in the system service, so a limit is needed to alleviate
+    // the risk of attack.
+    // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm
+    // is actually O(n²)+O(n²).
+    private static final int MAX_ROUTES_TO_EVALUATE = 150;
 
     // TODO: create separate trackers for each unique VPN to support
     // automated reconnection
@@ -862,10 +869,12 @@
      */
     @VisibleForTesting
     static boolean providesRoutesToMostDestinations(LinkProperties lp) {
+        final List<RouteInfo> routes = lp.getAllRoutes();
+        if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true;
         final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator();
         TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator);
         TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator);
-        for (final RouteInfo route : lp.getAllRoutes()) {
+        for (final RouteInfo route : routes) {
             IpPrefix destination = route.getDestination();
             if (destination.isIPv4()) {
                 ipv4Prefixes.add(destination);
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 518f6c1..ba67c94 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -30,10 +30,8 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Random;
 
@@ -119,7 +117,7 @@
         if (VDBG) {
             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
         }
-        if (!canTetherIPv6(ns, mLog)) {
+        if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
             stopIPv6TetheringOnAllInterfaces();
             setUpstreamNetworkState(null);
             return;
@@ -208,70 +206,6 @@
         return null;
     }
 
-    private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) {
-        // Broadly speaking:
-        //
-        //     [1] does the upstream have an IPv6 default route?
-        //
-        // and
-        //
-        //     [2] does the upstream have one or more global IPv6 /64s
-        //         dedicated to this device?
-        //
-        // In lieu of Prefix Delegation and other evaluation of whether a
-        // prefix may or may not be dedicated to this device, for now just
-        // check whether the upstream is TRANSPORT_CELLULAR. This works
-        // because "[t]he 3GPP network allocates each default bearer a unique
-        // /64 prefix", per RFC 6459, Section 5.2.
-
-        final boolean canTether =
-                (ns != null) && (ns.network != null) &&
-                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
-                // At least one upstream DNS server:
-                ns.linkProperties.isProvisioned() &&
-                // Minimal amount of IPv6 provisioning:
-                ns.linkProperties.hasIPv6DefaultRoute() &&
-                ns.linkProperties.hasGlobalIPv6Address() &&
-                // Temporary approximation of "dedicated prefix":
-                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
-
-        // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
-        // tethering with 464xlat involved). TODO: Rectify this shortcoming,
-        // likely by calling NetworkManagementService#startInterfaceForwarding()
-        // for all upstream interfaces.
-        RouteInfo v4default = null;
-        RouteInfo v6default = null;
-        if (canTether) {
-            for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
-                if (r.isIPv4Default()) {
-                    v4default = r;
-                } else if (r.isIPv6Default()) {
-                    v6default = r;
-                }
-
-                if (v4default != null && v6default != null) {
-                    break;
-                }
-            }
-        }
-
-        final boolean supportedConfiguration =
-                (v4default != null) && (v6default != null) &&
-                (v4default.getInterface() != null) &&
-                v4default.getInterface().equals(v6default.getInterface());
-
-        final boolean outcome = canTether && supportedConfiguration;
-
-        if (ns == null) {
-            sharedLog.log("No available upstream.");
-        } else {
-            sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s",
-                    (outcome ? "available" : "not available"), toDebugString(ns)));
-        }
-
-        return outcome;
-    }
-
     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
         final LinkProperties v6only = new LinkProperties();
         if (lp == null) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 2224913..5ed14a0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -31,7 +31,7 @@
 import android.net.ip.RouterAdvertisementDaemon;
 import android.net.ip.RouterAdvertisementDaemon.RaParams;
 import android.net.util.InterfaceParams;
-import android.net.util.NetdService;
+import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.Looper;
@@ -49,12 +49,12 @@
 
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Objects;
 import java.util.Random;
+import java.util.Set;
 
 /**
  * Provides the interface to IP-layer serving functionality for a given network
@@ -117,9 +117,11 @@
     private final int mInterfaceType;
     private final LinkProperties mLinkProperties;
 
+    private final TetheringDependencies mDeps;
+
     private int mLastError;
     private int mServingMode;
-    private String mMyUpstreamIfaceName;  // may change over time
+    private InterfaceSet mUpstreamIfaceSet;  // may change over time
     private InterfaceParams mInterfaceParams;
     // TODO: De-duplicate this with mLinkProperties above. Currently, these link
     // properties are those selected by the IPv6TetheringCoordinator and relayed
@@ -134,18 +136,19 @@
     public TetherInterfaceStateMachine(
             String ifaceName, Looper looper, int interfaceType, SharedLog log,
             INetworkManagementService nMService, INetworkStatsService statsService,
-            IControlsTethering tetherController) {
+            IControlsTethering tetherController,
+            TetheringDependencies deps) {
         super(ifaceName, looper);
         mLog = log.forSubComponent(ifaceName);
         mNMService = nMService;
-        // TODO: This should be passed in for testability.
-        mNetd = NetdService.getInstance();
+        mNetd = deps.getNetdService();
         mStatsService = statsService;
         mTetherController = tetherController;
         mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
         mIfaceName = ifaceName;
         mInterfaceType = interfaceType;
         mLinkProperties = new LinkProperties();
+        mDeps = deps;
         resetLinkProperties();
         mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
         mServingMode = IControlsTethering.STATE_AVAILABLE;
@@ -246,16 +249,14 @@
     }
 
     private boolean startIPv6() {
-        // TODO: Refactor for better testability.  This is one of the things
-        // that prohibits unittesting IPv6 tethering setup.
-        mInterfaceParams = InterfaceParams.getByName(mIfaceName);
+        mInterfaceParams = mDeps.getInterfaceParams(mIfaceName);
         if (mInterfaceParams == null) {
             mLog.e("Failed to find InterfaceParams");
             stopIPv6();
             return false;
         }
 
-        mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams);
+        mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams);
         if (!mRaDaemon.start()) {
             stopIPv6();
             return false;
@@ -621,10 +622,10 @@
         }
 
         private void cleanupUpstream() {
-            if (mMyUpstreamIfaceName == null) return;
+            if (mUpstreamIfaceSet == null) return;
 
-            cleanupUpstreamInterface(mMyUpstreamIfaceName);
-            mMyUpstreamIfaceName = null;
+            for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
+            mUpstreamIfaceSet = null;
         }
 
         private void cleanupUpstreamInterface(String upstreamIface) {
@@ -660,34 +661,66 @@
                     mLog.e("CMD_TETHER_REQUESTED while already tethering.");
                     break;
                 case CMD_TETHER_CONNECTION_CHANGED:
-                    String newUpstreamIfaceName = (String)(message.obj);
-                    if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) ||
-                            (mMyUpstreamIfaceName != null &&
-                            mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) {
+                    final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
+                    if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
                         if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
                         break;
                     }
-                    cleanupUpstream();
-                    if (newUpstreamIfaceName != null) {
+
+                    if (newUpstreamIfaceSet == null) {
+                        cleanupUpstream();
+                        break;
+                    }
+
+                    for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
+                        cleanupUpstreamInterface(removed);
+                    }
+
+                    final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
+                    // This makes the call to cleanupUpstream() in the error
+                    // path for any interface neatly cleanup all the interfaces.
+                    mUpstreamIfaceSet = newUpstreamIfaceSet;
+
+                    for (String ifname : added) {
                         try {
-                            mNMService.enableNat(mIfaceName, newUpstreamIfaceName);
-                            mNMService.startInterfaceForwarding(mIfaceName,
-                                    newUpstreamIfaceName);
+                            mNMService.enableNat(mIfaceName, ifname);
+                            mNMService.startInterfaceForwarding(mIfaceName, ifname);
                         } catch (Exception e) {
                             mLog.e("Exception enabling NAT: " + e);
-                            cleanupUpstreamInterface(newUpstreamIfaceName);
+                            cleanupUpstream();
                             mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
                             transitionTo(mInitialState);
                             return true;
                         }
                     }
-                    mMyUpstreamIfaceName = newUpstreamIfaceName;
                     break;
                 default:
                     return false;
             }
             return true;
         }
+
+        private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) {
+            if (mUpstreamIfaceSet == null && newIfaces == null) return true;
+            if (mUpstreamIfaceSet != null && newIfaces != null) {
+                return mUpstreamIfaceSet.equals(newIfaces);
+            }
+            return false;
+        }
+
+        private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) {
+            if (mUpstreamIfaceSet == null) return new HashSet<>();
+
+            final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames);
+            removed.removeAll(newIfaces.ifnames);
+            return removed;
+        }
+
+        private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) {
+            final HashSet<String> added = new HashSet<>(newIfaces.ifnames);
+            if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames);
+            return added;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index b8174b6..66afb0f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,9 +16,18 @@
 
 package com.android.server.connectivity.tethering;
 
+import android.content.Context;
+import android.net.INetd;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetdService;
 import android.os.Handler;
 import android.net.util.SharedLog;
 
+import com.android.internal.util.StateMachine;
+
+import java.util.ArrayList;
+
 
 /**
  * Capture tethering dependencies, for injection.
@@ -29,4 +38,26 @@
     public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
         return new OffloadHardwareInterface(h, log);
     }
+
+    public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target,
+            SharedLog log, int what) {
+        return new UpstreamNetworkMonitor(ctx, target, log, what);
+    }
+
+    public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+            ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+        return new IPv6TetheringCoordinator(notifyList, log);
+    }
+
+    public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+        return new RouterAdvertisementDaemon(ifParams);
+    }
+
+    public InterfaceParams getInterfaceParams(String ifName) {
+        return InterfaceParams.getByName(ifName);
+    }
+
+    public INetd getNetdService() {
+        return NetdService.getInstance();
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
new file mode 100644
index 0000000..6c7ff91
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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.tethering;
+
+import android.annotation.Nullable;
+import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkState;
+import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
+/**
+ * @hide
+ */
+public final class TetheringInterfaceUtils {
+    /**
+     * Get upstream interfaces for tethering based on default routes for IPv4/IPv6.
+     * @return null if there is no usable interface, or a set of at least one interface otherwise.
+     */
+    public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) {
+        if (ns == null) {
+            return null;
+        }
+
+        final LinkProperties lp = ns.linkProperties;
+        final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY);
+        final String if6 = getIPv6Interface(ns);
+
+        return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6);
+    }
+
+    /**
+     * Get the upstream interface for IPv6 tethering.
+     * @return null if there is no usable interface, or the interface name otherwise.
+     */
+    public static @Nullable String getIPv6Interface(NetworkState ns) {
+        // Broadly speaking:
+        //
+        //     [1] does the upstream have an IPv6 default route?
+        //
+        // and
+        //
+        //     [2] does the upstream have one or more global IPv6 /64s
+        //         dedicated to this device?
+        //
+        // In lieu of Prefix Delegation and other evaluation of whether a
+        // prefix may or may not be dedicated to this device, for now just
+        // check whether the upstream is TRANSPORT_CELLULAR. This works
+        // because "[t]he 3GPP network allocates each default bearer a unique
+        // /64 prefix", per RFC 6459, Section 5.2.
+        final boolean canTether =
+                (ns != null) && (ns.network != null) &&
+                (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
+                // At least one upstream DNS server:
+                ns.linkProperties.hasIPv6DnsServer() &&
+                // Minimal amount of IPv6 provisioning:
+                ns.linkProperties.hasGlobalIPv6Address() &&
+                // Temporary approximation of "dedicated prefix":
+                ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+
+        return canTether
+                ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY)
+                : null;
+    }
+
+    private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
+        final RouteInfo ri = (lp != null)
+                ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst)
+                : null;
+        return (ri != null) ? ri.getInterface() : null;
+    }
+}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 63308f8..ec404fe 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -26,6 +26,7 @@
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
 import android.content.Context;
 import android.content.IContentService;
 import android.content.ISyncStatusObserver;
@@ -78,7 +79,7 @@
  */
 public final class ContentService extends IContentService.Stub {
     static final String TAG = "ContentService";
-    static final boolean DEBUG = false;
+    static final boolean DEBUG = true;
 
     public static class Lifecycle extends SystemService {
         private ContentService mService;
@@ -451,7 +452,7 @@
                 SyncManager syncManager = getSyncManager();
                 if (syncManager != null) {
                     syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle, uid,
-                            uri.getAuthority(), /*isAppStandbyExempted=*/ isUidInForeground(uid));
+                            uri.getAuthority(), getSyncExemptionForCaller(uid));
                 }
             }
 
@@ -508,7 +509,7 @@
         int uId = Binder.getCallingUid();
 
         validateExtras(uId, extras);
-        final boolean isForegroundSyncRequest = isForegroundSyncRequest(uId, extras);
+        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(uId, extras);
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
@@ -518,7 +519,7 @@
             if (syncManager != null) {
                 syncManager.scheduleSync(account, userId, uId, authority, extras,
                         SyncStorageEngine.AuthorityInfo.UNDEFINED,
-                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
+                        syncExemption);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -561,7 +562,7 @@
         final Bundle extras = request.getBundle();
 
         validateExtras(callerUid, extras);
-        final boolean isForegroundSyncRequest = isForegroundSyncRequest(callerUid, extras);
+        final int syncExemption = getSyncExemptionAndCleanUpExtrasForCaller(callerUid, extras);
 
         // This makes it so that future permission checks will be in the context of this
         // process rather than the caller's process. We will restore this before returning.
@@ -589,7 +590,7 @@
                 syncManager.scheduleSync(
                         request.getAccount(), userId, callerUid, request.getProvider(), extras,
                         SyncStorageEngine.AuthorityInfo.UNDEFINED,
-                        /*isAppStandbyExempted=*/ isForegroundSyncRequest);
+                        syncExemption);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -777,13 +778,15 @@
                 "no permission to write the sync settings");
         enforceCrossUserPermission(userId,
                 "no permission to modify the sync settings for user " + userId);
+        final int callingUid = Binder.getCallingUid();
+        final int syncExemptionFlag = getSyncExemptionForCaller(callingUid);
 
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
                 syncManager.getSyncStorageEngine().setSyncAutomatically(account, userId,
-                        providerName, sync);
+                        providerName, sync, syncExemptionFlag);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -964,11 +967,14 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.WRITE_SYNC_SETTINGS,
                 "no permission to write the sync settings");
 
+        final int callingUid = Binder.getCallingUid();
+
         long identityToken = clearCallingIdentity();
         try {
             SyncManager syncManager = getSyncManager();
             if (syncManager != null) {
-                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId);
+                syncManager.getSyncStorageEngine().setMasterSyncAutomatically(flag, userId,
+                        getSyncExemptionForCaller(callingUid));
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1263,9 +1269,7 @@
     }
 
     private void validateExtras(int callingUid, Bundle extras) {
-        if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
-                || extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)
-                ) {
+        if (extras.containsKey(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG)) {
             switch (callingUid) {
                 case Process.ROOT_UID:
                 case Process.SHELL_UID:
@@ -1277,39 +1281,36 @@
         }
     }
 
-    private boolean isForegroundSyncRequest(int callingUid, Bundle extras) {
-        final boolean isForegroundRequest;
-        if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC)) {
-            isForegroundRequest = true;
-        } else if (extras.getBoolean(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC)) {
-            isForegroundRequest = false;
-        } else {
-            isForegroundRequest = isUidInForeground(callingUid);
-        }
-        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC);
-        extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC);
-
-        return isForegroundRequest;
+    @SyncExemption
+    private int getSyncExemptionForCaller(int callingUid) {
+        return getSyncExemptionAndCleanUpExtrasForCaller(callingUid, null);
     }
 
-    private boolean isUidInForeground(int uid) {
-        // If the caller is ADB, we assume it's a background request by default, because
-        // that's also the default of requests from the requestsync command.
-        // The requestsync command will always set either SYNC_VIRTUAL_EXTRAS_FORCE_FG_SYNC or
-        // SYNC_VIRTUAL_EXTRAS_FORCE_BG_SYNC (for non-periodic sync requests),
-        // so it shouldn't matter in practice.
-        switch (uid) {
-            case Process.SHELL_UID:
-            case Process.ROOT_UID:
-                return false;
+    @SyncExemption
+    private int getSyncExemptionAndCleanUpExtrasForCaller(int callingUid, Bundle extras) {
+        if (extras != null) {
+            final int exemption =
+                    extras.getInt(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG, -1);
+
+            // Need to remove the virtual extra.
+            extras.remove(ContentResolver.SYNC_VIRTUAL_EXTRAS_EXEMPTION_FLAG);
+            if (exemption != -1) {
+                return exemption;
+            }
         }
         final ActivityManagerInternal ami =
                 LocalServices.getService(ActivityManagerInternal.class);
-        if (ami != null) {
-            return ami.getUidProcessState(uid)
-                    <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+        final int procState = (ami != null)
+                ? ami.getUidProcessState(callingUid)
+                : ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+        if (procState <= ActivityManager.PROCESS_STATE_TOP) {
+            return ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP;
         }
-        return false;
+        if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+            return ContentResolver.SYNC_EXEMPTION_ACTIVE;
+        }
+        return ContentResolver.SYNC_EXEMPTION_NONE;
     }
 
     /**
diff --git a/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
new file mode 100644
index 0000000..62fb751
--- /dev/null
+++ b/services/core/java/com/android/server/content/SyncAdapterStateFetcher.java
@@ -0,0 +1,68 @@
+/*
+ * 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.content;
+
+import android.app.usage.UsageStatsManagerInternal;
+import android.os.SystemClock;
+import android.util.Pair;
+
+import com.android.server.AppStateTracker;
+import com.android.server.LocalServices;
+
+import java.util.HashMap;
+
+class SyncAdapterStateFetcher {
+
+    private final HashMap<Pair<Integer, String>, Integer> mBucketCache =
+            new HashMap<>();
+
+    public SyncAdapterStateFetcher() {
+    }
+
+    /**
+     * Return sync adapter state with a cache.
+     */
+    public int getStandbyBucket(int userId, String packageName) {
+        final Pair<Integer, String> key = Pair.create(userId, packageName);
+        final Integer cached = mBucketCache.get(key);
+        if (cached != null) {
+            return cached;
+        }
+        final UsageStatsManagerInternal usmi =
+                LocalServices.getService(UsageStatsManagerInternal.class);
+        if (usmi == null) {
+            return -1; // Unknown.
+        }
+
+        final int value = usmi.getAppStandbyBucket(packageName, userId,
+                SystemClock.elapsedRealtime());
+        mBucketCache.put(key, value);
+        return value;
+    }
+
+    /**
+     * Return UID active state.
+     */
+    public boolean isAppActive(int uid) {
+        final AppStateTracker ast =
+                LocalServices.getService(AppStateTracker.class);
+        if (ast == null) {
+            return false;
+        }
+
+        return ast.isUidActive(uid);
+    }
+}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 7089268..d1f50b7 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -29,9 +29,11 @@
 import android.app.PendingIntent;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
+import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
 import android.content.Context;
 import android.content.ISyncAdapter;
 import android.content.ISyncAdapterUnsyncableAccountCallback;
@@ -70,6 +72,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.PowerManager;
+import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -88,6 +91,8 @@
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.util.ArrayUtils;
+import com.android.server.DeviceIdleController;
+import com.android.server.DeviceIdleController.LocalService;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.job.JobSchedulerInternal;
@@ -550,10 +555,6 @@
         return mJobScheduler;
     }
 
-    /**
-     * Should only be created after {@link ContentService#systemReady()} so that
-     * {@link PackageManager} is ready to query.
-     */
     public SyncManager(Context context, boolean factoryTest) {
         // Initialize the SyncStorageEngine first, before registering observers
         // and creating threads and so on; it may fail if the disk is full.
@@ -566,9 +567,9 @@
         mSyncStorageEngine.setOnSyncRequestListener(new OnSyncRequestListener() {
             @Override
             public void onSyncRequest(SyncStorageEngine.EndPoint info, int reason, Bundle extras,
-                    boolean isAppStandbyExempted) {
+                    @SyncExemption int syncExemptionFlag) {
                 scheduleSync(info.account, info.userId, reason, info.provider, extras,
-                        AuthorityInfo.UNDEFINED, isAppStandbyExempted);
+                        AuthorityInfo.UNDEFINED, syncExemptionFlag);
             }
         });
 
@@ -599,7 +600,7 @@
                     scheduleSync(null, UserHandle.USER_ALL,
                             SyncOperation.REASON_SERVICE_CHANGED,
                             type.authority, null, AuthorityInfo.UNDEFINED,
-                            /*isAppStandbyExempted=*/ false);
+                            ContentResolver.SYNC_EXEMPTION_NONE);
                 }
             }
         }, mSyncHandler);
@@ -649,7 +650,7 @@
                 scheduleSync(account, UserHandle.getUserId(uid),
                         SyncOperation.REASON_ACCOUNTS_UPDATED,
                         null, null, AuthorityInfo.SYNCABLE_NO_ACCOUNT_ACCESS,
-                        /*isAppStandbyExempted=*/ false);
+                        ContentResolver.SYNC_EXEMPTION_NONE);
             }
         });
 
@@ -883,9 +884,9 @@
      */
     public void scheduleSync(Account requestedAccount, int userId, int reason,
             String requestedAuthority, Bundle extras, int targetSyncState,
-            boolean isAppStandbyExempted) {
+            @SyncExemption int syncExemptionFlag) {
         scheduleSync(requestedAccount, userId, reason, requestedAuthority, extras, targetSyncState,
-                0 /* min delay */, true /* checkIfAccountReady */, isAppStandbyExempted);
+                0 /* min delay */, true /* checkIfAccountReady */, syncExemptionFlag);
     }
 
     /**
@@ -894,7 +895,7 @@
     private void scheduleSync(Account requestedAccount, int userId, int reason,
             String requestedAuthority, Bundle extras, int targetSyncState,
             final long minDelayMillis, boolean checkIfAccountReady,
-            boolean isAppStandbyExempted) {
+            @SyncExemption int syncExemptionFlag) {
         final boolean isLoggable = Log.isLoggable(TAG, Log.VERBOSE);
         if (extras == null) {
             extras = new Bundle();
@@ -904,7 +905,7 @@
                     + requestedAuthority
                     + " reason=" + reason
                     + " checkIfAccountReady=" + checkIfAccountReady
-                    + " isAppStandbyExempted=" + isAppStandbyExempted);
+                    + " syncExemptionFlag=" + syncExemptionFlag);
         }
 
         AccountAndUser[] accounts = null;
@@ -1016,7 +1017,7 @@
                                     scheduleSync(account.account, userId, reason, authority,
                                             finalExtras, targetSyncState, minDelayMillis,
                                             true /* checkIfAccountReady */,
-                                            isAppStandbyExempted);
+                                            syncExemptionFlag);
                                 }
                             }
                         ));
@@ -1067,7 +1068,7 @@
                         sendOnUnsyncableAccount(mContext, syncAdapterInfo, account.userId,
                                 () -> scheduleSync(account.account, account.userId, reason,
                                         authority, finalExtras, targetSyncState, minDelayMillis,
-                                        false, isAppStandbyExempted));
+                                        false, syncExemptionFlag));
                     } else {
                         // Initialisation sync.
                         Bundle newExtras = new Bundle();
@@ -1086,7 +1087,7 @@
                                 new SyncOperation(account.account, account.userId,
                                         owningUid, owningPackage, reason, source,
                                         authority, newExtras, allowParallelSyncs,
-                                        isAppStandbyExempted),
+                                        syncExemptionFlag),
                                 minDelayMillis
                         );
                     }
@@ -1103,7 +1104,7 @@
                     postScheduleSyncMessage(
                             new SyncOperation(account.account, account.userId,
                                     owningUid, owningPackage, reason, source,
-                                    authority, extras, allowParallelSyncs, isAppStandbyExempted),
+                                    authority, extras, allowParallelSyncs, syncExemptionFlag),
                             minDelayMillis
                     );
                 }
@@ -1217,12 +1218,12 @@
      * ms to batch syncs.
      */
     public void scheduleLocalSync(Account account, int userId, int reason, String authority,
-            boolean isAppStandbyExempted) {
+            @SyncExemption int syncExemptionFlag) {
         final Bundle extras = new Bundle();
         extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, true);
         scheduleSync(account, userId, reason, authority, extras,
                 AuthorityInfo.UNDEFINED, LOCAL_SYNC_DELAY, true /* checkIfAccountReady */,
-                isAppStandbyExempted);
+                syncExemptionFlag);
     }
 
     public SyncAdapterType[] getSyncAdapterTypes(int userId) {
@@ -1493,7 +1494,7 @@
 
         // If any of the duplicate ones has exemption, then we inherit it.
         if (!syncOperation.isPeriodic) {
-            boolean inheritAppStandbyExemption = false;
+            int inheritedSyncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
 
             // Check currently running syncs
             for (ActiveSyncContext asc: mActiveSyncContexts) {
@@ -1534,10 +1535,11 @@
                 // This means the duplicate one has a negative expected run time, but it hasn't
                 // been executed possibly because of app-standby.
 
-                if (syncOperation.isAppStandbyExempted
-                        && (minDelay == 0)
-                        && !syncToRun.isAppStandbyExempted) {
+                if ((minDelay == 0)
+                        && (syncToRun.syncExemptionFlag < syncOperation.syncExemptionFlag)) {
                     syncToRun = syncOperation;
+                    inheritedSyncExemptionFlag =
+                            Math.max(inheritedSyncExemptionFlag, syncToRun.syncExemptionFlag);
                 }
             }
 
@@ -1551,9 +1553,8 @@
                         if (isLoggable) {
                             Slog.v(TAG, "Cancelling duplicate sync " + op);
                         }
-                        if (op.isAppStandbyExempted) {
-                            inheritAppStandbyExemption = true;
-                        }
+                        inheritedSyncExemptionFlag =
+                                Math.max(inheritedSyncExemptionFlag, op.syncExemptionFlag);
                         cancelJob(op, "scheduleSyncOperationH-duplicate");
                     }
                 }
@@ -1570,8 +1571,9 @@
             }
 
             // If any of the duplicates had exemption, we exempt the current one.
-            if (inheritAppStandbyExemption) {
-                syncOperation.isAppStandbyExempted = true;
+            //
+            if (inheritedSyncExemptionFlag > ContentResolver.SYNC_EXEMPTION_NONE) {
+                syncOperation.syncExemptionFlag = inheritedSyncExemptionFlag;
             }
         }
 
@@ -1591,7 +1593,7 @@
 
         // Note this logic means when an exempted sync fails,
         // the back-off one will inherit it too, and will be exempted from app-standby.
-        final int jobFlags = syncOperation.isAppStandbyExempted
+        final int jobFlags = syncOperation.isAppStandbyExempted()
                 ? JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY : 0;
 
         JobInfo.Builder b = new JobInfo.Builder(syncOperation.jobId,
@@ -1615,6 +1617,19 @@
             b.setRequiresCharging(true);
         }
 
+        if (syncOperation.syncExemptionFlag
+                == ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP) {
+            DeviceIdleController.LocalService dic =
+                    LocalServices.getService(DeviceIdleController.LocalService.class);
+            if (dic != null) {
+                dic.addPowerSaveTempWhitelistApp(Process.SYSTEM_UID,
+                        syncOperation.owningPackage,
+                        mConstants.getKeyExemptionTempWhitelistDurationInSeconds() * 1000,
+                        UserHandle.getUserId(syncOperation.owningUid),
+                        /* sync=*/ false, "sync by top app");
+            }
+        }
+
         getJobScheduler().scheduleAsPackage(b.build(), syncOperation.owningPackage,
                 syncOperation.target.userId, syncOperation.wakeLockName());
     }
@@ -1736,7 +1751,7 @@
                 mContext.getOpPackageName());
         for (Account account : accounts) {
             scheduleSync(account, userId, SyncOperation.REASON_USER_START, null, null,
-                    AuthorityInfo.NOT_INITIALIZED, /*isAppStandbyExempted=*/ false);
+                    AuthorityInfo.NOT_INITIALIZED, ContentResolver.SYNC_EXEMPTION_NONE);
         }
     }
 
@@ -1930,7 +1945,10 @@
 
     protected void dump(FileDescriptor fd, PrintWriter pw, boolean dumpAll) {
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
-        dumpSyncState(ipw);
+
+        final SyncAdapterStateFetcher buckets = new SyncAdapterStateFetcher();
+
+        dumpSyncState(ipw, buckets);
         mConstants.dump(pw, "");
         dumpSyncAdapters(ipw);
 
@@ -1991,7 +2009,7 @@
         return ret;
     }
 
-    protected void dumpPendingSyncs(PrintWriter pw) {
+    protected void dumpPendingSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
 
         pw.print("Pending Syncs: ");
@@ -2001,14 +2019,14 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (!op.isPeriodic) {
-                pw.println(op.dump(null, false));
+                pw.println(op.dump(null, false, buckets));
                 count++;
             }
         }
         pw.println();
     }
 
-    protected void dumpPeriodicSyncs(PrintWriter pw) {
+    protected void dumpPeriodicSyncs(PrintWriter pw, SyncAdapterStateFetcher buckets) {
         List<SyncOperation> pendingSyncs = getAllPendingSyncs();
 
         pw.print("Periodic Syncs: ");
@@ -2018,7 +2036,7 @@
         int count = 0;
         for (SyncOperation op: pendingSyncs) {
             if (op.isPeriodic) {
-                pw.println(op.dump(null, false));
+                pw.println(op.dump(null, false, buckets));
                 count++;
             }
         }
@@ -2075,7 +2093,7 @@
         return true;
     }
 
-    protected void dumpSyncState(PrintWriter pw) {
+    protected void dumpSyncState(PrintWriter pw, SyncAdapterStateFetcher buckets) {
         final StringBuilder sb = new StringBuilder();
 
         pw.print("Data connected: "); pw.println(mDataConnectionIsConnected);
@@ -2150,13 +2168,13 @@
             sb.setLength(0);
             pw.print(formatDurationHMS(sb, durationInSeconds));
             pw.print(" - ");
-            pw.print(activeSyncContext.mSyncOperation.dump(pm, false));
+            pw.print(activeSyncContext.mSyncOperation.dump(pm, false, buckets));
             pw.println();
         }
         pw.println();
 
-        dumpPendingSyncs(pw);
-        dumpPeriodicSyncs(pw);
+        dumpPendingSyncs(pw, buckets);
+        dumpPeriodicSyncs(pw, buckets);
 
         // Join the installed sync adapter with the accounts list and emit for everything.
         pw.println("Sync Status");
@@ -3219,7 +3237,7 @@
                 scheduleSync(syncTargets.account, syncTargets.userId,
                         SyncOperation.REASON_ACCOUNTS_UPDATED, syncTargets.provider,
                         null, AuthorityInfo.NOT_INITIALIZED,
-                        /*isAppStandbyExempted=*/ false);
+                        ContentResolver.SYNC_EXEMPTION_NONE);
             }
         }
 
@@ -3286,7 +3304,7 @@
                     syncAdapterInfo.componentName.getPackageName(), SyncOperation.REASON_PERIODIC,
                     SyncStorageEngine.SOURCE_PERIODIC, extras,
                     syncAdapterInfo.type.allowParallelSyncs(), true, SyncOperation.NO_JOB_ID,
-                    pollFrequencyMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+                    pollFrequencyMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
 
             final int syncOpState = computeSyncOpState(op);
             switch (syncOpState) {
@@ -3431,6 +3449,15 @@
                     Slog.v(TAG, syncContext.toString());
                 }
             }
+            if (op.isAppStandbyExempted()) {
+                final UsageStatsManagerInternal usmi = LocalServices.getService(
+                        UsageStatsManagerInternal.class);
+                if (usmi != null) {
+                    usmi.reportExemptedSyncStart(op.owningPackage,
+                            UserHandle.getUserId(op.owningUid));
+                }
+            }
+
             // Connect to the sync adapter.
             int targetUid;
             ComponentName targetComponent;
@@ -3604,7 +3631,7 @@
 
                     syncOperation.retries++;
                     if (syncOperation.retries > mConstants.getMaxRetriesWithAppStandbyExemption()) {
-                        syncOperation.isAppStandbyExempted = false;
+                        syncOperation.syncExemptionFlag = ContentResolver.SYNC_EXEMPTION_NONE;
                     }
 
                     // the operation failed so increase the backoff time
@@ -3672,7 +3699,7 @@
                                 syncOperation.reason,
                                 syncOperation.syncSource, info.provider, new Bundle(),
                                 syncOperation.allowParallelSyncs,
-                                syncOperation.isAppStandbyExempted));
+                                syncOperation.syncExemptionFlag));
             }
         }
 
diff --git a/services/core/java/com/android/server/content/SyncManagerConstants.java b/services/core/java/com/android/server/content/SyncManagerConstants.java
index 061e4ca..2a5858c 100644
--- a/services/core/java/com/android/server/content/SyncManagerConstants.java
+++ b/services/core/java/com/android/server/content/SyncManagerConstants.java
@@ -52,6 +52,12 @@
     private static final int DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION = 5;
     private int mMaxRetriesWithAppStandbyExemption = DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION;
 
+    private static final String KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS =
+            "exemption_temp_whitelist_duration_in_seconds";
+    private static final int DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS = 10 * 60;
+    private int mKeyExemptionTempWhitelistDurationInSeconds
+            = DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS;
+
     protected SyncManagerConstants(Context context) {
         super(null);
         mContext = context;
@@ -97,6 +103,11 @@
             mMaxRetriesWithAppStandbyExemption = parser.getInt(
                     KEY_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION,
                     DEF_MAX_RETRIES_WITH_APP_STANDBY_EXEMPTION);
+
+            mKeyExemptionTempWhitelistDurationInSeconds = parser.getInt(
+                    KEY_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS,
+                    DEF_EXEMPTION_TEMP_WHITELIST_DURATION_IN_SECONDS);
+
         }
     }
 
@@ -124,6 +135,12 @@
         }
     }
 
+    public int getKeyExemptionTempWhitelistDurationInSeconds() {
+        synchronized (mLock) {
+            return mKeyExemptionTempWhitelistDurationInSeconds;
+        }
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         synchronized (mLock) {
             pw.print(prefix);
@@ -144,6 +161,10 @@
             pw.print(prefix);
             pw.print("  mMaxRetriesWithAppStandbyExemption=");
             pw.println(mMaxRetriesWithAppStandbyExemption);
+
+            pw.print(prefix);
+            pw.print("  mKeyExemptionTempWhitelistDurationInSeconds=");
+            pw.println(mKeyExemptionTempWhitelistDurationInSeconds);
         }
     }
 }
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 96bdaea..d097563 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -19,6 +19,7 @@
 import android.accounts.Account;
 import android.app.job.JobInfo;
 import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.PersistableBundle;
@@ -98,33 +99,33 @@
     /** jobId of the JobScheduler job corresponding to this sync */
     public int jobId;
 
-    /** Whether this operation should be exempted from the app-standby throttling. */
-    public boolean isAppStandbyExempted;
+    @SyncExemption
+    public int syncExemptionFlag;
 
     public SyncOperation(Account account, int userId, int owningUid, String owningPackage,
                          int reason, int source, String provider, Bundle extras,
-                         boolean allowParallelSyncs, boolean isAppStandbyExempted) {
+                         boolean allowParallelSyncs, @SyncExemption int syncExemptionFlag) {
         this(new SyncStorageEngine.EndPoint(account, provider, userId), owningUid, owningPackage,
-                reason, source, extras, allowParallelSyncs, isAppStandbyExempted);
+                reason, source, extras, allowParallelSyncs, syncExemptionFlag);
     }
 
     private SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
             int reason, int source, Bundle extras, boolean allowParallelSyncs,
-            boolean isAppStandbyExempted) {
+            @SyncExemption int syncExemptionFlag) {
         this(info, owningUid, owningPackage, reason, source, extras, allowParallelSyncs, false,
-                NO_JOB_ID, 0, 0, isAppStandbyExempted);
+                NO_JOB_ID, 0, 0, syncExemptionFlag);
     }
 
     public SyncOperation(SyncOperation op, long periodMillis, long flexMillis) {
         this(op.target, op.owningUid, op.owningPackage, op.reason, op.syncSource,
                 new Bundle(op.extras), op.allowParallelSyncs, op.isPeriodic, op.sourcePeriodicId,
-                periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+                periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
     }
 
     public SyncOperation(SyncStorageEngine.EndPoint info, int owningUid, String owningPackage,
                          int reason, int source, Bundle extras, boolean allowParallelSyncs,
                          boolean isPeriodic, int sourcePeriodicId, long periodMillis,
-                         long flexMillis, boolean isAppStandbyExempted) {
+                         long flexMillis, @SyncExemption int syncExemptionFlag) {
         this.target = info;
         this.owningUid = owningUid;
         this.owningPackage = owningPackage;
@@ -138,7 +139,7 @@
         this.flexMillis = flexMillis;
         this.jobId = NO_JOB_ID;
         this.key = toKey();
-        this.isAppStandbyExempted = isAppStandbyExempted;
+        this.syncExemptionFlag = syncExemptionFlag;
     }
 
     /* Get a one off sync operation instance from a periodic sync. */
@@ -148,7 +149,7 @@
         }
         SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, syncSource,
                 new Bundle(extras), allowParallelSyncs, false, jobId /* sourcePeriodicId */,
-                periodMillis, flexMillis, /*isAppStandbyExempted=*/ false);
+                periodMillis, flexMillis, ContentResolver.SYNC_EXEMPTION_NONE);
         return op;
     }
 
@@ -166,7 +167,7 @@
         periodMillis = other.periodMillis;
         flexMillis = other.flexMillis;
         this.key = other.key;
-        isAppStandbyExempted = other.isAppStandbyExempted;
+        syncExemptionFlag = other.syncExemptionFlag;
     }
 
     /**
@@ -235,7 +236,7 @@
         jobInfoExtras.putLong("flexMillis", flexMillis);
         jobInfoExtras.putLong("expectedRuntime", expectedRuntime);
         jobInfoExtras.putInt("retries", retries);
-        jobInfoExtras.putBoolean("isAppStandbyExempted", isAppStandbyExempted);
+        jobInfoExtras.putInt("syncExemptionFlag", syncExemptionFlag);
         return jobInfoExtras;
     }
 
@@ -256,7 +257,7 @@
         Bundle extras;
         boolean allowParallelSyncs, isPeriodic;
         long periodMillis, flexMillis;
-        boolean isAppStandbyExempted;
+        int syncExemptionFlag;
 
         if (!jobExtras.getBoolean("SyncManagerJob", false)) {
             return null;
@@ -275,7 +276,8 @@
         initiatedBy = jobExtras.getInt("sourcePeriodicId", NO_JOB_ID);
         periodMillis = jobExtras.getLong("periodMillis");
         flexMillis = jobExtras.getLong("flexMillis");
-        isAppStandbyExempted = jobExtras.getBoolean("isAppStandbyExempted", false);
+        syncExemptionFlag = jobExtras.getInt("syncExemptionFlag",
+                ContentResolver.SYNC_EXEMPTION_NONE);
         extras = new Bundle();
 
         PersistableBundle syncExtras = jobExtras.getPersistableBundle("syncExtras");
@@ -298,7 +300,7 @@
                 new SyncStorageEngine.EndPoint(account, provider, userId);
         SyncOperation op = new SyncOperation(target, owningUid, owningPackage, reason, source,
                 extras, allowParallelSyncs, isPeriodic, initiatedBy, periodMillis, flexMillis,
-                isAppStandbyExempted);
+                syncExemptionFlag);
         op.jobId = jobExtras.getInt("jobId");
         op.expectedRuntime = jobExtras.getLong("expectedRuntime");
         op.retries = jobExtras.getInt("retries");
@@ -361,10 +363,10 @@
 
     @Override
     public String toString() {
-        return dump(null, true);
+        return dump(null, true, null);
     }
 
-    String dump(PackageManager pm, boolean shorter) {
+    String dump(PackageManager pm, boolean shorter, SyncAdapterStateFetcher appStates) {
         StringBuilder sb = new StringBuilder();
         sb.append("JobId=").append(jobId)
                 .append(" ")
@@ -385,8 +387,18 @@
         if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
             sb.append(" EXPEDITED");
         }
-        if (isAppStandbyExempted) {
-            sb.append(" STANDBY-EXEMPTED");
+        switch (syncExemptionFlag) {
+            case ContentResolver.SYNC_EXEMPTION_NONE:
+                break;
+            case ContentResolver.SYNC_EXEMPTION_ACTIVE:
+                sb.append(" STANDBY-EXEMPTED");
+                break;
+            case ContentResolver.SYNC_EXEMPTION_ACTIVE_WITH_TEMP:
+                sb.append(" STANDBY-EXEMPTED(TOP)");
+                break;
+            default:
+                sb.append(" ExemptionFlag=" + syncExemptionFlag);
+                break;
         }
         sb.append(" Reason=");
         sb.append(reasonToString(pm, reason));
@@ -397,21 +409,31 @@
             SyncManager.formatDurationHMS(sb, flexMillis);
             sb.append(")");
         }
+        if (retries > 0) {
+            sb.append(" Retries=");
+            sb.append(retries);
+        }
         if (!shorter) {
             sb.append(" Owner={");
             UserHandle.formatUid(sb, owningUid);
             sb.append(" ");
             sb.append(owningPackage);
+            if (appStates != null) {
+                sb.append(" [");
+                sb.append(appStates.getStandbyBucket(
+                        UserHandle.getUserId(owningUid), owningPackage));
+                sb.append("]");
+
+                if (appStates.isAppActive(owningUid)) {
+                    sb.append(" [ACTIVE]");
+                }
+            }
             sb.append("}");
             if (!extras.keySet().isEmpty()) {
                 sb.append(" ");
                 extrasToStringBuilder(extras, sb);
             }
         }
-        if (retries > 0) {
-            sb.append(" Retries=");
-            sb.append(retries);
-        }
         return sb.toString();
     }
 
@@ -464,6 +486,10 @@
         return extras.getBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, false);
     }
 
+    boolean isAppStandbyExempted() {
+        return syncExemptionFlag != ContentResolver.SYNC_EXEMPTION_NONE;
+    }
+
     static void extrasToStringBuilder(Bundle bundle, StringBuilder sb) {
         if (bundle == null) {
             sb.append("null");
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 8b67b7a..6081af8 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -22,6 +22,7 @@
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
+import android.content.ContentResolver.SyncExemption;
 import android.content.Context;
 import android.content.ISyncStatusObserver;
 import android.content.PeriodicSync;
@@ -341,7 +342,7 @@
 
         /** Called when a sync is needed on an account(s) due to some change in state. */
         public void onSyncRequest(EndPoint info, int reason, Bundle extras,
-                boolean exemptFromAppStandby);
+                @SyncExemption int syncExemptionFlag);
     }
 
     interface PeriodicSyncAddedListener {
@@ -647,7 +648,7 @@
     }
 
     public void setSyncAutomatically(Account account, int userId, String providerName,
-                                     boolean sync) {
+                                     boolean sync, @SyncExemption int syncExemptionFlag) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Slog.d(TAG, "setSyncAutomatically: " + /* account + */" provider " + providerName
                     + ", user " + userId + " -> " + sync);
@@ -677,7 +678,7 @@
         if (sync) {
             requestSync(account, userId, SyncOperation.REASON_SYNC_AUTO, providerName,
                     new Bundle(),
-                    /* exemptFromAppStandby=*/ false);
+                    syncExemptionFlag);
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
         queueBackup();
@@ -739,7 +740,7 @@
         }
         if (syncable == AuthorityInfo.SYNCABLE) {
             requestSync(aInfo, SyncOperation.REASON_IS_SYNCABLE, new Bundle(),
-                    /*exemptFromAppStandby=*/ false); // Or the caller FG state?
+                    ContentResolver.SYNC_EXEMPTION_NONE);
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
     }
@@ -900,7 +901,8 @@
         return true;
     }
 
-    public void setMasterSyncAutomatically(boolean flag, int userId) {
+    public void setMasterSyncAutomatically(boolean flag, int userId,
+            @SyncExemption int syncExemptionFlag) {
         synchronized (mAuthorities) {
             Boolean auto = mMasterSyncAutomatically.get(userId);
             if (auto != null && auto.equals(flag)) {
@@ -912,7 +914,7 @@
         if (flag) {
             requestSync(null, userId, SyncOperation.REASON_MASTER_SYNC_AUTO, null,
                     new Bundle(),
-                    /*exemptFromAppStandby=*/ false); // Or the caller FG state?
+                    syncExemptionFlag);
         }
         reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS);
         mContext.sendBroadcast(ContentResolver.ACTION_SYNC_CONN_STATUS_CHANGED);
@@ -2046,7 +2048,8 @@
                 String value = c.getString(c.getColumnIndex("value"));
                 if (name == null) continue;
                 if (name.equals("listen_for_tickles")) {
-                    setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0);
+                    setMasterSyncAutomatically(value == null || Boolean.parseBoolean(value), 0,
+                            ContentResolver.SYNC_EXEMPTION_NONE);
                 } else if (name.startsWith("sync_provider_")) {
                     String provider = name.substring("sync_provider_".length(),
                             name.length());
@@ -2143,11 +2146,11 @@
     }
 
     private void requestSync(AuthorityInfo authorityInfo, int reason, Bundle extras,
-            boolean exemptFromAppStandby) {
+            @SyncExemption int syncExemptionFlag) {
         if (android.os.Process.myUid() == android.os.Process.SYSTEM_UID
                 && mSyncRequestListener != null) {
             mSyncRequestListener.onSyncRequest(authorityInfo.target, reason, extras,
-                    exemptFromAppStandby);
+                    syncExemptionFlag);
         } else {
             SyncRequest.Builder req =
                     new SyncRequest.Builder()
@@ -2159,7 +2162,7 @@
     }
 
     private void requestSync(Account account, int userId, int reason, String authority,
-            Bundle extras, boolean exemptFromAppStandby) {
+            Bundle extras, @SyncExemption int syncExemptionFlag) {
         // If this is happening in the system process, then call the syncrequest listener
         // to make a request back to the SyncManager directly.
         // If this is probably a test instance, then call back through the ContentResolver
@@ -2168,7 +2171,7 @@
                 && mSyncRequestListener != null) {
             mSyncRequestListener.onSyncRequest(
                     new EndPoint(account, authority, userId),
-                    reason, extras, exemptFromAppStandby);
+                    reason, extras, syncExemptionFlag);
         } else {
             ContentResolver.requestSync(account, authority, extras);
         }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index 711d40b..4313d17 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -60,14 +60,8 @@
         int[] backlightRange = resources.getIntArray(
                 com.android.internal.R.array.config_screenBrightnessBacklight);
 
-        float[] minimumBrightnessCurveLux = getLuxLevels(resources.getIntArray(
-                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux));
-        float[] minimumBrightnessCurveNits = getFloatArray(resources.obtainTypedArray(
-                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits));
-
         if (isValidMapping(nitsRange, backlightRange)
-                && isValidMapping(luxLevels, brightnessLevelsNits)
-                && isValidMapping(minimumBrightnessCurveLux, minimumBrightnessCurveNits)) {
+                && isValidMapping(luxLevels, brightnessLevelsNits)) {
             int minimumBacklight = resources.getInteger(
                     com.android.internal.R.integer.config_screenBrightnessSettingMinimum);
             int maximumBacklight = resources.getInteger(
@@ -79,8 +73,7 @@
             }
             BrightnessConfiguration.Builder builder = new BrightnessConfiguration.Builder();
             builder.setCurve(luxLevels, brightnessLevelsNits);
-            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange,
-                    minimumBrightnessCurveLux, minimumBrightnessCurveNits);
+            return new PhysicalMappingStrategy(builder.build(), nitsRange, backlightRange);
         } else if (isValidMapping(luxLevels, brightnessLevelsBacklight)) {
             return new SimpleMappingStrategy(luxLevels, brightnessLevelsBacklight);
         } else {
@@ -455,11 +448,8 @@
         private float mUserLux;
         private float mUserBrightness;
 
-        private final Spline mMinimumBrightnessCurve;
-
         public PhysicalMappingStrategy(BrightnessConfiguration config,
-                float[] nits, int[] backlight, float[] minimumBrightnessCurveLux,
-                float[] minimumBrightnessCurveNits) {
+                float[] nits, int[] backlight) {
             Preconditions.checkArgument(nits.length != 0 && backlight.length != 0,
                     "Nits and backlight arrays must not be empty!");
             Preconditions.checkArgument(nits.length == backlight.length,
@@ -479,9 +469,6 @@
                 normalizedBacklight[i] = normalizeAbsoluteBrightness(backlight[i]);
             }
 
-            mMinimumBrightnessCurve = Spline.createSpline(
-                minimumBrightnessCurveLux, minimumBrightnessCurveNits);
-
             mNitsToBacklightSpline = createSpline(nits, normalizedBacklight);
             mBacklightToNitsSpline = createSpline(normalizedBacklight, nits);
 
@@ -497,7 +484,7 @@
             if (config.equals(mConfig)) {
                 return false;
             }
-            validateBrightnessConfiguration(config);
+
             Pair<float[], float[]> curve = config.getCurve();
             mBrightnessSpline = createSpline(curve.first /*lux*/, curve.second /*nits*/);
             mConfig = config;
@@ -562,24 +549,5 @@
             pw.println("  mUserLux=" + mUserLux);
             pw.println("  mUserBrightness=" + mUserBrightness);
         }
-
-        private void validateBrightnessConfiguration(BrightnessConfiguration config) {
-            Pair<float[], float[]> curve = config.getCurve();
-            Spline brightnessSpline = Spline.createSpline(curve.first, curve.second);
-            if (isBrightnessSplineTooDark(brightnessSpline)) {
-                throw new IllegalArgumentException("brightness curve is too dark");
-            }
-        }
-
-        private boolean isBrightnessSplineTooDark(Spline brightnessSpline) {
-            float[] lux = mDefaultConfig.getCurve().first;
-            for (int i = 0; i < lux.length; i++) {
-                if (brightnessSpline.interpolate(lux[i]) <
-                        mMinimumBrightnessCurve.interpolate(lux[i])) {
-                    return true;
-                }
-            }
-            return false;
-        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c4b2b5e..c7ae1f4 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -695,6 +695,27 @@
         }
     }
 
+    private void setSaturationLevelInternal(float level) {
+        if (level < 0 || level > 1) {
+            throw new IllegalArgumentException("Saturation level must be between 0 and 1");
+        }
+        float[] matrix = (level == 1.0f ? null : computeSaturationMatrix(level));
+        DisplayTransformManager dtm = LocalServices.getService(DisplayTransformManager.class);
+        dtm.setColorMatrix(DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION, matrix);
+    }
+
+    private static float[] computeSaturationMatrix(float saturation) {
+        float desaturation = 1.0f - saturation;
+        float[] luminance = {0.231f * desaturation, 0.715f * desaturation, 0.072f * desaturation};
+        float[] matrix = {
+            luminance[0] + saturation, luminance[0], luminance[0], 0,
+            luminance[1], luminance[1] + saturation, luminance[1], 0,
+            luminance[2], luminance[2], luminance[2] + saturation, 0,
+            0, 0, 0, 1
+        };
+        return matrix;
+    }
+
     private int createVirtualDisplayInternal(IVirtualDisplayCallback callback,
             IMediaProjection projection, int callingUid, String packageName, String name, int width,
             int height, int densityDpi, Surface surface, int flags, String uniqueId) {
@@ -1687,6 +1708,19 @@
         }
 
         @Override // Binder call
+        public void setSaturationLevel(float level) {
+            mContext.enforceCallingOrSelfPermission(
+                   Manifest.permission.CONTROL_DISPLAY_SATURATION,
+                   "Permission required to set display saturation level");
+            final long token = Binder.clearCallingIdentity();
+            try {
+                setSaturationLevelInternal(level);
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+        }
+
+        @Override // Binder call
         public int createVirtualDisplay(IVirtualDisplayCallback callback,
                 IMediaProjection projection, String packageName, String name,
                 int width, int height, int densityDpi, Surface surface, int flags,
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 000fcf3..a94f049 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -17,7 +17,6 @@
 package com.android.server.display;
 
 import android.app.ActivityManager;
-import android.app.IActivityManager;
 import android.opengl.Matrix;
 import android.os.IBinder;
 import android.os.Parcel;
@@ -28,7 +27,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import com.android.internal.annotations.GuardedBy;
-
 import com.android.internal.app.ColorDisplayController;
 import java.util.Arrays;
 
@@ -46,6 +44,10 @@
      */
     public static final int LEVEL_COLOR_MATRIX_NIGHT_DISPLAY = 100;
     /**
+     * Color transform level used to adjust the color saturation of the display.
+     */
+    public static final int LEVEL_COLOR_MATRIX_SATURATION = 150;
+    /**
      * Color transform level used by A11y services to make the display monochromatic.
      */
     public static final int LEVEL_COLOR_MATRIX_GRAYSCALE = 200;
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index a52dd0b..8be2c9e 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -18,10 +18,10 @@
 
 import android.content.Context;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
+import android.hardware.biometrics.BiometricDialog;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.hardware.fingerprint.Fingerprint;
-import android.hardware.fingerprint.FingerprintDialog;
 import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -46,22 +46,22 @@
     public static final int LOCKOUT_PERMANENT = 2;
 
     // Callback mechanism received from the client
-    // (FingerprintDialog -> FingerprintManager -> FingerprintService -> AuthenticationClient)
-    private IFingerprintDialogReceiver mDialogReceiverFromClient;
+    // (BiometricDialog -> FingerprintManager -> FingerprintService -> AuthenticationClient)
+    private IBiometricDialogReceiver mDialogReceiverFromClient;
     private Bundle mBundle;
     private IStatusBarService mStatusBarService;
     private boolean mInLockout;
     private final FingerprintManager mFingerprintManager;
     protected boolean mDialogDismissed;
 
-    // Receives events from SystemUI
-    protected IFingerprintDialogReceiver mDialogReceiver = new IFingerprintDialogReceiver.Stub() {
+    // Receives events from SystemUI and handles them before forwarding them to FingerprintDialog
+    protected IBiometricDialogReceiver mDialogReceiver = new IBiometricDialogReceiver.Stub() {
         @Override // binder call
         public void onDialogDismissed(int reason) {
             if (mBundle != null && mDialogReceiverFromClient != null) {
                 try {
                     mDialogReceiverFromClient.onDialogDismissed(reason);
-                    if (reason == FingerprintDialog.DISMISSED_REASON_USER_CANCEL) {
+                    if (reason == BiometricDialog.DISMISSED_REASON_USER_CANCEL) {
                         onError(FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED,
                                 0 /* vendorCode */);
                     }
@@ -88,7 +88,7 @@
     public AuthenticationClient(Context context, long halDeviceId, IBinder token,
             IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
             boolean restricted, String owner, Bundle bundle,
-            IFingerprintDialogReceiver dialogReceiver, IStatusBarService statusBarService) {
+            IBiometricDialogReceiver dialogReceiver, IStatusBarService statusBarService) {
         super(context, halDeviceId, token, receiver, targetUserId, groupId, restricted, owner);
         mOpId = opId;
         mBundle = bundle;
@@ -299,7 +299,7 @@
             // If the user already cancelled authentication (via some interaction with the
             // dialog, we do not need to hide it since it's already hidden.
             // If the device is in lockout, don't hide the dialog - it will automatically hide
-            // after FingerprintDialog.HIDE_DIALOG_DELAY
+            // after BiometricDialog.HIDE_DIALOG_DELAY
             if (mBundle != null && !mDialogDismissed && !mInLockout) {
                 try {
                     mStatusBarService.hideFingerprintDialog();
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index fc8aace..262a2f8 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.MANAGE_FINGERPRINT;
 import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
+import static android.Manifest.permission.USE_BIOMETRIC;
 import static android.Manifest.permission.USE_FINGERPRINT;
 import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 
@@ -37,12 +38,12 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
 import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
 import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintClientActiveCallback;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
 import android.hardware.fingerprint.IFingerprintService;
 import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
 import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -61,7 +62,6 @@
 import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.security.KeyStore;
@@ -778,7 +778,11 @@
      */
     private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
             int pid, int userId) {
-        checkPermission(USE_FINGERPRINT);
+        if (getContext().checkCallingPermission(USE_FINGERPRINT)
+                != PackageManager.PERMISSION_GRANTED) {
+            checkPermission(USE_BIOMETRIC);
+        }
+
         if (isKeyguard(opPackageName)) {
             return true; // Keyguard is always allowed
         }
@@ -845,7 +849,7 @@
 
     private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
                 IFingerprintServiceReceiver receiver, int flags, boolean restricted,
-                String opPackageName, Bundle bundle, IFingerprintDialogReceiver dialogReceiver) {
+                String opPackageName, Bundle bundle, IBiometricDialogReceiver dialogReceiver) {
         updateActiveGroup(groupId, opPackageName);
 
         if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
@@ -1156,7 +1160,7 @@
         public void authenticate(final IBinder token, final long opId, final int groupId,
                 final IFingerprintServiceReceiver receiver, final int flags,
                 final String opPackageName, final Bundle bundle,
-                final IFingerprintDialogReceiver dialogReceiver) {
+                final IBiometricDialogReceiver dialogReceiver) {
             final int callingUid = Binder.getCallingUid();
             final int callingPid = Binder.getCallingPid();
             final int callingUserId = UserHandle.getCallingUserId();
@@ -1483,10 +1487,7 @@
                 userId = getUserOrWorkProfileId(clientPackage, userId);
                 if (userId != mCurrentUserId) {
                     File baseDir;
-                    if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1
-                            && !SystemProperties.getBoolean(
-                                "ro.treble.supports_vendor_data", false)) {
-                        // TODO(b/72405644) remove the override when possible.
+                    if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
                         baseDir = Environment.getUserSystemDirectory(userId);
                     } else {
                         baseDir = Environment.getDataVendorDeDirectory(userId);
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index abe55bb..8365fd2 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -160,7 +160,7 @@
     private static boolean isRelaxedSatisfied(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
         // Only consider doing this for prefetching jobs
-        if ((jobStatus.getJob().getFlags() & JobInfo.FLAG_IS_PREFETCH) == 0) {
+        if (!jobStatus.getJob().isPrefetch()) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 1dbcfd6..40d2a3a 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -19,7 +19,6 @@
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.app.AlarmManager;
-import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -101,18 +100,19 @@
 
     final class IdlenessTracker extends BroadcastReceiver {
         private AlarmManager mAlarm;
-        private PendingIntent mIdleTriggerIntent;
-        boolean mIdle;
-        boolean mScreenOn;
+
+        // After construction, mutations of idle/screen-on state will only happen
+        // on the main looper thread, either in onReceive() or in an alarm callback.
+        private boolean mIdle;
+        private boolean mScreenOn;
+
+        private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
+            handleIdleTrigger();
+        };
 
         public IdlenessTracker() {
             mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
 
-            Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE)
-                    .setPackage("android")
-                    .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-            mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
-
             // At boot we presume that the user has just "interacted" with the
             // device in some meaningful way.
             mIdle = false;
@@ -150,7 +150,7 @@
                 }
                 mScreenOn = true;
                 //cancel the alarm
-                mAlarm.cancel(mIdleTriggerIntent);
+                mAlarm.cancel(mIdleAlarmListener);
                 if (mIdle) {
                 // possible transition to not-idle
                     mIdle = false;
@@ -169,20 +169,24 @@
                 }
                 mScreenOn = false;
                 mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        when, mIdleWindowSlop, mIdleTriggerIntent);
+                        when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
             } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
-                // idle time starts now. Do not set mIdle if screen is on.
-                if (!mIdle && !mScreenOn) {
-                    if (DEBUG) {
-                        Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
-                    }
-                    mIdle = true;
-                    reportNewIdleState(mIdle);
-                } else {
-                    if (DEBUG) {
-                        Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
-                                + mIdle + " screen=" + mScreenOn);
-                    }
+                handleIdleTrigger();
+            }
+        }
+
+        private void handleIdleTrigger() {
+            // idle time starts now. Do not set mIdle if screen is on.
+            if (!mIdle && !mScreenOn) {
+                if (DEBUG) {
+                    Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
+                }
+                mIdle = true;
+                reportNewIdleState(mIdle);
+            } else {
+                if (DEBUG) {
+                    Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
+                            + mIdle + " screen=" + mScreenOn);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3374b30..5955c9c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -2513,6 +2513,8 @@
          * this handler.
          */
         private void handleInitialize() {
+            native_init_once();
+
             /*
              * A cycle of native_init() and native_cleanup() is needed so that callbacks are
              * registered after bootup even when location is disabled.
@@ -2576,9 +2578,9 @@
             // register for connectivity change events, this is equivalent to the deprecated way of
             // registering for CONNECTIVITY_ACTION broadcasts
             NetworkRequest.Builder networkRequestBuilder = new NetworkRequest.Builder();
-            networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
-            networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
-            networkRequestBuilder.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH);
+            networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+            networkRequestBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+            networkRequestBuilder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
             NetworkRequest networkRequest = networkRequestBuilder.build();
             mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback);
 
@@ -2899,6 +2901,8 @@
 
     private static native boolean native_is_gnss_configuration_supported();
 
+    private static native void native_init_once();
+
     private native boolean native_init();
 
     private native void native_cleanup();
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index f617964..4b58d53 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -2079,11 +2079,6 @@
     }
 
     @Override
-    public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
-        return mRecoverableKeyStoreManager.generateAndStoreKey(alias);
-    }
-
-    @Override
     public @Nullable String generateKey(@NonNull String alias) throws RemoteException {
         return mRecoverableKeyStoreManager.generateKey(alias);
     }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
index a87adbd..b0afac2 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java
@@ -19,10 +19,12 @@
 import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN;
 
 import android.annotation.Nullable;
+import android.annotation.NonNull;
 import android.content.Context;
-import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.util.Log;
 
@@ -77,6 +79,7 @@
     private final PlatformKeyManager mPlatformKeyManager;
     private final RecoverySnapshotStorage mRecoverySnapshotStorage;
     private final RecoverySnapshotListenersStorage mSnapshotListenersStorage;
+    private final TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
 
     public static KeySyncTask newInstance(
             Context context,
@@ -96,7 +99,8 @@
                 credentialType,
                 credential,
                 credentialUpdated,
-                PlatformKeyManager.getInstance(context, recoverableKeyStoreDb));
+                PlatformKeyManager.getInstance(context, recoverableKeyStoreDb),
+                new TestOnlyInsecureCertificateHelper());
     }
 
     /**
@@ -108,6 +112,7 @@
      * @param credential The credential, encoded as a {@link String}.
      * @param credentialUpdated signals weather credentials were updated.
      * @param platformKeyManager platform key manager
+     * @param TestOnlyInsecureCertificateHelper utility class used for end-to-end tests
      */
     @VisibleForTesting
     KeySyncTask(
@@ -118,7 +123,8 @@
             int credentialType,
             String credential,
             boolean credentialUpdated,
-            PlatformKeyManager platformKeyManager) {
+            PlatformKeyManager platformKeyManager,
+            TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) {
         mSnapshotListenersStorage = recoverySnapshotListenersStorage;
         mRecoverableKeyStoreDb = recoverableKeyStoreDb;
         mUserId = userId;
@@ -127,6 +133,7 @@
         mCredentialUpdated = credentialUpdated;
         mPlatformKeyManager = platformKeyManager;
         mRecoverySnapshotStorage = snapshotStorage;
+        mTestOnlyInsecureCertificateHelper = TestOnlyInsecureCertificateHelper;
     }
 
     @Override
@@ -185,8 +192,13 @@
         }
 
         PublicKey publicKey;
+        String rootCertAlias =
+                mRecoverableKeyStoreDb.getActiveRootOfTrust(mUserId, recoveryAgentUid);
+        rootCertAlias = mTestOnlyInsecureCertificateHelper
+                .getDefaultCertificateAliasIfEmpty(rootCertAlias);
+
         CertPath certPath = mRecoverableKeyStoreDb.getRecoveryServiceCertPath(mUserId,
-                recoveryAgentUid);
+                recoveryAgentUid, rootCertAlias);
         if (certPath != null) {
             Log.d(TAG, "Using the public key in stored CertPath for syncing");
             publicKey = certPath.getCertificates().get(0).getPublicKey();
@@ -206,6 +218,20 @@
             return;
         }
 
+        if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(rootCertAlias)) {
+            Log.w(TAG, "Insecure root certificate is used by recovery agent "
+                    + recoveryAgentUid);
+            if (mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(
+                    mCredentialType, mCredential)) {
+                Log.w(TAG, "Whitelisted credential is used to generate snapshot by "
+                        + "recovery agent "+ recoveryAgentUid);
+            } else {
+                Log.w(TAG, "Non whitelisted credential is used to generate recovery snapshot by "
+                        + recoveryAgentUid + " - ignore attempt.");
+                return; // User secret will not be used.
+            }
+        }
+
         byte[] salt = generateSalt();
         byte[] localLskfHash = hashCredentials(salt, mCredential);
 
@@ -225,6 +251,10 @@
             return;
         }
 
+        // Only include insecure key material for test
+        if (mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(rootCertAlias)) {
+            rawKeys = mTestOnlyInsecureCertificateHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+        }
         SecretKey recoveryKey;
         try {
             recoveryKey = generateRecoveryKey();
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
index e0487ea..ff4c678 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java
@@ -31,7 +31,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.os.Binder;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.os.UserHandle;
@@ -39,7 +38,6 @@
 import android.security.keystore.recovery.KeyChainSnapshot;
 import android.security.keystore.recovery.RecoveryCertPath;
 import android.security.keystore.recovery.RecoveryController;
-import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.security.KeyStore;
 import android.util.ArrayMap;
@@ -100,8 +98,8 @@
     private final RecoverableKeyGenerator mRecoverableKeyGenerator;
     private final RecoverySnapshotStorage mSnapshotStorage;
     private final PlatformKeyManager mPlatformKeyManager;
-    private final KeyStore mKeyStore;
     private final ApplicationKeyStorage mApplicationKeyStorage;
+    private final TestOnlyInsecureCertificateHelper mTestCertHelper;
 
     /**
      * Returns a new or existing instance.
@@ -126,14 +124,14 @@
 
             mInstance = new RecoverableKeyStoreManager(
                     context.getApplicationContext(),
-                    keystore,
                     db,
                     new RecoverySessionStorage(),
                     Executors.newSingleThreadExecutor(),
-                    new RecoverySnapshotStorage(),
+                    RecoverySnapshotStorage.newInstance(),
                     new RecoverySnapshotListenersStorage(),
                     platformKeyManager,
-                    applicationKeyStorage);
+                    applicationKeyStorage,
+                    new TestOnlyInsecureCertificateHelper());
         }
         return mInstance;
     }
@@ -141,16 +139,15 @@
     @VisibleForTesting
     RecoverableKeyStoreManager(
             Context context,
-            KeyStore keystore,
             RecoverableKeyStoreDb recoverableKeyStoreDb,
             RecoverySessionStorage recoverySessionStorage,
             ExecutorService executorService,
             RecoverySnapshotStorage snapshotStorage,
             RecoverySnapshotListenersStorage listenersStorage,
             PlatformKeyManager platformKeyManager,
-            ApplicationKeyStorage applicationKeyStorage) {
+            ApplicationKeyStorage applicationKeyStorage,
+            TestOnlyInsecureCertificateHelper TestOnlyInsecureCertificateHelper) {
         mContext = context;
-        mKeyStore = keystore;
         mDatabase = recoverableKeyStoreDb;
         mRecoverySessionStorage = recoverySessionStorage;
         mExecutorService = executorService;
@@ -158,6 +155,7 @@
         mSnapshotStorage = snapshotStorage;
         mPlatformKeyManager = platformKeyManager;
         mApplicationKeyStorage = applicationKeyStorage;
+        mTestCertHelper = TestOnlyInsecureCertificateHelper;
 
         try {
             mRecoverableKeyGenerator = RecoverableKeyGenerator.newInstance(mDatabase);
@@ -176,6 +174,21 @@
         checkRecoverKeyStorePermission();
         int userId = UserHandle.getCallingUserId();
         int uid = Binder.getCallingUid();
+        rootCertificateAlias
+                = mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
+
+        // Always set active alias to the argument of the last call to initRecoveryService method,
+        // even if cert file is incorrect.
+        String activeRootAlias = mDatabase.getActiveRootOfTrust(userId, uid);
+        if (activeRootAlias == null) {
+            Log.d(TAG, "Root of trust for recovery agent + " + uid
+                + " is assigned for the first time to " + rootCertificateAlias);
+            mDatabase.setActiveRootOfTrust(userId, uid, rootCertificateAlias);
+        } else if (!activeRootAlias.equals(rootCertificateAlias)) {
+            Log.i(TAG, "Root of trust for recovery agent " + uid + " is changed to "
+                    + rootCertificateAlias + " from  " + activeRootAlias);
+            mDatabase.setActiveRootOfTrust(userId, uid, rootCertificateAlias);
+        }
 
         CertXml certXml;
         try {
@@ -194,7 +207,7 @@
 
         // Check serial number
         long newSerial = certXml.getSerial();
-        Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid);
+        Long oldSerial = mDatabase.getRecoveryServiceCertSerial(userId, uid, rootCertificateAlias);
         if (oldSerial != null && oldSerial >= newSerial) {
             if (oldSerial == newSerial) {
                 Log.i(TAG, "The cert file serial number is the same, so skip updating.");
@@ -207,7 +220,8 @@
 
         // Randomly choose and validate an endpoint certificate from the list
         CertPath certPath;
-        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
+        X509Certificate rootCert =
+                mTestCertHelper.getRootCertificate(rootCertificateAlias);
         try {
             Log.d(TAG, "Getting and validating a random endpoint certificate");
             certPath = certXml.getRandomEndpointCert(rootCert);
@@ -217,12 +231,20 @@
                     ERROR_INVALID_CERTIFICATE, "Failed to validate certificate.");
         }
 
+        boolean wasInitialized = mDatabase.getRecoveryServiceCertPath(userId, uid,
+                rootCertificateAlias) != null;
+
         // Save the chosen and validated certificate into database
         try {
             Log.d(TAG, "Saving the randomly chosen endpoint certificate to database");
-            if (mDatabase.setRecoveryServiceCertPath(userId, uid, certPath) > 0) {
-                mDatabase.setRecoveryServiceCertSerial(userId, uid, newSerial);
-                mDatabase.setShouldCreateSnapshot(userId, uid, true);
+            if (mDatabase.setRecoveryServiceCertPath(userId, uid, rootCertificateAlias,
+                    certPath) > 0) {
+                mDatabase.setRecoveryServiceCertSerial(userId, uid, rootCertificateAlias,
+                        newSerial);
+                if (wasInitialized) {
+                    Log.i(TAG, "This is a certificate change. Snapshot pending.");
+                    mDatabase.setShouldCreateSnapshot(userId, uid, true);
+                }
                 mDatabase.setCounterId(userId, uid, new SecureRandom().nextLong());
             }
         } catch (CertificateEncodingException e) {
@@ -248,9 +270,8 @@
             @NonNull byte[] recoveryServiceSigFile)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-        if (rootCertificateAlias == null) {
-            Log.e(TAG, "rootCertificateAlias is null");
-        }
+        rootCertificateAlias =
+                mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
         Preconditions.checkNotNull(recoveryServiceCertFile, "recoveryServiceCertFile is null");
         Preconditions.checkNotNull(recoveryServiceSigFile, "recoveryServiceSigFile is null");
 
@@ -264,7 +285,8 @@
                     ERROR_BAD_CERTIFICATE_FORMAT, "Failed to parse the sig file.");
         }
 
-        X509Certificate rootCert = getRootCertificate(rootCertificateAlias);
+        X509Certificate rootCert =
+                mTestCertHelper.getRootCertificate(rootCertificateAlias);
         try {
             sigXml.verifyFileSignature(rootCert, recoveryServiceCertFile);
         } catch (CertValidationException e) {
@@ -504,9 +526,8 @@
             @NonNull List<KeyChainProtectionParams> secrets)
             throws RemoteException {
         checkRecoverKeyStorePermission();
-        if (rootCertificateAlias == null) {
-            Log.e(TAG, "rootCertificateAlias is null");
-        }
+        rootCertificateAlias =
+                mTestCertHelper.getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
         Preconditions.checkNotNull(sessionId, "invalid session");
         Preconditions.checkNotNull(verifierCertPath, "verifierCertPath is null");
         Preconditions.checkNotNull(vaultParams, "vaultParams is null");
@@ -521,7 +542,8 @@
         }
 
         try {
-            CertUtils.validateCertPath(getRootCertificate(rootCertificateAlias), certPath);
+            CertUtils.validateCertPath(
+                    mTestCertHelper.getRootCertificate(rootCertificateAlias), certPath);
         } catch (CertValidationException e) {
             Log.e(TAG, "Failed to validate the given cert path", e);
             throw new ServiceSpecificException(ERROR_INVALID_CERTIFICATE, e.getMessage());
@@ -650,40 +672,6 @@
     }
 
     /**
-     * Deprecated
-     * Generates a key named {@code alias} in the recoverable store for the calling uid. Then
-     * returns the raw key material.
-     *
-     * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes.
-     *
-     * @deprecated
-     * @hide
-     */
-    public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException {
-        checkRecoverKeyStorePermission();
-        int uid = Binder.getCallingUid();
-        int userId = UserHandle.getCallingUserId();
-
-        PlatformEncryptionKey encryptionKey;
-        try {
-            encryptionKey = mPlatformKeyManager.getEncryptKey(userId);
-        } catch (NoSuchAlgorithmException e) {
-            // Impossible: all algorithms must be supported by AOSP
-            throw new RuntimeException(e);
-        } catch (KeyStoreException | UnrecoverableKeyException e) {
-            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
-        } catch (InsecureUserException e) {
-            throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage());
-        }
-
-        try {
-            return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias);
-        } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) {
-            throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage());
-        }
-    }
-
-    /**
      * Destroys the session with the given {@code sessionId}.
      */
     public void closeSession(@NonNull String sessionId) throws RemoteException {
@@ -947,21 +935,6 @@
         }
     }
 
-    private X509Certificate getRootCertificate(String rootCertificateAlias) throws RemoteException {
-        if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
-            // Use the default Google Key Vault Service CA certificate if the alias is not provided
-            rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
-        }
-
-        X509Certificate rootCertificate =
-                TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
-        if (rootCertificate == null) {
-            throw new ServiceSpecificException(
-                    ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
-        }
-        return rootCertificate;
-    }
-
     private void checkRecoverKeyStorePermission() {
         mContext.enforceCallingOrSelfPermission(
                 Manifest.permission.RECOVER_KEYSTORE,
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
new file mode 100644
index 0000000..490f733
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java
@@ -0,0 +1,103 @@
+/*
+ * 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.locksettings.recoverablekeystore;
+
+import static android.security.keystore.recovery.RecoveryController.ERROR_INVALID_CERTIFICATE;
+
+import com.android.internal.widget.LockPatternUtils;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.recovery.TrustedRootCertificates;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import javax.crypto.SecretKey;
+
+/**
+ * The class provides helper methods to support end-to-end test with insecure certificate.
+ */
+public class TestOnlyInsecureCertificateHelper {
+    private static final String TAG = "TestCertHelper";
+
+    /**
+     * Constructor for the helper class.
+     */
+    public TestOnlyInsecureCertificateHelper() {
+    }
+
+    /**
+     * Returns a root certificate installed in the system for given alias.
+     * Returns default secure certificate if alias is empty or null.
+     * Can return insecure certificate for its alias.
+     */
+    public @NonNull X509Certificate
+            getRootCertificate(String rootCertificateAlias) throws RemoteException {
+        rootCertificateAlias = getDefaultCertificateAliasIfEmpty(rootCertificateAlias);
+        if (isTestOnlyCertificate(rootCertificateAlias)) {
+            return TrustedRootCertificates.getTestOnlyInsecureCertificate();
+        }
+
+        X509Certificate rootCertificate =
+                TrustedRootCertificates.getRootCertificate(rootCertificateAlias);
+        if (rootCertificate == null) {
+            throw new ServiceSpecificException(
+                    ERROR_INVALID_CERTIFICATE, "The provided root certificate alias is invalid");
+        }
+        return rootCertificate;
+    }
+
+    public @NonNull String getDefaultCertificateAliasIfEmpty(
+            @Nullable String rootCertificateAlias) {
+        if (rootCertificateAlias == null || rootCertificateAlias.isEmpty()) {
+            Log.e(TAG, "rootCertificateAlias is null or empty - use secure default value");
+            // Use the default Google Key Vault Service CA certificate if the alias is not provided
+            rootCertificateAlias = TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+        }
+        return rootCertificateAlias;
+    }
+
+    public boolean isTestOnlyCertificate(String rootCertificateAlias) {
+        return TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS
+                .equals(rootCertificateAlias);
+    }
+
+    public boolean doesCredentailSupportInsecureMode(int credentialType, String credential) {
+        return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD)
+            && (credential != null)
+            && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX);
+    }
+
+    public Map<String, SecretKey> keepOnlyWhitelistedInsecureKeys(Map<String, SecretKey> rawKeys) {
+        if (rawKeys == null) {
+            return null;
+        }
+        Map<String, SecretKey> filteredKeys = new HashMap<>();
+        for (Map.Entry<String, SecretKey> entry : rawKeys.entrySet()) {
+            String alias = entry.getKey();
+            if (alias != null
+                    && alias.startsWith(TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX)) {
+                filteredKeys.put(entry.getKey(), entry.getValue());
+                Log.d(TAG, "adding key with insecure alias " + alias + " to the recovery snapshot");
+            }
+        }
+        return filteredKeys;
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
new file mode 100644
index 0000000..f789155
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotDeserializer.java
@@ -0,0 +1,409 @@
+/*
+ * 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.locksettings.recoverablekeystore.serialization;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERTIFICATE_FACTORY_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Base64;
+import android.util.Xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Deserializes a {@link android.security.keystore.recovery.KeyChainSnapshot} instance from XML.
+ */
+public class KeyChainSnapshotDeserializer {
+
+    /**
+     * Deserializes a {@link KeyChainSnapshot} instance from the XML in the {@code inputStream}.
+     *
+     * @throws IOException if there is an IO error reading from the stream.
+     * @throws KeyChainSnapshotParserException if the XML does not conform to the expected XML for
+     *     a snapshot.
+     */
+    public static KeyChainSnapshot deserialize(InputStream inputStream)
+            throws KeyChainSnapshotParserException, IOException {
+        try {
+            return deserializeInternal(inputStream);
+        } catch (XmlPullParserException e) {
+            throw new KeyChainSnapshotParserException("Malformed KeyChainSnapshot XML", e);
+        }
+    }
+
+    private static KeyChainSnapshot deserializeInternal(InputStream inputStream) throws IOException,
+            XmlPullParserException, KeyChainSnapshotParserException {
+        XmlPullParser parser = Xml.newPullParser();
+        parser.setInput(inputStream, OUTPUT_ENCODING);
+
+        parser.nextTag();
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+
+        KeyChainSnapshot.Builder builder = new KeyChainSnapshot.Builder();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+
+            switch (name) {
+                case TAG_SNAPSHOT_VERSION:
+                    builder.setSnapshotVersion(readIntTag(parser, TAG_SNAPSHOT_VERSION));
+                    break;
+
+                case TAG_RECOVERY_KEY_MATERIAL:
+                    builder.setEncryptedRecoveryKeyBlob(
+                            readBlobTag(parser, TAG_RECOVERY_KEY_MATERIAL));
+                    break;
+
+                case TAG_COUNTER_ID:
+                    builder.setCounterId(readLongTag(parser, TAG_COUNTER_ID));
+                    break;
+
+                case TAG_SERVER_PARAMS:
+                    builder.setServerParams(readBlobTag(parser, TAG_SERVER_PARAMS));
+                    break;
+
+                case TAG_MAX_ATTEMPTS:
+                    builder.setMaxAttempts(readIntTag(parser, TAG_MAX_ATTEMPTS));
+                    break;
+
+                case TAG_TRUSTED_HARDWARE_CERT_PATH:
+                    try {
+                        builder.setTrustedHardwareCertPath(
+                                readCertPathTag(parser, TAG_TRUSTED_HARDWARE_CERT_PATH));
+                    } catch (CertificateException e) {
+                        throw new KeyChainSnapshotParserException(
+                                "Could not set trustedHardwareCertPath", e);
+                    }
+                    break;
+
+                case TAG_BACKEND_PUBLIC_KEY:
+                    builder.setTrustedHardwarePublicKey(
+                            readBlobTag(parser, TAG_BACKEND_PUBLIC_KEY));
+                    break;
+
+                case TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST:
+                    builder.setKeyChainProtectionParams(readKeyChainProtectionParamsList(parser));
+                    break;
+
+                case TAG_APPLICATION_KEYS:
+                    builder.setWrappedApplicationKeys(readWrappedApplicationKeys(parser));
+                    break;
+
+                default:
+                    throw new KeyChainSnapshotParserException(String.format(
+                            Locale.US, "Unexpected tag %s in keyChainSnapshot", name));
+            }
+        }
+
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+        try {
+            return builder.build();
+        } catch (NullPointerException e) {
+            throw new KeyChainSnapshotParserException("Failed to build KeyChainSnapshot", e);
+        }
+    }
+
+    private static List<WrappedApplicationKey> readWrappedApplicationKeys(XmlPullParser parser)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
+        ArrayList<WrappedApplicationKey> keys = new ArrayList<>();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+            keys.add(readWrappedApplicationKey(parser));
+        }
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEYS);
+        return keys;
+    }
+
+    private static WrappedApplicationKey readWrappedApplicationKey(XmlPullParser parser)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_APPLICATION_KEY);
+        WrappedApplicationKey.Builder builder = new WrappedApplicationKey.Builder();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+
+            switch (name) {
+                case TAG_ALIAS:
+                    builder.setAlias(readStringTag(parser, TAG_ALIAS));
+                    break;
+
+                case TAG_KEY_MATERIAL:
+                    builder.setEncryptedKeyMaterial(readBlobTag(parser, TAG_KEY_MATERIAL));
+                    break;
+
+                default:
+                    throw new KeyChainSnapshotParserException(String.format(
+                            Locale.US, "Unexpected tag %s in wrappedApplicationKey", name));
+            }
+        }
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_APPLICATION_KEY);
+
+        try {
+            return builder.build();
+        } catch (NullPointerException e) {
+            throw new KeyChainSnapshotParserException("Failed to build WrappedApplicationKey", e);
+        }
+    }
+
+    private static List<KeyChainProtectionParams> readKeyChainProtectionParamsList(
+            XmlPullParser parser) throws IOException, XmlPullParserException,
+            KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+
+        ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList = new ArrayList<>();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+            keyChainProtectionParamsList.add(readKeyChainProtectionParams(parser));
+        }
+
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+        return keyChainProtectionParamsList;
+    }
+
+    private static KeyChainProtectionParams readKeyChainProtectionParams(XmlPullParser parser)
+        throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+
+        KeyChainProtectionParams.Builder builder = new KeyChainProtectionParams.Builder();
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+
+            switch (name) {
+                case TAG_LOCK_SCREEN_UI_TYPE:
+                    builder.setLockScreenUiFormat(readIntTag(parser, TAG_LOCK_SCREEN_UI_TYPE));
+                    break;
+
+                case TAG_USER_SECRET_TYPE:
+                    builder.setUserSecretType(readIntTag(parser, TAG_USER_SECRET_TYPE));
+                    break;
+
+                case TAG_KEY_DERIVATION_PARAMS:
+                    builder.setKeyDerivationParams(readKeyDerivationParams(parser));
+                    break;
+
+                default:
+                    throw new KeyChainSnapshotParserException(String.format(
+                            Locale.US,
+                            "Unexpected tag %s in keyChainProtectionParams",
+                            name));
+
+            }
+        }
+
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+
+        try {
+            return builder.build();
+        } catch (NullPointerException e) {
+            throw new KeyChainSnapshotParserException(
+                    "Failed to build KeyChainProtectionParams", e);
+        }
+    }
+
+    private static KeyDerivationParams readKeyDerivationParams(XmlPullParser parser)
+            throws XmlPullParserException, IOException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+
+        int memoryDifficulty = -1;
+        int algorithm = -1;
+        byte[] salt = null;
+
+        while (parser.next() != XmlPullParser.END_TAG) {
+            if (parser.getEventType() != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+
+            switch (name) {
+                case TAG_MEMORY_DIFFICULTY:
+                    memoryDifficulty = readIntTag(parser, TAG_MEMORY_DIFFICULTY);
+                    break;
+
+                case TAG_ALGORITHM:
+                    algorithm = readIntTag(parser, TAG_ALGORITHM);
+                    break;
+
+                case TAG_SALT:
+                    salt = readBlobTag(parser, TAG_SALT);
+                    break;
+
+                default:
+                    throw new KeyChainSnapshotParserException(
+                            String.format(
+                                    Locale.US,
+                                    "Unexpected tag %s in keyDerivationParams",
+                                    name));
+            }
+        }
+
+        if (salt == null) {
+            throw new KeyChainSnapshotParserException("salt was not set in keyDerivationParams");
+        }
+
+        KeyDerivationParams keyDerivationParams = null;
+
+        switch (algorithm) {
+            case KeyDerivationParams.ALGORITHM_SHA256:
+                keyDerivationParams = KeyDerivationParams.createSha256Params(salt);
+                break;
+
+            case KeyDerivationParams.ALGORITHM_SCRYPT:
+                keyDerivationParams = KeyDerivationParams.createScryptParams(
+                        salt, memoryDifficulty);
+                break;
+
+            default:
+                throw new KeyChainSnapshotParserException(
+                        "Unknown algorithm in keyDerivationParams");
+        }
+
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+        return keyDerivationParams;
+    }
+
+    private static int readIntTag(XmlPullParser parser, String tagName)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+        String text = readText(parser);
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+        try {
+            return Integer.valueOf(text);
+        } catch (NumberFormatException e) {
+            throw new KeyChainSnapshotParserException(
+                    String.format(
+                            Locale.US, "%s expected int but got '%s'", tagName, text), e);
+        }
+    }
+
+    private static long readLongTag(XmlPullParser parser, String tagName)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+        String text = readText(parser);
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+        try {
+            return Long.valueOf(text);
+        } catch (NumberFormatException e) {
+            throw new KeyChainSnapshotParserException(
+                    String.format(
+                            Locale.US, "%s expected long but got '%s'", tagName, text), e);
+        }
+    }
+
+    private static String readStringTag(XmlPullParser parser, String tagName)
+            throws IOException, XmlPullParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+        String text = readText(parser);
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+        return text;
+    }
+
+    private static byte[] readBlobTag(XmlPullParser parser, String tagName)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        parser.require(XmlPullParser.START_TAG, NAMESPACE, tagName);
+        String text = readText(parser);
+        parser.require(XmlPullParser.END_TAG, NAMESPACE, tagName);
+
+        try {
+            return Base64.decode(text, /*flags=*/ Base64.DEFAULT);
+        } catch (IllegalArgumentException e) {
+            throw new KeyChainSnapshotParserException(
+                    String.format(
+                            Locale.US,
+                            "%s expected base64 encoded bytes but got '%s'",
+                            tagName, text), e);
+        }
+    }
+
+    private static CertPath readCertPathTag(XmlPullParser parser, String tagName)
+            throws IOException, XmlPullParserException, KeyChainSnapshotParserException {
+        byte[] bytes = readBlobTag(parser, tagName);
+        try {
+            return CertificateFactory.getInstance(CERTIFICATE_FACTORY_TYPE)
+                    .generateCertPath(new ByteArrayInputStream(bytes));
+        } catch (CertificateException e) {
+            throw new KeyChainSnapshotParserException("Could not parse CertPath in tag " + tagName,
+                    e);
+        }
+    }
+
+    private static String readText(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        String result = "";
+        if (parser.next() == XmlPullParser.TEXT) {
+            result = parser.getText();
+            parser.nextTag();
+        }
+        return result;
+    }
+
+    // Statics only
+    private KeyChainSnapshotDeserializer() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java
new file mode 100644
index 0000000..a3208af
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotParserException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore.serialization;
+
+/**
+ * Error thrown when parsing invalid XML, while trying to read a
+ * {@link android.security.keystore.recovery.KeyChainSnapshot}.
+ */
+public class KeyChainSnapshotParserException extends Exception {
+
+    public KeyChainSnapshotParserException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public KeyChainSnapshotParserException(String message) {
+        super(message);
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
new file mode 100644
index 0000000..ff30ecd
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSchema.java
@@ -0,0 +1,58 @@
+/*
+ * 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.locksettings.recoverablekeystore.serialization;
+
+/**
+ * Describes the XML schema of the {@link android.security.keystore.recovery.KeyChainSnapshot} file.
+ */
+class KeyChainSnapshotSchema {
+    static final String NAMESPACE = null;
+
+    static final String OUTPUT_ENCODING = "UTF-8";
+
+    static final String CERTIFICATE_FACTORY_TYPE = "X.509";
+    static final String CERT_PATH_ENCODING = "PkiPath";
+
+    static final String TAG_KEY_CHAIN_SNAPSHOT = "keyChainSnapshot";
+
+    static final String TAG_SNAPSHOT_VERSION = "snapshotVersion";
+    static final String TAG_COUNTER_ID = "counterId";
+    static final String TAG_MAX_ATTEMPTS = "maxAttempts";
+    static final String TAG_RECOVERY_KEY_MATERIAL = "recoveryKeyMaterial";
+    static final String TAG_SERVER_PARAMS = "serverParams";
+    static final String TAG_TRUSTED_HARDWARE_CERT_PATH = "thmCertPath";
+    static final String TAG_BACKEND_PUBLIC_KEY = "backendPublicKey";
+
+    static final String TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST =
+            "keyChainProtectionParamsList";
+    static final String TAG_KEY_CHAIN_PROTECTION_PARAMS = "keyChainProtectionParams";
+    static final String TAG_USER_SECRET_TYPE = "userSecretType";
+    static final String TAG_LOCK_SCREEN_UI_TYPE = "lockScreenUiType";
+
+    static final String TAG_KEY_DERIVATION_PARAMS = "keyDerivationParams";
+    static final String TAG_ALGORITHM = "algorithm";
+    static final String TAG_MEMORY_DIFFICULTY = "memoryDifficulty";
+    static final String TAG_SALT = "salt";
+
+    static final String TAG_APPLICATION_KEYS = "applicationKeysList";
+    static final String TAG_APPLICATION_KEY = "applicationKey";
+    static final String TAG_ALIAS = "alias";
+    static final String TAG_KEY_MATERIAL = "keyMaterial";
+
+    // Statics only
+    private KeyChainSnapshotSchema() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
new file mode 100644
index 0000000..17a16bf
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializer.java
@@ -0,0 +1,203 @@
+/*
+ * 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.locksettings.recoverablekeystore.serialization;
+
+
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.CERT_PATH_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.NAMESPACE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.OUTPUT_ENCODING;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALGORITHM;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_ALIAS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_APPLICATION_KEYS;
+
+import static com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotSchema.TAG_BACKEND_PUBLIC_KEY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_COUNTER_ID;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_RECOVERY_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_CHAIN_SNAPSHOT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_DERIVATION_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_KEY_MATERIAL;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_LOCK_SCREEN_UI_TYPE;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MAX_ATTEMPTS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_MEMORY_DIFFICULTY;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SALT;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SERVER_PARAMS;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_SNAPSHOT_VERSION;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_TRUSTED_HARDWARE_CERT_PATH;
+import static com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSchema.TAG_USER_SECRET_TYPE;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.util.Base64;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateEncodingException;
+import java.util.List;
+
+/**
+ * Serializes a {@link KeyChainSnapshot} instance to XML.
+ */
+public class KeyChainSnapshotSerializer {
+
+    /**
+     * Serializes {@code keyChainSnapshot} to XML, writing to {@code outputStream}.
+     *
+     * @throws IOException if there was an IO error writing to the stream.
+     * @throws CertificateEncodingException if the {@link CertPath} from
+     *     {@link KeyChainSnapshot#getTrustedHardwareCertPath()} is not encoded correctly.
+     */
+    public static void serialize(KeyChainSnapshot keyChainSnapshot, OutputStream outputStream)
+            throws IOException, CertificateEncodingException {
+        XmlSerializer xmlSerializer = Xml.newSerializer();
+        xmlSerializer.setOutput(outputStream, OUTPUT_ENCODING);
+        xmlSerializer.startDocument(
+                /*encoding=*/ null,
+                /*standalone=*/ null);
+        xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+        writeKeyChainSnapshotProperties(xmlSerializer, keyChainSnapshot);
+        writeKeyChainProtectionParams(xmlSerializer,
+                keyChainSnapshot.getKeyChainProtectionParams());
+        writeApplicationKeys(xmlSerializer,
+                keyChainSnapshot.getWrappedApplicationKeys());
+        xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_SNAPSHOT);
+        xmlSerializer.endDocument();
+    }
+
+    private static void writeApplicationKeys(
+            XmlSerializer xmlSerializer, List<WrappedApplicationKey> wrappedApplicationKeys)
+            throws IOException {
+        xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEYS);
+        for (WrappedApplicationKey key : wrappedApplicationKeys) {
+            xmlSerializer.startTag(NAMESPACE, TAG_APPLICATION_KEY);
+            writeApplicationKeyProperties(xmlSerializer, key);
+            xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEY);
+        }
+        xmlSerializer.endTag(NAMESPACE, TAG_APPLICATION_KEYS);
+    }
+
+    private static void writeApplicationKeyProperties(
+            XmlSerializer xmlSerializer, WrappedApplicationKey applicationKey) throws IOException {
+        writePropertyTag(xmlSerializer, TAG_ALIAS, applicationKey.getAlias());
+        writePropertyTag(xmlSerializer, TAG_KEY_MATERIAL, applicationKey.getEncryptedKeyMaterial());
+    }
+
+    private static void writeKeyChainProtectionParams(
+            XmlSerializer xmlSerializer,
+            List<KeyChainProtectionParams> keyChainProtectionParamsList) throws IOException {
+        xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+        for (KeyChainProtectionParams keyChainProtectionParams : keyChainProtectionParamsList) {
+            xmlSerializer.startTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+            writeKeyChainProtectionParamsProperties(xmlSerializer, keyChainProtectionParams);
+            xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS);
+        }
+        xmlSerializer.endTag(NAMESPACE, TAG_KEY_CHAIN_PROTECTION_PARAMS_LIST);
+    }
+
+    private static void writeKeyChainProtectionParamsProperties(
+            XmlSerializer xmlSerializer, KeyChainProtectionParams keyChainProtectionParams)
+            throws IOException {
+        writePropertyTag(xmlSerializer, TAG_USER_SECRET_TYPE,
+                keyChainProtectionParams.getUserSecretType());
+        writePropertyTag(xmlSerializer, TAG_LOCK_SCREEN_UI_TYPE,
+                keyChainProtectionParams.getLockScreenUiFormat());
+
+        // NOTE: Do not serialize the 'secret' field. It should never be set anyway for snapshots
+        // we generate.
+
+        writeKeyDerivationParams(xmlSerializer, keyChainProtectionParams.getKeyDerivationParams());
+    }
+
+    private static void writeKeyDerivationParams(
+            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            throws IOException {
+        xmlSerializer.startTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+        writeKeyDerivationParamsProperties(
+                xmlSerializer, keyDerivationParams);
+        xmlSerializer.endTag(NAMESPACE, TAG_KEY_DERIVATION_PARAMS);
+    }
+
+    private static void writeKeyDerivationParamsProperties(
+            XmlSerializer xmlSerializer, KeyDerivationParams keyDerivationParams)
+            throws IOException {
+        writePropertyTag(xmlSerializer, TAG_ALGORITHM, keyDerivationParams.getAlgorithm());
+        writePropertyTag(xmlSerializer, TAG_SALT, keyDerivationParams.getSalt());
+        writePropertyTag(xmlSerializer, TAG_MEMORY_DIFFICULTY,
+                keyDerivationParams.getMemoryDifficulty());
+    }
+
+    private static void writeKeyChainSnapshotProperties(
+            XmlSerializer xmlSerializer, KeyChainSnapshot keyChainSnapshot)
+            throws IOException, CertificateEncodingException {
+
+        writePropertyTag(xmlSerializer, TAG_SNAPSHOT_VERSION,
+                keyChainSnapshot.getSnapshotVersion());
+        writePropertyTag(xmlSerializer, TAG_MAX_ATTEMPTS, keyChainSnapshot.getMaxAttempts());
+        writePropertyTag(xmlSerializer, TAG_COUNTER_ID, keyChainSnapshot.getCounterId());
+        writePropertyTag(xmlSerializer, TAG_RECOVERY_KEY_MATERIAL,
+                keyChainSnapshot.getEncryptedRecoveryKeyBlob());
+        writePropertyTag(xmlSerializer, TAG_SERVER_PARAMS, keyChainSnapshot.getServerParams());
+        writePropertyTag(xmlSerializer, TAG_TRUSTED_HARDWARE_CERT_PATH,
+                keyChainSnapshot.getTrustedHardwareCertPath());
+        if (keyChainSnapshot.getTrustedHardwarePublicKey() != null) {
+            writePropertyTag(xmlSerializer, TAG_BACKEND_PUBLIC_KEY,
+                    keyChainSnapshot.getTrustedHardwarePublicKey());
+        }
+    }
+
+    private static void writePropertyTag(
+            XmlSerializer xmlSerializer, String propertyName, long propertyValue)
+            throws IOException {
+        xmlSerializer.startTag(NAMESPACE, propertyName);
+        xmlSerializer.text(Long.toString(propertyValue));
+        xmlSerializer.endTag(NAMESPACE, propertyName);
+    }
+
+    private static void writePropertyTag(
+            XmlSerializer xmlSerializer, String propertyName, String propertyValue)
+            throws IOException {
+        xmlSerializer.startTag(NAMESPACE, propertyName);
+        xmlSerializer.text(propertyValue);
+        xmlSerializer.endTag(NAMESPACE, propertyName);
+    }
+
+    private static void writePropertyTag(
+            XmlSerializer xmlSerializer, String propertyName, byte[] propertyValue)
+            throws IOException {
+        xmlSerializer.startTag(NAMESPACE, propertyName);
+        xmlSerializer.text(Base64.encodeToString(propertyValue, /*flags=*/ Base64.DEFAULT));
+        xmlSerializer.endTag(NAMESPACE, propertyName);
+    }
+
+    private static void writePropertyTag(
+            XmlSerializer xmlSerializer, String propertyName, CertPath certPath)
+            throws IOException, CertificateEncodingException {
+        writePropertyTag(xmlSerializer, propertyName, certPath.getEncoded(CERT_PATH_ENCODING));
+    }
+
+    // Statics only
+    private KeyChainSnapshotSerializer() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
index 2676ee8..38834ac 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDb.java
@@ -29,6 +29,7 @@
 import com.android.server.locksettings.recoverablekeystore.WrappedKey;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
 
 import java.io.ByteArrayInputStream;
@@ -385,13 +386,15 @@
      *
      * @param userId The userId of the profile the application is running under.
      * @param uid The uid of the application who initializes the local recovery components.
+     * @param rootAlias The root of trust alias.
      * @return The value that were previously set, or null if there's none.
      *
      * @hide
      */
     @Nullable
-    public Long getRecoveryServiceCertSerial(int userId, int uid) {
-        return getLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
+    public Long getRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias) {
+        return getLong(userId, uid, rootAlias,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL);
     }
 
     /**
@@ -399,13 +402,16 @@
      *
      * @param userId The userId of the profile the application is running under.
      * @param uid The uid of the application who initializes the local recovery components.
+     * @param rootAlias The root of trust alias.
      * @param serial The serial number contained in the XML file for recovery service certificates.
      * @return The primary key of the inserted row, or -1 if failed.
      *
      * @hide
      */
-    public long setRecoveryServiceCertSerial(int userId, int uid, long serial) {
-        return setLong(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL, serial);
+    public long setRecoveryServiceCertSerial(int userId, int uid, @NonNull String rootAlias,
+            long serial) {
+        return setLong(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL,
+                serial);
     }
 
     /**
@@ -413,13 +419,15 @@
      *
      * @param userId The userId of the profile the application is running under.
      * @param uid The uid of the application who initializes the local recovery components.
+     * @param rootAlias The root of trust alias.
      * @return The value that were previously set, or null if there's none.
      *
      * @hide
      */
     @Nullable
-    public CertPath getRecoveryServiceCertPath(int userId, int uid) {
-        byte[] bytes = getBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
+    public CertPath getRecoveryServiceCertPath(int userId, int uid, @NonNull String rootAlias) {
+        byte[] bytes = getBytes(userId, uid, rootAlias,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH);
         if (bytes == null) {
             return null;
         }
@@ -440,16 +448,17 @@
      *
      * @param userId The userId of the profile the application is running under.
      * @param uid The uid of the application who initializes the local recovery components.
+     * @param rootAlias The root of trust alias.
      * @param certPath The certificate path of the recovery service.
      * @return The primary key of the inserted row, or -1 if failed.
      * @hide
      */
-    public long setRecoveryServiceCertPath(int userId, int uid, CertPath certPath) throws
-            CertificateEncodingException {
+    public long setRecoveryServiceCertPath(int userId, int uid, @NonNull String rootAlias,
+            CertPath certPath) throws CertificateEncodingException {
         if (certPath.getCertificates().size() == 0) {
             throw new CertificateEncodingException("No certificate contained in the cert path.");
         }
-        return setBytes(userId, uid, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
+        return setBytes(userId, uid, rootAlias, RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH,
                 certPath.getEncoded(CERT_PATH_ENCODING));
     }
 
@@ -608,6 +617,85 @@
     }
 
     /**
+     * Active root of trust for the recovery agent.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application.
+     * @param rootAlias The root of trust alias.
+     * @return The primary key of the updated row, or -1 if failed.
+     *
+     * @hide
+     */
+    public long setActiveRootOfTrust(int userId, int uid, @Nullable String rootAlias) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, rootAlias);
+        String selection =
+                RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
+        ensureRecoveryServiceMetadataEntryExists(userId, uid);
+        return db.update(RecoveryServiceMetadataEntry.TABLE_NAME, values,
+            selection, new String[] {String.valueOf(userId), String.valueOf(uid)});
+    }
+
+    /**
+     * Active root of trust for the recovery agent.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application who initialized the local recovery components.
+     * @return Active root of trust alias of null if it was not set
+     *
+     * @hide
+     */
+    public @Nullable String getActiveRootOfTrust(int userId, int uid) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+
+        String[] projection = {
+                RecoveryServiceMetadataEntry._ID,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_UID,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST};
+        String selection =
+                RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                        + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " = ?";
+        String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid)};
+
+        try (
+                Cursor cursor = db.query(
+                        RecoveryServiceMetadataEntry.TABLE_NAME,
+                        projection,
+                        selection,
+                        selectionArguments,
+                        /*groupBy=*/ null,
+                        /*having=*/ null,
+                        /*orderBy=*/ null)
+        ) {
+            int count = cursor.getCount();
+            if (count == 0) {
+                return null;
+            }
+            if (count > 1) {
+                Log.wtf(TAG,
+                        String.format(Locale.US,
+                                "%d deviceId entries found for userId=%d uid=%d. "
+                                        + "Should only ever be 0 or 1.", count, userId, uid));
+                return null;
+            }
+            cursor.moveToFirst();
+            int idx = cursor.getColumnIndexOrThrow(
+                    RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST);
+            if (cursor.isNull(idx)) {
+                return null;
+            }
+            String result = cursor.getString(idx);
+            if (TextUtils.isEmpty(result)) {
+                return null;
+            }
+            return result;
+        }
+    }
+
+    /**
      * Updates the counterId
      *
      * @param userId The userId of the profile the application is running under.
@@ -874,7 +962,6 @@
      *
      * @hide
      */
-
     private long setBytes(int userId, int uid, String key, byte[] value) {
         SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
         ContentValues values = new ContentValues();
@@ -890,6 +977,176 @@
     }
 
     /**
+     * Returns given binary value from the database.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application who initialized the local recovery components.
+     * @param rootAlias The root of trust alias.
+     * @param key from {@code RootOfTrustEntry}
+     * @return The value that were previously set, or null if there's none.
+     *
+     * @hide
+     */
+    private byte[] getBytes(int userId, int uid, String rootAlias, String key) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+
+        String[] projection = {
+                RootOfTrustEntry._ID,
+                RootOfTrustEntry.COLUMN_NAME_USER_ID,
+                RootOfTrustEntry.COLUMN_NAME_UID,
+                RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS,
+                key};
+        String selection =
+                RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
+        String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
+
+        try (
+            Cursor cursor = db.query(
+                    RootOfTrustEntry.TABLE_NAME,
+                    projection,
+                    selection,
+                    selectionArguments,
+                    /*groupBy=*/ null,
+                    /*having=*/ null,
+                    /*orderBy=*/ null)
+        ) {
+            int count = cursor.getCount();
+            if (count == 0) {
+                return null;
+            }
+            if (count > 1) {
+                Log.wtf(TAG,
+                        String.format(Locale.US,
+                                "%d entries found for userId=%d uid=%d. "
+                                        + "Should only ever be 0 or 1.", count, userId, uid));
+                return null;
+            }
+            cursor.moveToFirst();
+            int idx = cursor.getColumnIndexOrThrow(key);
+            if (cursor.isNull(idx)) {
+                return null;
+            } else {
+                return cursor.getBlob(idx);
+            }
+        }
+    }
+
+    /**
+     * Sets a binary value in the database.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application who initialized the local recovery components.
+     * @param rootAlias The root of trust alias.
+     * @param key defined in {@code RootOfTrustEntry}
+     * @param value new value.
+     * @return The primary key of the inserted row, or -1 if failed.
+     *
+     * @hide
+     */
+    private long setBytes(int userId, int uid, String rootAlias, String key, byte[] value) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(key, value);
+        String selection =
+                RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
+        String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
+
+        ensureRootOfTrustEntryExists(userId, uid, rootAlias);
+        return db.update(
+                RootOfTrustEntry.TABLE_NAME, values, selection, selectionArguments);
+    }
+
+    /**
+     * Returns given long value from the database.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application who initialized the local recovery components.
+     * @param rootAlias The root of trust alias.
+     * @param key from {@code RootOfTrustEntry}
+     * @return The value that were previously set, or null if there's none.
+     *
+     * @hide
+     */
+    private Long getLong(int userId, int uid, String rootAlias, String key) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getReadableDatabase();
+
+        String[] projection = {
+                RootOfTrustEntry._ID,
+                RootOfTrustEntry.COLUMN_NAME_USER_ID,
+                RootOfTrustEntry.COLUMN_NAME_UID,
+                RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS,
+                key};
+        String selection =
+                RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
+        String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
+
+        try (
+            Cursor cursor = db.query(
+                    RootOfTrustEntry.TABLE_NAME,
+                    projection,
+                    selection,
+                    selectionArguments,
+                    /*groupBy=*/ null,
+                    /*having=*/ null,
+                    /*orderBy=*/ null)
+        ) {
+            int count = cursor.getCount();
+            if (count == 0) {
+                return null;
+            }
+            if (count > 1) {
+                Log.wtf(TAG,
+                        String.format(Locale.US,
+                                "%d entries found for userId=%d uid=%d. "
+                                        + "Should only ever be 0 or 1.", count, userId, uid));
+                return null;
+            }
+            cursor.moveToFirst();
+            int idx = cursor.getColumnIndexOrThrow(key);
+            if (cursor.isNull(idx)) {
+                return null;
+            } else {
+                return cursor.getLong(idx);
+            }
+        }
+    }
+
+    /**
+     * Sets a long value in the database.
+     *
+     * @param userId The userId of the profile the application is running under.
+     * @param uid The uid of the application who initialized the local recovery components.
+     * @param rootAlias The root of trust alias.
+     * @param key defined in {@code RootOfTrustEntry}
+     * @param value new value.
+     * @return The primary key of the inserted row, or -1 if failed.
+     *
+     * @hide
+     */
+
+    private long setLong(int userId, int uid, String rootAlias, String key, long value) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(key, value);
+        String selection =
+                RootOfTrustEntry.COLUMN_NAME_USER_ID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_UID + " = ? AND "
+                        + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " = ?";
+        String[] selectionArguments = {Integer.toString(userId), Integer.toString(uid), rootAlias};
+
+        ensureRootOfTrustEntryExists(userId, uid, rootAlias);
+        return db.update(
+                RootOfTrustEntry.TABLE_NAME, values, selection, selectionArguments);
+    }
+
+
+    /**
      * Creates an empty row in the recovery service metadata table if such a row doesn't exist for
      * the given userId and uid, so db.update will succeed.
      */
@@ -903,6 +1160,20 @@
     }
 
     /**
+     * Creates an empty row in the root of trust table if such a row doesn't exist for
+     * the given userId and uid, so db.update will succeed.
+     */
+    private void ensureRootOfTrustEntryExists(int userId, int uid, String rootAlias) {
+        SQLiteDatabase db = mKeyStoreDbHelper.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, userId);
+        values.put(RootOfTrustEntry.COLUMN_NAME_UID, uid);
+        values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, rootAlias);
+        db.insertWithOnConflict(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null,
+                values, SQLiteDatabase.CONFLICT_IGNORE);
+    }
+
+    /**
      * Closes all open connections to the database.
      */
     public void close() {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
index 2c3d3ab..7ee809a 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbContract.java
@@ -70,121 +70,6 @@
     }
 
     /**
-     * Table holding encrypted snapshots of the recoverable key store.
-     */
-    static class SnapshotsEntry implements BaseColumns {
-        static final String TABLE_NAME = "snapshots";
-
-        /**
-         * The version number of the snapshot.
-         */
-        static final String COLUMN_NAME_VERSION = "version";
-
-        /**
-         * The ID of the user whose keystore was snapshotted.
-         */
-        static final String COLUMN_NAME_USER_ID = "user_id";
-
-        /**
-         * The UID of the app that owns the snapshot (i.e., the recovery agent).
-         */
-        static final String COLUMN_NAME_UID = "uid";
-
-        /**
-         * The maximum number of attempts allowed to attempt to decrypt the recovery key.
-         */
-        static final String COLUMN_NAME_MAX_ATTEMPTS = "max_attempts";
-
-        /**
-         * The ID of the counter in the trusted hardware module.
-         */
-        static final String COLUMN_NAME_COUNTER_ID = "counter_id";
-
-        /**
-         * Server parameters used to help identify the device (during recovery).
-         */
-        static final String SERVER_PARAMS = "server_params";
-
-        /**
-         * The public key of the trusted hardware module. This key has been used to encrypt the
-         * snapshot, to ensure that it can only be read by the trusted module.
-         */
-        static final String TRUSTED_HARDWARE_PUBLIC_KEY = "thm_public_key";
-
-        /**
-         * {@link java.security.cert.CertPath} signing the trusted hardware module to whose public
-         * key this snapshot is encrypted.
-         */
-        static final String CERT_PATH = "cert_path";
-
-        /**
-         * The recovery key, encrypted with the user's lock screen and the trusted hardware module's
-         * public key.
-         */
-        static final String ENCRYPTED_RECOVERY_KEY = "encrypted_recovery_key";
-    }
-
-    /**
-     * Table holding encrypted keys belonging to a particular snapshot.
-     */
-    static class SnapshotKeysEntry implements BaseColumns {
-        static final String TABLE_NAME = "snapshot_keys";
-
-        /**
-         * ID of the associated snapshot entry in {@link SnapshotsEntry}.
-         */
-        static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
-
-        /**
-         * Alias of the key.
-         */
-        static final String COLUMN_NAME_ALIAS = "alias";
-
-        /**
-         * Key material, encrypted with the recovery key from the snapshot.
-         */
-        static final String COLUMN_NAME_ENCRYPTED_BYTES = "encrypted_key_bytes";
-    }
-
-    /**
-     * A layer of protection associated with a snapshot.
-     */
-    static class SnapshotProtectionParams implements BaseColumns {
-        static final String TABLE_NAME = "snapshot_protection_params";
-
-        /**
-         * ID of the associated snapshot entry in {@link SnapshotsEntry}.
-         */
-        static final String COLUMN_NAME_SNAPSHOT_ID = "snapshot_id";
-
-        /**
-         * Type of secret used to generate recovery key. One of
-         * {@link android.security.keystore.recovery.KeyChainProtectionParams#TYPE_LOCKSCREEN} or
-         */
-        static final String COLUMN_NAME_SECRET_TYPE = "secret_type";
-
-        /**
-         * If a lock screen, the type of UI used. One of
-         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PATTERN},
-         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PIN}, or
-         * {@link android.security.keystore.recovery.KeyChainProtectionParams#UI_FORMAT_PASSWORD}.
-         */
-        static final String COLUMN_NAME_LOCKSCREEN_UI_TYPE = "lock_screen_ui_type";
-
-        /**
-         * The algorithm used to derive cryptographic material from the key and salt. One of
-         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SHA256} or
-         * {@link android.security.keystore.recovery.KeyDerivationParams#ALGORITHM_SCRYPT}.
-         */
-        static final String COLUMN_NAME_KEY_DERIVATION_ALGORITHM = "key_derivation_algorithm";
-
-        /**
-         * The salt used along with the secret to generate cryptographic material.
-         */
-        static final String COLUMN_NAME_KEY_DERIVATION_SALT = "key_derivation_salt";
-    }
-
-    /**
      * Recoverable KeyStore metadata for a specific user profile.
      */
     static class UserMetadataEntry implements BaseColumns {
@@ -230,16 +115,19 @@
 
         /**
          * The public key of the recovery service.
+         * Deprecated.
          */
         static final String COLUMN_NAME_PUBLIC_KEY = "public_key";
 
         /**
          * The certificate path of the recovery service.
+         * Deprecated.
          */
         static final String COLUMN_NAME_CERT_PATH = "cert_path";
 
         /**
          * The serial number contained in the certificate XML file of the recovery service.
+         * Deprecated.
          */
         static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
 
@@ -257,5 +145,42 @@
          * The server parameters of the recovery service.
          */
         static final String COLUMN_NAME_SERVER_PARAMS = "server_params";
+
+        /**
+         * Active root of trust
+         */
+        static final String COLUMN_NAME_ACTIVE_ROOT_OF_TRUST = "active_root_of_trust";
+    }
+
+    /**
+     * Table data for given recovery agent and root of trust pair.
+     */
+    static class RootOfTrustEntry implements BaseColumns {
+        static final String TABLE_NAME = "root_of_trust";
+
+        /**
+         * The user id of the profile the application is running under.
+         */
+        static final String COLUMN_NAME_USER_ID = "user_id";
+
+        /**
+         * The uid of the application that initializes the local recovery components.
+         */
+        static final String COLUMN_NAME_UID = "uid";
+
+        /**
+         * Root of trust alias
+         */
+        static final String COLUMN_NAME_ROOT_ALIAS = "root_alias";
+
+        /**
+         * The certificate path of the recovery service.
+         */
+        static final String COLUMN_NAME_CERT_PATH = "cert_path";
+
+        /**
+         * The serial number contained in the certificate XML file of the recovery service.
+         */
+        static final String COLUMN_NAME_CERT_SERIAL = "cert_serial";
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
index 8a89f2d..43efe9c 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelper.java
@@ -23,6 +23,7 @@
 
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
 
 /**
@@ -31,7 +32,7 @@
 class RecoverableKeyStoreDbHelper extends SQLiteOpenHelper {
     private static final String TAG = "RecoverableKeyStoreDbHp";
 
-    static final int DATABASE_VERSION = 3;
+    static final int DATABASE_VERSION = 4;
     private static final String DATABASE_NAME = "recoverablekeystore.db";
 
     private static final String SQL_CREATE_KEYS_ENTRY =
@@ -61,6 +62,7 @@
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + " INTEGER,"
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_SNAPSHOT_VERSION + " INTEGER,"
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT + " INTEGER,"
+                    + RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST + " TEXT,"
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY + " BLOB,"
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
@@ -71,6 +73,19 @@
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_USER_ID  + ","
                     + RecoveryServiceMetadataEntry.COLUMN_NAME_UID + "))";
 
+    private static final String SQL_CREATE_ROOT_OF_TRUST_ENTRY =
+            "CREATE TABLE " + RootOfTrustEntry.TABLE_NAME + " ("
+                    + RootOfTrustEntry._ID + " INTEGER PRIMARY KEY,"
+                    + RootOfTrustEntry.COLUMN_NAME_USER_ID + " INTEGER,"
+                    + RootOfTrustEntry.COLUMN_NAME_UID + " INTEGER,"
+                    + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + " TEST,"
+                    + RootOfTrustEntry.COLUMN_NAME_CERT_PATH + " BLOB,"
+                    + RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL + " INTEGER,"
+                    + "UNIQUE("
+                    + RootOfTrustEntry.COLUMN_NAME_USER_ID  + ","
+                    + RootOfTrustEntry.COLUMN_NAME_UID  + ","
+                    + RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS + "))";
+
     private static final String SQL_DELETE_KEYS_ENTRY =
             "DROP TABLE IF EXISTS " + KeysEntry.TABLE_NAME;
 
@@ -80,6 +95,9 @@
     private static final String SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY =
             "DROP TABLE IF EXISTS " + RecoveryServiceMetadataEntry.TABLE_NAME;
 
+    private static final String SQL_DELETE_ROOT_OF_TRUST_ENTRY =
+            "DROP TABLE IF EXISTS " + RootOfTrustEntry.TABLE_NAME;
+
     RecoverableKeyStoreDbHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);
     }
@@ -89,21 +107,44 @@
         db.execSQL(SQL_CREATE_KEYS_ENTRY);
         db.execSQL(SQL_CREATE_USER_METADATA_ENTRY);
         db.execSQL(SQL_CREATE_RECOVERY_SERVICE_METADATA_ENTRY);
+        db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
+    }
+
+    @Override
+    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+        Log.e(TAG, "Recreating recoverablekeystore after unexpected version downgrade.");
+        dropAllKnownTables(db); // Wipe database.
+        onCreate(db);
     }
 
     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
         if (oldVersion < 2) {
-            db.execSQL(SQL_DELETE_KEYS_ENTRY);
-            db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
-            db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
+            dropAllKnownTables(db); // Wipe database.
             onCreate(db);
             return;
         }
 
-        if (oldVersion < 3) {
+        if (oldVersion < 3 && newVersion >= 3) {
             upgradeDbForVersion3(db);
+            oldVersion = 3;
         }
+
+        if (oldVersion < 4 && newVersion >= 4) {
+            upgradeDbForVersion4(db);
+            oldVersion = 4;
+        }
+
+        if (oldVersion != newVersion) {
+            Log.e(TAG, "Failed to update recoverablekeystore database to the most recent version");
+        }
+    }
+
+    private void dropAllKnownTables(SQLiteDatabase db) {
+            db.execSQL(SQL_DELETE_KEYS_ENTRY);
+            db.execSQL(SQL_DELETE_USER_METADATA_ENTRY);
+            db.execSQL(SQL_DELETE_RECOVERY_SERVICE_METADATA_ENTRY);
+            db.execSQL(SQL_DELETE_ROOT_OF_TRUST_ENTRY);
     }
 
     private void upgradeDbForVersion3(SQLiteDatabase db) {
@@ -115,6 +156,16 @@
                 null);
     }
 
+    private void upgradeDbForVersion4(SQLiteDatabase db) {
+        Log.d(TAG, "Updating recoverable keystore database to version 4");
+        // Add new table with two columns for cert path and cert serial number.
+        db.execSQL(SQL_CREATE_ROOT_OF_TRUST_ENTRY);
+        // adds column to store root of trust currently used by the recovery agent
+        addColumnToTable(db, RecoveryServiceMetadataEntry.TABLE_NAME,
+                RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, "TEXT",
+                /*defaultStr=*/ null);
+    }
+
     private static void addColumnToTable(
             SQLiteDatabase db, String tableName, String column, String columnType,
             String defaultStr) {
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
index 3f93cc6..c02b103 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorage.java
@@ -17,13 +17,28 @@
 package com.android.server.locksettings.recoverablekeystore.storage;
 
 import android.annotation.Nullable;
+import android.os.Environment;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotDeserializer;
+import com.android.server.locksettings.recoverablekeystore.serialization
+        .KeyChainSnapshotParserException;
+import com.android.server.locksettings.recoverablekeystore.serialization.KeyChainSnapshotSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.util.Locale;
 
 /**
- * In-memory storage for recovery snapshots.
+ * Storage for recovery snapshots. Stores snapshots in memory, backed by disk storage.
  *
  * <p>Recovery snapshots are generated after a successful screen unlock. They are only generated if
  * the recoverable keystore has been mutated since the previous snapshot. This class stores only the
@@ -33,14 +48,46 @@
  * {@link com.android.server.locksettings.recoverablekeystore.KeySyncTask} thread.
  */
 public class RecoverySnapshotStorage {
+
+    private static final String TAG = "RecoverySnapshotStorage";
+
+    private static final String ROOT_PATH = "system";
+    private static final String STORAGE_PATH = "recoverablekeystore/snapshots/";
+
     @GuardedBy("this")
     private final SparseArray<KeyChainSnapshot> mSnapshotByUid = new SparseArray<>();
 
+    private final File rootDirectory;
+
+    /**
+     * A new instance, storing snapshots in /data/system/recoverablekeystore/snapshots.
+     *
+     * <p>NOTE: calling this multiple times DOES NOT return the same instance, so will NOT be backed
+     * by the same in-memory store.
+     */
+    public static RecoverySnapshotStorage newInstance() {
+        return new RecoverySnapshotStorage(
+                new File(Environment.getDataDirectory(), ROOT_PATH));
+    }
+
+    @VisibleForTesting
+    public RecoverySnapshotStorage(File rootDirectory) {
+        this.rootDirectory = rootDirectory;
+    }
+
     /**
      * Sets the latest {@code snapshot} for the recovery agent {@code uid}.
      */
     public synchronized void put(int uid, KeyChainSnapshot snapshot) {
         mSnapshotByUid.put(uid, snapshot);
+
+        try {
+            writeToDisk(uid, snapshot);
+        } catch (IOException | CertificateEncodingException e) {
+            Log.e(TAG,
+                    String.format(Locale.US, "Error persisting snapshot for %d to disk", uid),
+                    e);
+        }
     }
 
     /**
@@ -48,7 +95,17 @@
      */
     @Nullable
     public synchronized KeyChainSnapshot get(int uid) {
-        return mSnapshotByUid.get(uid);
+        KeyChainSnapshot snapshot = mSnapshotByUid.get(uid);
+        if (snapshot != null) {
+            return snapshot;
+        }
+
+        try {
+            return readFromDisk(uid);
+        } catch (IOException | KeyChainSnapshotParserException e) {
+            Log.e(TAG, String.format(Locale.US, "Error reading snapshot for %d from disk", uid), e);
+            return null;
+        }
     }
 
     /**
@@ -56,5 +113,66 @@
      */
     public synchronized void remove(int uid) {
         mSnapshotByUid.remove(uid);
+        getSnapshotFile(uid).delete();
+    }
+
+    /**
+     * Writes the snapshot for recovery agent {@code uid} to disk.
+     *
+     * @throws IOException if an IO error occurs writing to disk.
+     */
+    private void writeToDisk(int uid, KeyChainSnapshot snapshot)
+            throws IOException, CertificateEncodingException {
+        File snapshotFile = getSnapshotFile(uid);
+
+        try (
+            FileOutputStream fileOutputStream = new FileOutputStream(snapshotFile)
+        ) {
+            KeyChainSnapshotSerializer.serialize(snapshot, fileOutputStream);
+        } catch (IOException | CertificateEncodingException e) {
+            // If we fail to write the latest snapshot, we should delete any older snapshot that
+            // happens to be around. Otherwise snapshot syncs might end up going 'back in time'.
+            snapshotFile.delete();
+            throw e;
+        }
+    }
+
+    /**
+     * Reads the last snapshot for recovery agent {@code uid} from disk.
+     *
+     * @return The snapshot, or null if none existed.
+     * @throws IOException if an IO error occurs reading from disk.
+     */
+    @Nullable
+    private KeyChainSnapshot readFromDisk(int uid)
+            throws IOException, KeyChainSnapshotParserException {
+        File snapshotFile = getSnapshotFile(uid);
+
+        try (
+            FileInputStream fileInputStream = new FileInputStream(snapshotFile)
+        ) {
+            return KeyChainSnapshotDeserializer.deserialize(fileInputStream);
+        } catch (IOException | KeyChainSnapshotParserException e) {
+            // If we fail to read the latest snapshot, we should delete it in case it is in some way
+            // corrupted. We can regenerate snapshots anyway.
+            snapshotFile.delete();
+            throw e;
+        }
+    }
+
+    private File getSnapshotFile(int uid) {
+        File folder = getStorageFolder();
+        String fileName = getSnapshotFileName(uid);
+        return new File(folder, fileName);
+    }
+
+    private String getSnapshotFileName(int uid) {
+        return String.format(Locale.US, "%d.xml", uid);
+    }
+
+    private File getStorageFolder() {
+        File folder = new File(rootDirectory, STORAGE_PATH);
+        folder.mkdirs();
+        return folder;
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 664d2f9..0d1644b 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -71,8 +71,6 @@
      */
     private static final int OPTIMISTIC_VOLUME_TIMEOUT = 1000;
 
-    private static final int UID_NOT_SET = -1;
-
     private final MessageHandler mHandler;
 
     private final int mOwnerPid;
@@ -117,9 +115,6 @@
     private boolean mIsActive = false;
     private boolean mDestroyed = false;
 
-    private int mCallingUid = UID_NOT_SET;
-    private String mCallingPackage;
-
     public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName,
             ISessionCallback cb, String tag, MediaSessionService service, Looper handlerLooper) {
         mOwnerPid = ownerPid;
@@ -234,14 +229,14 @@
      * {@link AudioManager#ADJUST_LOWER}, {@link AudioManager#ADJUST_RAISE},
      * {@link AudioManager#ADJUST_SAME}.
      *
+     * @param packageName The package that made the original volume request.
+     * @param pid The pid that made the original volume request.
+     * @param uid The uid that made the original volume request.
      * @param direction The direction to adjust volume in.
      * @param flags Any of the flags from {@link AudioManager}.
-     * @param packageName The package that made the original volume request.
-     * @param uid The uid that made the original volume request.
      * @param useSuggested True to use adjustSuggestedStreamVolume instead of
-     *            adjustStreamVolume.
      */
-    public void adjustVolume(int direction, int flags, String packageName, int uid,
+    public void adjustVolume(String packageName, int pid, int uid, int direction, int flags,
             boolean useSuggested) {
         int previousFlagPlaySound = flags & AudioManager.FLAG_PLAY_SOUND;
         if (isPlaybackActive() || hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
@@ -263,7 +258,7 @@
                 Log.w(TAG, "Muting remote playback is not supported");
                 return;
             }
-            mSessionCb.adjustVolume(direction);
+            mSessionCb.adjustVolume(packageName, pid, uid, direction);
 
             int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
             mOptimisticVolume = volumeBefore + direction;
@@ -282,7 +277,7 @@
         }
     }
 
-    public void setVolumeTo(int value, int flags, String packageName, int uid) {
+    public void setVolumeTo(String packageName, int pid, int uid, int value, int flags) {
         if (mVolumeType == PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
             mAudioManagerInternal.setStreamVolumeForUid(stream, value, flags, packageName, uid);
@@ -292,7 +287,7 @@
                 return;
             }
             value = Math.max(0, Math.min(value, mMaxVolume));
-            mSessionCb.setVolumeTo(value);
+            mSessionCb.setVolumeTo(packageName, pid, uid, value);
 
             int volumeBefore = (mOptimisticVolume < 0 ? mCurrentVolume : mOptimisticVolume);
             mOptimisticVolume = Math.max(0, Math.min(value, mMaxVolume));
@@ -423,10 +418,9 @@
         return mSessionCb.mCb;
     }
 
-    public void sendMediaButton(KeyEvent ke, int sequenceId,
-            ResultReceiver cb, int uid, String packageName) {
-        updateCallingPackage(uid, packageName);
-        mSessionCb.sendMediaButton(ke, sequenceId, cb);
+    public void sendMediaButton(String packageName, int pid, int uid, KeyEvent ke, int sequenceId,
+            ResultReceiver cb) {
+        mSessionCb.sendMediaButton(packageName, pid, uid, ke, sequenceId, cb);
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -703,22 +697,6 @@
         return -1;
     }
 
-    private void updateCallingPackage() {
-        updateCallingPackage(UID_NOT_SET, null);
-    }
-
-    private void updateCallingPackage(int uid, String packageName) {
-        if (uid == UID_NOT_SET) {
-            uid = Binder.getCallingUid();
-        }
-        synchronized (mLock) {
-            if (mCallingUid == UID_NOT_SET || mCallingUid != uid) {
-                mCallingUid = uid;
-                mCallingPackage = packageName != null ? packageName : getPackageName(uid);
-            }
-        }
-    }
-
     private String getPackageName(int uid) {
         Context context = mService.getContext();
         if (context == null) {
@@ -920,11 +898,6 @@
                 mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
             }
         }
-
-        @Override
-        public String getCallingPackage() {
-            return mCallingPackage;
-        }
     }
 
     class SessionCb {
@@ -934,11 +907,12 @@
             mCb = cb;
         }
 
-        public boolean sendMediaButton(KeyEvent keyEvent, int sequenceId, ResultReceiver cb) {
+        public boolean sendMediaButton(String packageName, int pid, int uid, KeyEvent keyEvent,
+                int sequenceId, ResultReceiver cb) {
             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
             mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
             try {
-                mCb.onMediaButton(mediaButtonIntent, sequenceId, cb);
+                mCb.onMediaButton(packageName, pid, uid, mediaButtonIntent, sequenceId, cb);
                 return true;
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in sendMediaRequest.", e);
@@ -946,169 +920,176 @@
             return false;
         }
 
-        public void sendCommand(String command, Bundle args, ResultReceiver cb) {
+        public void sendCommand(String packageName, int pid, int uid, String command, Bundle args,
+                ResultReceiver cb) {
             try {
-                mCb.onCommand(command, args, cb);
+                mCb.onCommand(packageName, pid, uid, command, args, cb);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in sendCommand.", e);
             }
         }
 
-        public void sendCustomAction(String action, Bundle args) {
+        public void sendCustomAction(String packageName, int pid, int uid, String action,
+                Bundle args) {
             try {
-                mCb.onCustomAction(action, args);
+                mCb.onCustomAction(packageName, pid, uid, action, args);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in sendCustomAction.", e);
             }
         }
 
-        public void prepare() {
+        public void prepare(String packageName, int pid, int uid) {
             try {
-                mCb.onPrepare();
+                mCb.onPrepare(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in prepare.", e);
             }
         }
 
-        public void prepareFromMediaId(String mediaId, Bundle extras) {
+        public void prepareFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             try {
-                mCb.onPrepareFromMediaId(mediaId, extras);
+                mCb.onPrepareFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in prepareFromMediaId.", e);
             }
         }
 
-        public void prepareFromSearch(String query, Bundle extras) {
+        public void prepareFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             try {
-                mCb.onPrepareFromSearch(query, extras);
+                mCb.onPrepareFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in prepareFromSearch.", e);
             }
         }
 
-        public void prepareFromUri(Uri uri, Bundle extras) {
+        public void prepareFromUri(String packageName, int pid, int uid, Uri uri,
+                Bundle extras) {
             try {
-                mCb.onPrepareFromUri(uri, extras);
+                mCb.onPrepareFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in prepareFromUri.", e);
             }
         }
 
-        public void play() {
+        public void play(String packageName, int pid, int uid) {
             try {
-                mCb.onPlay();
+                mCb.onPlay(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in play.", e);
             }
         }
 
-        public void playFromMediaId(String mediaId, Bundle extras) {
+        public void playFromMediaId(String packageName, int pid, int uid, String mediaId,
+                Bundle extras) {
             try {
-                mCb.onPlayFromMediaId(mediaId, extras);
+                mCb.onPlayFromMediaId(packageName, pid, uid, mediaId, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in playFromMediaId.", e);
             }
         }
 
-        public void playFromSearch(String query, Bundle extras) {
+        public void playFromSearch(String packageName, int pid, int uid, String query,
+                Bundle extras) {
             try {
-                mCb.onPlayFromSearch(query, extras);
+                mCb.onPlayFromSearch(packageName, pid, uid, query, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in playFromSearch.", e);
             }
         }
 
-        public void playFromUri(Uri uri, Bundle extras) {
+        public void playFromUri(String packageName, int pid, int uid, Uri uri, Bundle extras) {
             try {
-                mCb.onPlayFromUri(uri, extras);
+                mCb.onPlayFromUri(packageName, pid, uid, uri, extras);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in playFromUri.", e);
             }
         }
 
-        public void skipToTrack(long id) {
+        public void skipToTrack(String packageName, int pid, int uid, long id) {
             try {
-                mCb.onSkipToTrack(id);
+                mCb.onSkipToTrack(packageName, pid, uid, id);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in skipToTrack", e);
             }
         }
 
-        public void pause() {
+        public void pause(String packageName, int pid, int uid) {
             try {
-                mCb.onPause();
+                mCb.onPause(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in pause.", e);
             }
         }
 
-        public void stop() {
+        public void stop(String packageName, int pid, int uid) {
             try {
-                mCb.onStop();
+                mCb.onStop(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in stop.", e);
             }
         }
 
-        public void next() {
+        public void next(String packageName, int pid, int uid) {
             try {
-                mCb.onNext();
+                mCb.onNext(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in next.", e);
             }
         }
 
-        public void previous() {
+        public void previous(String packageName, int pid, int uid) {
             try {
-                mCb.onPrevious();
+                mCb.onPrevious(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in previous.", e);
             }
         }
 
-        public void fastForward() {
+        public void fastForward(String packageName, int pid, int uid) {
             try {
-                mCb.onFastForward();
+                mCb.onFastForward(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in fastForward.", e);
             }
         }
 
-        public void rewind() {
+        public void rewind(String packageName, int pid, int uid) {
             try {
-                mCb.onRewind();
+                mCb.onRewind(packageName, pid, uid);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in rewind.", e);
             }
         }
 
-        public void seekTo(long pos) {
+        public void seekTo(String packageName, int pid, int uid, long pos) {
             try {
-                mCb.onSeekTo(pos);
+                mCb.onSeekTo(packageName, pid, uid, pos);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in seekTo.", e);
             }
         }
 
-        public void rate(Rating rating) {
+        public void rate(String packageName, int pid, int uid, Rating rating) {
             try {
-                mCb.onRate(rating);
+                mCb.onRate(packageName, pid, uid, rating);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in rate.", e);
             }
         }
 
-        public void adjustVolume(int direction) {
+        public void adjustVolume(String packageName, int pid, int uid, int direction) {
             try {
-                mCb.onAdjustVolume(direction);
+                mCb.onAdjustVolume(packageName, pid, uid, direction);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in adjustVolume.", e);
             }
         }
 
-        public void setVolumeTo(int value) {
+        public void setVolumeTo(String packageName, int pid, int uid, int value) {
             try {
-                mCb.onSetVolumeTo(value);
+                mCb.onSetVolumeTo(packageName, pid, uid, value);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Remote failure in setVolumeTo.", e);
             }
@@ -1117,16 +1098,16 @@
 
     class ControllerStub extends ISessionController.Stub {
         @Override
-        public void sendCommand(String command, Bundle args, ResultReceiver cb)
-                throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.sendCommand(command, args, cb);
+        public void sendCommand(String packageName, String command, Bundle args,
+                ResultReceiver cb) {
+            mSessionCb.sendCommand(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    command, args, cb);
         }
 
         @Override
-        public boolean sendMediaButton(KeyEvent mediaButtonIntent) {
-            updateCallingPackage();
-            return mSessionCb.sendMediaButton(mediaButtonIntent, 0, null);
+        public boolean sendMediaButton(String packageName, KeyEvent mediaButtonIntent) {
+            return mSessionCb.sendMediaButton(packageName, Binder.getCallingPid(),
+                    Binder.getCallingUid(), mediaButtonIntent, 0, null);
         }
 
         @Override
@@ -1207,137 +1188,125 @@
         }
 
         @Override
-        public void adjustVolume(int direction, int flags, String packageName) {
-            updateCallingPackage();
+        public void adjustVolume(String packageName, int direction, int flags) {
+            int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.adjustVolume(direction, flags, packageName, uid, false);
+                MediaSessionRecord.this.adjustVolume(packageName, pid, uid, direction, flags,
+                        false /* useSuggested */);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public void setVolumeTo(int value, int flags, String packageName) {
-            updateCallingPackage();
+        public void setVolumeTo(String packageName, int value, int flags) {
+            int pid = Binder.getCallingPid();
             int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
             try {
-                MediaSessionRecord.this.setVolumeTo(value, flags, packageName, uid);
+                MediaSessionRecord.this.setVolumeTo(packageName, pid, uid, value, flags);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
         }
 
         @Override
-        public void prepare() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.prepare();
+        public void prepare(String packageName) {
+            mSessionCb.prepare(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void prepareFromMediaId(String mediaId, Bundle extras)
-                throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.prepareFromMediaId(mediaId, extras);
+        public void prepareFromMediaId(String packageName, String mediaId, Bundle extras) {
+            mSessionCb.prepareFromMediaId(packageName, Binder.getCallingPid(),
+                    Binder.getCallingUid(), mediaId, extras);
         }
 
         @Override
-        public void prepareFromSearch(String query, Bundle extras) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.prepareFromSearch(query, extras);
+        public void prepareFromSearch(String packageName, String query, Bundle extras) {
+            mSessionCb.prepareFromSearch(packageName, Binder.getCallingPid(),
+                    Binder.getCallingUid(), query, extras);
         }
 
         @Override
-        public void prepareFromUri(Uri uri, Bundle extras) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.prepareFromUri(uri, extras);
+        public void prepareFromUri(String packageName, Uri uri, Bundle extras) {
+            mSessionCb.prepareFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    uri, extras);
         }
 
         @Override
-        public void play() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.play();
+        public void play(String packageName) {
+            mSessionCb.play(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void playFromMediaId(String mediaId, Bundle extras) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.playFromMediaId(mediaId, extras);
+        public void playFromMediaId(String packageName, String mediaId, Bundle extras) {
+            mSessionCb.playFromMediaId(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    mediaId, extras);
         }
 
         @Override
-        public void playFromSearch(String query, Bundle extras) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.playFromSearch(query, extras);
+        public void playFromSearch(String packageName, String query, Bundle extras) {
+            mSessionCb.playFromSearch(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    query, extras);
         }
 
         @Override
-        public void playFromUri(Uri uri, Bundle extras) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.playFromUri(uri, extras);
+        public void playFromUri(String packageName, Uri uri, Bundle extras) {
+            mSessionCb.playFromUri(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    uri, extras);
         }
 
         @Override
-        public void skipToQueueItem(long id) {
-            updateCallingPackage();
-            mSessionCb.skipToTrack(id);
+        public void skipToQueueItem(String packageName, long id) {
+            mSessionCb.skipToTrack(packageName, Binder.getCallingPid(), Binder.getCallingUid(), id);
         }
 
         @Override
-        public void pause() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.pause();
+        public void pause(String packageName) {
+            mSessionCb.pause(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void stop() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.stop();
+        public void stop(String packageName) {
+            mSessionCb.stop(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void next() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.next();
+        public void next(String packageName) {
+            mSessionCb.next(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void previous() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.previous();
+        public void previous(String packageName) {
+            mSessionCb.previous(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void fastForward() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.fastForward();
+        public void fastForward(String packageName) {
+            mSessionCb.fastForward(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void rewind() throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.rewind();
+        public void rewind(String packageName) {
+            mSessionCb.rewind(packageName, Binder.getCallingPid(), Binder.getCallingUid());
         }
 
         @Override
-        public void seekTo(long pos) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.seekTo(pos);
+        public void seekTo(String packageName, long pos) {
+            mSessionCb.seekTo(packageName, Binder.getCallingPid(), Binder.getCallingUid(), pos);
         }
 
         @Override
-        public void rate(Rating rating) throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.rate(rating);
+        public void rate(String packageName, Rating rating) {
+            mSessionCb.rate(packageName, Binder.getCallingPid(), Binder.getCallingUid(), rating);
         }
 
         @Override
-        public void sendCustomAction(String action, Bundle args)
-                throws RemoteException {
-            updateCallingPackage();
-            mSessionCb.sendCustomAction(action, args);
+        public void sendCustomAction(String packageName, String action, Bundle args) {
+            mSessionCb.sendCustomAction(packageName, Binder.getCallingPid(), Binder.getCallingUid(),
+                    action, args);
         }
 
 
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 01f0d74..6413ba9 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -101,6 +101,7 @@
  */
 public class MediaSessionService extends SystemService implements Monitor {
     private static final String TAG = "MediaSessionService";
+    static final boolean USE_MEDIA2_APIS = false; // TODO: Change this to true when we're ready.
     static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     // Leave log for key event always.
     private static final boolean DEBUG_KEY_EVENT = true;
@@ -508,6 +509,9 @@
     }
 
     private void buildMediaSessionService2List() {
+        if (!USE_MEDIA2_APIS) {
+            return;
+        }
         if (DEBUG) {
             Log.d(TAG, "buildMediaSessionService2List");
         }
@@ -1531,6 +1535,9 @@
          */
         @Override
         public boolean createSession2(Bundle sessionToken) {
+            if (!USE_MEDIA2_APIS) {
+                return false;
+            }
             final int uid = Binder.getCallingUid();
             final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
             if (token == null || token.getUid() != uid) {
@@ -1570,6 +1577,9 @@
          */
         @Override
         public void destroySession2(Bundle sessionToken) {
+            if (!USE_MEDIA2_APIS) {
+                return;
+            }
             final int uid = Binder.getCallingUid();
             final SessionToken2 token = SessionToken2.fromBundle(sessionToken);
             if (token == null || token.getUid() != uid) {
@@ -1586,6 +1596,9 @@
         @Override
         public List<Bundle> getSessionTokens(boolean activeSessionOnly,
                 boolean sessionServiceOnly, String packageName) throws RemoteException {
+            if (!USE_MEDIA2_APIS) {
+                return null;
+            }
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1614,6 +1627,9 @@
         @Override
         public void addSessionTokensListener(ISessionTokensListener listener, int userId,
                 String packageName) throws RemoteException {
+            if (!USE_MEDIA2_APIS) {
+                return;
+            }
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1637,6 +1653,9 @@
         @Override
         public void removeSessionTokensListener(ISessionTokensListener listener,
                 String packageName) throws RemoteException {
+            if (!USE_MEDIA2_APIS) {
+                return;
+            }
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1785,8 +1804,8 @@
                     }
                 });
             } else {
-                session.adjustVolume(direction, flags, getContext().getPackageName(),
-                        Process.SYSTEM_UID, true);
+                session.adjustVolume(getContext().getPackageName(), Process.myPid(),
+                        Process.SYSTEM_UID, direction, flags, true);
             }
         }
 
@@ -1824,10 +1843,12 @@
                     mKeyEventReceiver.aquireWakeLockLocked();
                 }
                 // If we don't need a wakelock use -1 as the id so we won't release it later.
-                session.sendMediaButton(keyEvent,
+                session.sendMediaButton(getContext().getPackageName(),
+                        Process.myPid(),
+                        Process.SYSTEM_UID,
+                        keyEvent,
                         needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
-                        mKeyEventReceiver, Process.SYSTEM_UID,
-                        getContext().getPackageName());
+                        mKeyEventReceiver);
                 if (mCurrentFullUserRecord.mCallback != null) {
                     try {
                         mCurrentFullUserRecord.mCallback.onMediaKeyEventDispatchedToMediaSession(
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8febecf..ddb2a85 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -2338,7 +2338,7 @@
     }
 
     private void setUidPolicyUncheckedUL(int uid, int oldPolicy, int policy, boolean persist) {
-        setUidPolicyUncheckedUL(uid, policy, persist);
+        setUidPolicyUncheckedUL(uid, policy, false);
 
         final boolean notifyApp;
         if (!isUidValidForWhitelistRules(uid)) {
@@ -2361,6 +2361,11 @@
         }
         mHandler.obtainMessage(MSG_POLICIES_CHANGED, uid, policy, Boolean.valueOf(notifyApp))
                 .sendToTarget();
+        if (persist) {
+            synchronized (mNetworkPoliciesSecondLock) {
+                writePolicyAL();
+            }
+        }
     }
 
     private void setUidPolicyUncheckedUL(int uid, int policy, boolean persist) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index a5f8dc7..2ef754e 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -16,9 +16,9 @@
 
 package com.android.server.net;
 
-import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
 import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.METERED_NO;
 import static android.net.NetworkStats.METERED_YES;
 import static android.net.NetworkStats.ROAMING_NO;
@@ -42,9 +42,11 @@
 import android.service.NetworkStatsCollectionProto;
 import android.service.NetworkStatsCollectionStatsProto;
 import android.telephony.SubscriptionPlan;
+import android.text.format.DateUtils;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.IntArray;
+import android.util.MathUtils;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -245,7 +247,10 @@
                     + " is forbidden for caller " + callerUid);
         }
 
-        final int bucketEstimate = (int) ((end - start) / mBucketDuration);
+        // 180 days of history should be enough for anyone; if we end up needing
+        // more, we'll dynamically grow the history object.
+        final int bucketEstimate = (int) MathUtils.constrain(((end - start) / mBucketDuration), 0,
+                (180 * DateUtils.DAY_IN_MILLIS) / mBucketDuration);
         final NetworkStatsHistory combined = new NetworkStatsHistory(
                 mBucketDuration, bucketEstimate, fields);
 
diff --git a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
index 6907c58..29b1339 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistService.java
@@ -171,7 +171,7 @@
             Slog.w(TAG, "Only shell is allowed to call network watchlist shell commands");
             return;
         }
-        (new NetworkWatchlistShellCommand(mContext)).exec(this, in, out, err, args, callback,
+        (new NetworkWatchlistShellCommand(this, mContext)).exec(this, in, out, err, args, callback,
                 resultReceiver);
     }
 
@@ -262,6 +262,21 @@
         mNetworkWatchlistHandler.reportWatchlistIfNecessary();
     }
 
+    /**
+     * Force generate watchlist report for testing.
+     *
+     * @param lastReportTime Watchlist report will cotain all records before this time.
+     * @return True if operation success.
+     */
+    public boolean forceReportWatchlistForTest(long lastReportTime) {
+        if (mConfig.isConfigSecure()) {
+            // Should not force generate report under production config.
+            return false;
+        }
+        mNetworkWatchlistHandler.forceReportWatchlistForTest(lastReportTime);
+        return true;
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
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 9533823..17c5868 100644
--- a/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
+++ b/services/core/java/com/android/server/net/watchlist/NetworkWatchlistShellCommand.java
@@ -19,9 +19,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.net.NetworkWatchlistManager;
+import android.os.Binder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.provider.Settings;
 
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -34,10 +36,12 @@
  */
 class NetworkWatchlistShellCommand extends ShellCommand {
 
-    final NetworkWatchlistManager mNetworkWatchlistManager;
+    final Context mContext;
+    final NetworkWatchlistService mService;
 
-    NetworkWatchlistShellCommand(Context context) {
-        mNetworkWatchlistManager = new NetworkWatchlistManager(context);
+    NetworkWatchlistShellCommand(NetworkWatchlistService service, Context context) {
+        mContext = context;
+        mService = service;
     }
 
     @Override
@@ -51,11 +55,13 @@
             switch(cmd) {
                 case "set-test-config":
                     return runSetTestConfig();
+                case "force-generate-report":
+                    return runForceGenerateReport();
                 default:
                     return handleDefaultCommands(cmd);
             }
-        } catch (RemoteException e) {
-            pw.println("Remote exception: " + e);
+        } catch (Exception e) {
+            pw.println("Exception: " + e);
         }
         return -1;
     }
@@ -73,22 +79,44 @@
                 WatchlistConfig.getInstance().setTestMode(fileStream);
             }
             pw.println("Success!");
-        } catch (RuntimeException | IOException ex) {
+        } catch (Exception ex) {
             pw.println("Error: " + ex.toString());
             return -1;
         }
         return 0;
     }
 
+    private int runForceGenerateReport() throws RemoteException {
+        final PrintWriter pw = getOutPrintWriter();
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            // Reset last report time
+            if (!WatchlistConfig.getInstance().isConfigSecure()) {
+                pw.println("Error: Cannot force generate report under production config");
+                return -1;
+            }
+            Settings.Global.putLong(mContext.getContentResolver(),
+                    Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L);
+            mService.forceReportWatchlistForTest(System.currentTimeMillis());
+            pw.println("Success!");
+        } catch (Exception ex) {
+            pw.println("Error: " + ex);
+            return -1;
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
         pw.println("Network watchlist manager commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("");
         pw.println("  set-test-config your_watchlist_config.xml");
-        pw.println();
-        Intent.printIntentArgsHelp(pw , "");
+        pw.println("    Set network watchlist test config file.");
+        pw.println("  force-generate-report");
+        pw.println("    Force generate watchlist test report.");
     }
 }
diff --git a/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java
index c1231fa..408a9ed 100644
--- a/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java
+++ b/services/core/java/com/android/server/net/watchlist/PrivacyUtils.java
@@ -19,6 +19,7 @@
 import android.privacy.DifferentialPrivacyEncoder;
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingConfig;
 import android.privacy.internal.longitudinalreporting.LongitudinalReportingEncoder;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -32,6 +33,7 @@
 class PrivacyUtils {
 
     private static final String TAG = "PrivacyUtils";
+    private static final boolean DEBUG = NetworkWatchlistService.DEBUG;
 
     /**
      * Parameters used for encoding watchlist reports.
@@ -84,6 +86,7 @@
     @VisibleForTesting
     static Map<String, Boolean> createDpEncodedReportMap(boolean isSecure, byte[] userSecret,
             List<String> appDigestList, WatchlistReportDbHelper.AggregatedResult aggregatedResult) {
+        if (DEBUG) Slog.i(TAG, "createDpEncodedReportMap start");
         final int appDigestListSize = appDigestList.size();
         final HashMap<String, Boolean> resultMap = new HashMap<>(appDigestListSize);
         for (int i = 0; i < appDigestListSize; i++) {
@@ -93,6 +96,7 @@
                     ? createSecureDPEncoder(userSecret, appDigest)
                     : createInsecureDPEncoderForTest(appDigest);
             final boolean visitedWatchlist = aggregatedResult.appDigestList.contains(appDigest);
+            if (DEBUG) Slog.i(TAG, appDigest + ": " + visitedWatchlist);
             // Get the least significant bit of first byte, and set result to True if it is 1
             boolean encodedVisitedWatchlist = ((int) encoder.encodeBoolean(visitedWatchlist)[0]
                     & 0x1) == 0x1;
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
index e8b39c0..b331b9c 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistLoggingHandler.java
@@ -43,6 +43,7 @@
 import java.io.IOException;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
+import java.util.GregorianCalendar;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -61,6 +62,8 @@
     static final int LOG_WATCHLIST_EVENT_MSG = 1;
     @VisibleForTesting
     static final int REPORT_RECORDS_IF_NECESSARY_MSG = 2;
+    @VisibleForTesting
+    static final int FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG = 3;
 
     private static final long ONE_DAY_MS = TimeUnit.DAYS.toMillis(1);
     private static final String DROPBOX_TAG = "network_watchlist_report";
@@ -110,7 +113,15 @@
                 break;
             }
             case REPORT_RECORDS_IF_NECESSARY_MSG:
-                tryAggregateRecords();
+                tryAggregateRecords(getLastMidnightTime());
+                break;
+            case FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG:
+                if (msg.obj instanceof Long) {
+                    long lastRecordTime = (Long) msg.obj;
+                    tryAggregateRecords(lastRecordTime);
+                } else {
+                    Slog.e(TAG, "Msg.obj needs to be a Long object.");
+                }
                 break;
             default: {
                 Slog.d(TAG, "WatchlistLoggingHandler received an unknown of message.");
@@ -146,6 +157,12 @@
         sendMessage(msg);
     }
 
+    public void forceReportWatchlistForTest(long lastReportTime) {
+        final Message msg = obtainMessage(FORCE_REPORT_RECORDS_NOW_FOR_TEST_MSG);
+        msg.obj = lastReportTime;
+        sendMessage(msg);
+    }
+
     /**
      * Insert network traffic event to watchlist async queue processor.
      */
@@ -177,8 +194,14 @@
     }
 
     private boolean insertRecord(int uid, String cncHost, long timestamp) {
+        if (DEBUG) {
+            Slog.i(TAG, "trying to insert record with host: " + cncHost + ", uid: " + uid);
+        }
         if (!mConfig.isConfigSecure() && !isPackageTestOnly(uid)) {
             // Skip package if config is not secure and package is not TestOnly app.
+            if (DEBUG) {
+                Slog.i(TAG, "uid: " + uid + " is not test only package");
+            }
             return true;
         }
         final byte[] digest = getDigestFromUid(uid);
@@ -187,50 +210,56 @@
             return false;
         }
         final boolean result = mDbHelper.insertNewRecord(digest, cncHost, timestamp);
-        tryAggregateRecords();
         return result;
     }
 
-    private boolean shouldReportNetworkWatchlist() {
+    private boolean shouldReportNetworkWatchlist(long lastRecordTime) {
         final long lastReportTime = Settings.Global.getLong(mResolver,
                 Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME, 0L);
-        final long currentTimestamp = System.currentTimeMillis();
-        if (currentTimestamp < lastReportTime) {
+        if (lastRecordTime < lastReportTime) {
             Slog.i(TAG, "Last report time is larger than current time, reset report");
-            mDbHelper.cleanup();
+            mDbHelper.cleanup(lastReportTime);
             return false;
         }
-        return currentTimestamp >= lastReportTime + ONE_DAY_MS;
+        return lastRecordTime >= lastReportTime + ONE_DAY_MS;
     }
 
-    private void tryAggregateRecords() {
-        // Check if it's necessary to generate watchlist report now.
-        if (!shouldReportNetworkWatchlist()) {
-            Slog.i(TAG, "No need to aggregate record yet.");
-            return;
-        }
-        Slog.i(TAG, "Start aggregating watchlist records.");
-        if (mDropBoxManager != null && mDropBoxManager.isTagEnabled(DROPBOX_TAG)) {
-            Settings.Global.putLong(mResolver,
-                    Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
-                    System.currentTimeMillis());
-            final WatchlistReportDbHelper.AggregatedResult aggregatedResult =
-                    mDbHelper.getAggregatedRecords();
-            if (aggregatedResult == null) {
-                Slog.i(TAG, "Cannot get result from database");
+    private void tryAggregateRecords(long lastRecordTime) {
+        long startTime = System.currentTimeMillis();
+        try {
+            // Check if it's necessary to generate watchlist report now.
+            if (!shouldReportNetworkWatchlist(lastRecordTime)) {
+                Slog.i(TAG, "No need to aggregate record yet.");
                 return;
             }
-            // Get all digests for watchlist report, it should include all installed
-            // application digests and previously recorded app digests.
-            final List<String> digestsForReport = getAllDigestsForReport(aggregatedResult);
-            final byte[] secretKey = mSettings.getPrivacySecretKey();
-            final byte[] encodedResult = ReportEncoder.encodeWatchlistReport(mConfig,
-                    secretKey, digestsForReport, aggregatedResult);
-            if (encodedResult != null) {
-                addEncodedReportToDropBox(encodedResult);
+            Slog.i(TAG, "Start aggregating watchlist records.");
+            if (mDropBoxManager != null && mDropBoxManager.isTagEnabled(DROPBOX_TAG)) {
+                Settings.Global.putLong(mResolver,
+                        Settings.Global.NETWORK_WATCHLIST_LAST_REPORT_TIME,
+                        lastRecordTime);
+                final WatchlistReportDbHelper.AggregatedResult aggregatedResult =
+                        mDbHelper.getAggregatedRecords(lastRecordTime);
+                if (aggregatedResult == null) {
+                    Slog.i(TAG, "Cannot get result from database");
+                    return;
+                }
+                // Get all digests for watchlist report, it should include all installed
+                // application digests and previously recorded app digests.
+                final List<String> digestsForReport = getAllDigestsForReport(aggregatedResult);
+                final byte[] secretKey = mSettings.getPrivacySecretKey();
+                final byte[] encodedResult = ReportEncoder.encodeWatchlistReport(mConfig,
+                        secretKey, digestsForReport, aggregatedResult);
+                if (encodedResult != null) {
+                    addEncodedReportToDropBox(encodedResult);
+                }
+            } else {
+                Slog.w(TAG, "Network Watchlist dropbox tag is not enabled");
             }
+            mDbHelper.cleanup(lastRecordTime);
+        } finally {
+            long endTime = System.currentTimeMillis();
+            Slog.i(TAG, "Milliseconds spent on tryAggregateRecords(): " + (endTime - startTime));
         }
-        mDbHelper.cleanup();
     }
 
     /**
@@ -379,4 +408,19 @@
         }
         return subDomainList.toArray(new String[0]);
     }
+
+    static long getLastMidnightTime() {
+        return getMidnightTimestamp(0);
+    }
+
+    static long getMidnightTimestamp(int daysBefore) {
+        java.util.Calendar date = new GregorianCalendar();
+        // reset hour, minutes, seconds and millis
+        date.set(java.util.Calendar.HOUR_OF_DAY, 0);
+        date.set(java.util.Calendar.MINUTE, 0);
+        date.set(java.util.Calendar.SECOND, 0);
+        date.set(java.util.Calendar.MILLISECOND, 0);
+        date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore);
+        return date.getTimeInMillis();
+    }
 }
diff --git a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
index 4b577bb..632ab81 100644
--- a/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
+++ b/services/core/java/com/android/server/net/watchlist/WatchlistReportDbHelper.java
@@ -141,11 +141,10 @@
     }
 
     /**
-     * Aggregate all records before most recent local midnight in database, and return a
+     * Aggregate all records in database before input timestamp, and return a
      * rappor encoded result.
      */
-    public AggregatedResult getAggregatedRecords() {
-        final long lastMidnightTime = getLastMidnightTime();
+    public AggregatedResult getAggregatedRecords(long untilTimestamp) {
         final String selectStatement = WhiteListReportContract.TIMESTAMP + " < ?";
 
         final SQLiteDatabase db = getReadableDatabase();
@@ -153,7 +152,7 @@
         try {
             c = db.query(true /* distinct */,
                     WhiteListReportContract.TABLE, DIGEST_DOMAIN_PROJECTION, selectStatement,
-                    new String[]{"" + lastMidnightTime}, null, null,
+                    new String[]{Long.toString(untilTimestamp)}, null, null,
                     null, null);
             if (c == null) {
                 return null;
@@ -181,29 +180,13 @@
     }
 
     /**
-     * Remove all the records before most recent local midnight.
+     * Remove all the records before input timestamp.
      *
      * @return True if success.
      */
-    public boolean cleanup() {
+    public boolean cleanup(long untilTimestamp) {
         final SQLiteDatabase db = getWritableDatabase();
-        final long midnightTime = getLastMidnightTime();
-        final String clause = WhiteListReportContract.TIMESTAMP + "< " + midnightTime;
+        final String clause = WhiteListReportContract.TIMESTAMP + "< " + untilTimestamp;
         return db.delete(WhiteListReportContract.TABLE, clause, null) != 0;
     }
-
-    static long getLastMidnightTime() {
-        return getMidnightTimestamp(0);
-    }
-
-    static long getMidnightTimestamp(int daysBefore) {
-        java.util.Calendar date = new GregorianCalendar();
-        // reset hour, minutes, seconds and millis
-        date.set(java.util.Calendar.HOUR_OF_DAY, 0);
-        date.set(java.util.Calendar.MINUTE, 0);
-        date.set(java.util.Calendar.SECOND, 0);
-        date.set(java.util.Calendar.MILLISECOND, 0);
-        date.add(java.util.Calendar.DAY_OF_MONTH, -daysBefore);
-        return date.getTimeInMillis();
-    }
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7842d5..d5a32aa 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -982,7 +982,11 @@
                     Slog.w(TAG, getCaption() + " binding died: " + name);
                     synchronized (mMutex) {
                         mServicesBinding.remove(servicesBindingTag);
-                        mContext.unbindService(this);
+                        try {
+                            mContext.unbindService(this);
+                        } catch (IllegalArgumentException e) {
+                            Slog.e(TAG, "failed to unbind " + name, e);
+                        }
                         if (!mServicesRebinding.contains(servicesBindingTag)) {
                             mServicesRebinding.add(servicesBindingTag);
                             mHandler.postDelayed(new Runnable() {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 59cd561..f31ca0a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -182,6 +182,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.notification.SystemNotificationChannels;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.util.ArrayUtils;
@@ -898,6 +899,8 @@
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) {
+                // update system notification channels
+                SystemNotificationChannels.createAll(context);
                 mZenModeHelper.updateDefaultZenRules();
                 mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser());
             }
@@ -2149,7 +2152,8 @@
                 final NotificationChannel channel = channels.get(i);
                 Preconditions.checkNotNull(channel, "channel in list is null");
                 mRankingHelper.createNotificationChannel(pkg, uid, channel,
-                        true /* fromTargetApp */);
+                        true /* fromTargetApp */, mConditionProviders.isPackageOrComponentAllowed(
+                                pkg, UserHandle.getUserId(uid)));
                 mListeners.notifyNotificationChannelChanged(pkg,
                         UserHandle.getUserHandleForUid(uid),
                         mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
@@ -4691,11 +4695,12 @@
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
         boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
-        // do not play notifications if there is a user of exclusive audio focus
-        // or the device is in vibrate mode
-        if (!mAudioManager.isAudioFocusExclusive() && (mAudioManager.getRingerModeInternal()
-                != AudioManager.RINGER_MODE_VIBRATE || mAudioManager.getStreamVolume(
-                AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
+        // play notifications if there is no user of exclusive audio focus
+        // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
+        //   VIBRATE ringer mode)
+        if (!mAudioManager.isAudioFocusExclusive()
+                && (mAudioManager.getStreamVolume(
+                        AudioAttributes.toLegacyStreamType(record.getAudioAttributes())) != 0)) {
             final long identity = Binder.clearCallingIdentity();
             try {
                 final IRingtonePlayer player = mAudioManager.getRingtonePlayer();
@@ -5969,6 +5974,7 @@
     }
 
     private boolean isPackageSuspendedForUser(String pkg, int uid) {
+        final long identity = Binder.clearCallingIdentity();
         int userId = UserHandle.getUserId(uid);
         try {
             return mPackageManager.isPackageSuspendedForUser(pkg, userId);
@@ -5977,6 +5983,8 @@
         } catch (IllegalArgumentException ex) {
             // Package not found.
             return false;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index b1b0bf2..af64683 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -38,7 +38,7 @@
     ParceledListSlice<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
             int uid, boolean includeDeleted, boolean includeNonGrouped);
     void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
-            boolean fromTargetApp);
+            boolean fromTargetApp, boolean hasDndAccess);
     void updateNotificationChannel(String pkg, int uid, NotificationChannel channel, boolean fromUser);
     NotificationChannel getNotificationChannel(String pkg, int uid, String channelId, boolean includeDeleted);
     void deleteNotificationChannel(String pkg, int uid, String channelId);
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index f163113..98d5c9a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -569,7 +569,7 @@
 
     @Override
     public void createNotificationChannel(String pkg, int uid, NotificationChannel channel,
-            boolean fromTargetApp) {
+            boolean fromTargetApp, boolean hasDndAccess) {
         Preconditions.checkNotNull(pkg);
         Preconditions.checkNotNull(channel);
         Preconditions.checkNotNull(channel.getId());
@@ -610,8 +610,9 @@
                 existing.setImportance(channel.getImportance());
             }
 
-            // system apps can bypass dnd if the user hasn't changed any fields on the channel yet
-            if (existing.getUserLockedFields() == 0 & isSystemApp) {
+            // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+            // fields on the channel yet
+            if (existing.getUserLockedFields() == 0 && (isSystemApp || hasDndAccess)) {
                 existing.setBypassDnd(channel.canBypassDnd());
             }
 
@@ -624,7 +625,7 @@
         }
 
         // Reset fields that apps aren't allowed to set.
-        if (fromTargetApp && !isSystemApp) {
+        if (fromTargetApp && !(isSystemApp || hasDndAccess)) {
             channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
         }
         if (fromTargetApp) {
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index c0c66b2..6cf8f86 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -468,12 +468,14 @@
         private final LinkedList<String> mPendingLookups;
         private final Context mContext;
 
+        // Amount of time to wait for a result from the contacts db before rechecking affinity.
+        private static final long LOOKUP_TIME = 1000;
         private float mContactAffinity = NONE;
         private NotificationRecord mRecord;
 
         private PeopleRankingReconsideration(Context context, String key,
                 LinkedList<String> pendingLookups) {
-            super(key);
+            super(key, LOOKUP_TIME);
             mContext = context;
             mPendingLookups = pendingLookups;
         }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5c82343..586abc1 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -933,11 +933,7 @@
                 }
                 break;
             case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
-                if (ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(mConfig)
-                        && ringerModeInternal != AudioManager.RINGER_MODE_SILENT) {
-                    setPreviousRingerModeSetting(ringerModeInternal);
-                    newRingerModeInternal = AudioManager.RINGER_MODE_SILENT;
-                }
+                // do not apply zen to ringer, streams zen muted in AudioService
                 break;
             case Global.ZEN_MODE_OFF:
                 if (ringerModeInternal == AudioManager.RINGER_MODE_SILENT) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index e315bc5..0774672 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -472,10 +472,17 @@
 
         if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
             mAbortPostBootUpdate.set(true);
+
+            // Do not reschedule.
+            // TODO: We should reschedule if we didn't process all apps, yet.
+            return false;
         } else {
             mAbortIdleOptimization.set(true);
+
+            // Reschedule the run.
+            // TODO: Should this be dependent on the stop reason?
+            return true;
         }
-        return false;
     }
 
     private void notifyPinService(ArraySet<String> updatedPackages) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 24abf86..9d3f48b 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -484,11 +484,11 @@
         }
     }
 
-    public void installApkVerity(String filePath, FileDescriptor verityInput)
+    public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
         try {
-            mInstalld.installApkVerity(filePath, verityInput);
+            mInstalld.installApkVerity(filePath, verityInput, contentSize);
         } catch (Exception e) {
             throw InstallerException.from(e);
         }
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 61c6be7..9807342 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -449,13 +449,15 @@
             pw.increaseIndent();
 
             for (String isa : dexCodeInstructionSets) {
-                String status = null;
                 try {
-                    status = DexFile.getDexFileStatus(path, isa);
+                    String[] status = DexFile.getDexFileOptimizationStatus(path, isa);
+                    String compilationStatus = status[0];
+                    String compilationReason = status[1];
+                    pw.println(isa + ": [status=" + compilationStatus
+                            +"] reason=[" + compilationReason + "]");
                 } catch (IOException ioe) {
-                     status = "[Exception]: " + ioe.getMessage();
+                    pw.println(isa + ": [Exception]: " + ioe.getMessage());
                 }
-                pw.println(isa + ": " + status);
             }
 
             if (useInfo.isUsedByOtherApps(path)) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 950d8df..cb1972f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -92,6 +92,7 @@
 import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
 import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME;
 import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME;
+import static com.android.internal.util.ArrayUtils.appendElement;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet;
@@ -778,8 +779,14 @@
             return PackageManagerService.this.hasSystemFeature(feature, 0);
         }
 
-        final List<PackageParser.Package> getStaticOverlayPackagesLocked(
+        final List<PackageParser.Package> getStaticOverlayPackages(
                 Collection<PackageParser.Package> allPackages, String targetPackageName) {
+            if ("android".equals(targetPackageName)) {
+                // Static RROs targeting to "android", ie framework-res.apk, are already applied by
+                // native AssetManager.
+                return null;
+            }
+
             List<PackageParser.Package> overlayPackages = null;
             for (PackageParser.Package p : allPackages) {
                 if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
@@ -800,16 +807,8 @@
             return overlayPackages;
         }
 
-        @GuardedBy("mInstallLock")
-        final String[] getStaticOverlayPathsLocked(Collection<PackageParser.Package> allPackages,
-                String targetPackageName, String targetPath) {
-            if ("android".equals(targetPackageName)) {
-                // Static RROs targeting to "android", ie framework-res.apk, are already applied by
-                // native AssetManager.
-                return null;
-            }
-            List<PackageParser.Package> overlayPackages =
-                    getStaticOverlayPackagesLocked(allPackages, targetPackageName);
+        final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
+                String targetPath) {
             if (overlayPackages == null || overlayPackages.isEmpty()) {
                 return null;
             }
@@ -845,9 +844,15 @@
         }
 
         String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            synchronized (mPackages) {
-                return getStaticOverlayPathsLocked(
-                        mPackages.values(), targetPackageName, targetPath);
+            List<PackageParser.Package> overlayPackages;
+            synchronized (mInstallLock) {
+                synchronized (mPackages) {
+                    overlayPackages = getStaticOverlayPackages(
+                            mPackages.values(), targetPackageName);
+                }
+                // It is safe to keep overlayPackages without holding mPackages because static overlay
+                // packages can't be uninstalled or disabled.
+                return getStaticOverlayPaths(overlayPackages, targetPath);
             }
         }
 
@@ -881,9 +886,13 @@
         synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
             // We can trust mOverlayPackages without holding mPackages because package uninstall
             // can't happen while running parallel parsing.
-            // Moreover holding mPackages on each parsing thread causes dead-lock.
+            // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
+            // because mInstallLock is held before running parallel parsing.
+            // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
             return mOverlayPackages == null ? null :
-                    getStaticOverlayPathsLocked(mOverlayPackages, targetPackageName, targetPath);
+                    getStaticOverlayPaths(
+                            getStaticOverlayPackages(mOverlayPackages, targetPackageName),
+                            targetPath);
         }
     }
 
@@ -10355,7 +10364,7 @@
 
         if (Build.IS_DEBUGGABLE &&
                 pkg.isPrivileged() &&
-                !SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, true)) {
+                SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
             PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
         }
 
@@ -13987,18 +13996,15 @@
             return packageNames;
         }
 
-        // List of package names for whom the suspended state has changed.
-        final List<String> changedPackages = new ArrayList<>(packageNames.length);
-        // List of package names for whom the suspended state is not set as requested in this
-        // method.
+        final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
         final List<String> unactionedPackages = new ArrayList<>(packageNames.length);
         final long callingId = Binder.clearCallingIdentity();
         try {
             synchronized (mPackages) {
                 for (int i = 0; i < packageNames.length; i++) {
                     final String packageName = packageNames[i];
-                    if (packageName == callingPackage) {
-                        Slog.w(TAG, "Calling package: " + callingPackage + "trying to "
+                    if (callingPackage.equals(packageName)) {
+                        Slog.w(TAG, "Calling package: " + callingPackage + " trying to "
                                 + (suspended ? "" : "un") + "suspend itself. Ignoring");
                         unactionedPackages.add(packageName);
                         continue;
@@ -14018,17 +14024,18 @@
                         }
                         pkgSetting.setSuspended(suspended, callingPackage, appExtras,
                                 launcherExtras, userId);
-                        changedPackages.add(packageName);
+                        changedPackagesList.add(packageName);
                     }
                 }
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
         }
-        // TODO (b/75036698): Also send each package a broadcast when suspended state changed
-        if (!changedPackages.isEmpty()) {
-            sendPackagesSuspendedForUser(changedPackages.toArray(
-                    new String[changedPackages.size()]), userId, suspended);
+        if (!changedPackagesList.isEmpty()) {
+            final String[] changedPackages = changedPackagesList.toArray(
+                    new String[changedPackagesList.size()]);
+            sendPackagesSuspendedForUser(changedPackages, userId, suspended);
+            sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, appExtras, userId);
             synchronized (mPackages) {
                 scheduleWritePackageRestrictionsLocked(userId);
             }
@@ -14038,7 +14045,7 @@
     }
 
     @Override
-    public PersistableBundle getPackageSuspendedAppExtras(String packageName, int userId) {
+    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);
@@ -14049,7 +14056,10 @@
                 throw new IllegalArgumentException("Unknown target package: " + packageName);
             }
             final PackageUserState packageUserState = ps.readUserState(userId);
-            return packageUserState.suspended ? packageUserState.suspendedAppExtras : null;
+            if (packageUserState.suspended) {
+                return packageUserState.suspendedAppExtras;
+            }
+            return null;
         }
     }
 
@@ -14065,12 +14075,49 @@
             }
             final PackageUserState packageUserState = ps.readUserState(userId);
             if (packageUserState.suspended) {
-                // TODO (b/75036698): Also send this package a broadcast with the new app extras
                 packageUserState.suspendedAppExtras = appExtras;
+                sendMyPackageSuspendedOrUnsuspended(new String[] {packageName}, true, appExtras,
+                        userId);
             }
         }
     }
 
+    private void sendMyPackageSuspendedOrUnsuspended(String[] affectedPackages, boolean suspended,
+            PersistableBundle appExtras, int userId) {
+        final String action;
+        final Bundle intentExtras = new Bundle();
+        if (suspended) {
+            action = Intent.ACTION_MY_PACKAGE_SUSPENDED;
+            if (appExtras != null) {
+                final Bundle bundledAppExtras = new Bundle(appExtras.deepCopy());
+                intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, bundledAppExtras);
+            }
+        } else {
+            action = Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final IActivityManager am = ActivityManager.getService();
+                    if (am == null) {
+                        Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                                + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                        return;
+                    }
+                    final int[] targetUserIds = new int[] {userId};
+                    for (String packageName : affectedPackages) {
+                        doSendBroadcast(am, action, null, intentExtras,
+                                Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                                targetUserIds, false);
+                    }
+                } catch (RemoteException ex) {
+                    // Shouldn't happen as AMS is in the same process.
+                }
+            }
+        });
+    }
+
     @Override
     public boolean isPackageSuspendedForUser(String packageName, int userId) {
         final int callingUid = Binder.getCallingUid();
@@ -16756,12 +16803,6 @@
                 if (userId != UserHandle.USER_ALL) {
                     ps.setInstalled(true, userId);
                     ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
-                } else {
-                    for (int currentUserId : sUserManager.getUserIds()) {
-                        ps.setInstalled(true, currentUserId);
-                        ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
-                                installerPackageName);
-                    }
                 }
 
                 // When replacing an existing package, preserve the original install reason for all
@@ -17281,8 +17322,11 @@
                     if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath);
                     FileDescriptor fd = result.getUnownedFileDescriptor();
                     try {
-                        mInstaller.installApkVerity(apkPath, fd);
-                    } catch (InstallerException e) {
+                        final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath);
+                        mInstaller.installApkVerity(apkPath, fd, result.getContentSize());
+                        mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash);
+                    } catch (InstallerException | IOException | DigestException |
+                             NoSuchAlgorithmException e) {
                         res.setError(INSTALL_FAILED_INTERNAL_ERROR,
                                 "Failed to set up verity: " + e);
                         return;
@@ -18890,7 +18934,7 @@
         return true;
     }
 
-    private static final class ClearStorageConnection implements ServiceConnection {
+    private final class ClearStorageConnection implements ServiceConnection {
         IMediaContainerService mContainerService;
 
         @Override
@@ -21611,37 +21655,35 @@
                 // the given package is involved with.
                 if (dumpState.onTitlePrinted()) pw.println();
 
-                try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120)) {
-                    ipw.println();
-                    ipw.println("Frozen packages:");
-                    ipw.increaseIndent();
-                    if (mFrozenPackages.size() == 0) {
-                        ipw.println("(none)");
-                    } else {
-                        for (int i = 0; i < mFrozenPackages.size(); i++) {
-                            ipw.println(mFrozenPackages.valueAt(i));
-                        }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println();
+                ipw.println("Frozen packages:");
+                ipw.increaseIndent();
+                if (mFrozenPackages.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mFrozenPackages.size(); i++) {
+                        ipw.println(mFrozenPackages.valueAt(i));
                     }
-                    ipw.decreaseIndent();
                 }
+                ipw.decreaseIndent();
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_VOLUMES) && packageName == null) {
                 if (dumpState.onTitlePrinted()) pw.println();
 
-                try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120)) {
-                    ipw.println();
-                    ipw.println("Loaded volumes:");
-                    ipw.increaseIndent();
-                    if (mLoadedVolumes.size() == 0) {
-                        ipw.println("(none)");
-                    } else {
-                        for (int i = 0; i < mLoadedVolumes.size(); i++) {
-                            ipw.println(mLoadedVolumes.valueAt(i));
-                        }
+                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
+                ipw.println();
+                ipw.println("Loaded volumes:");
+                ipw.increaseIndent();
+                if (mLoadedVolumes.size() == 0) {
+                    ipw.println("(none)");
+                } else {
+                    for (int i = 0; i < mLoadedVolumes.size(); i++) {
+                        ipw.println(mLoadedVolumes.valueAt(i));
                     }
-                    ipw.decreaseIndent();
                 }
+                ipw.decreaseIndent();
             }
 
             if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
@@ -21770,63 +21812,61 @@
     }
 
     private void dumpDexoptStateLPr(PrintWriter pw, String packageName) {
-        try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ")) {
-            ipw.println();
-            ipw.println("Dexopt state:");
-            ipw.increaseIndent();
-            Collection<PackageParser.Package> packages = null;
-            if (packageName != null) {
-                PackageParser.Package targetPackage = mPackages.get(packageName);
-                if (targetPackage != null) {
-                    packages = Collections.singletonList(targetPackage);
-                } else {
-                    ipw.println("Unable to find package: " + packageName);
-                    return;
-                }
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println();
+        ipw.println("Dexopt state:");
+        ipw.increaseIndent();
+        Collection<PackageParser.Package> packages = null;
+        if (packageName != null) {
+            PackageParser.Package targetPackage = mPackages.get(packageName);
+            if (targetPackage != null) {
+                packages = Collections.singletonList(targetPackage);
             } else {
-                packages = mPackages.values();
+                ipw.println("Unable to find package: " + packageName);
+                return;
             }
+        } else {
+            packages = mPackages.values();
+        }
 
-            for (PackageParser.Package pkg : packages) {
-                ipw.println("[" + pkg.packageName + "]");
-                ipw.increaseIndent();
-                mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
-                        mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
-                ipw.decreaseIndent();
-            }
+        for (PackageParser.Package pkg : packages) {
+            ipw.println("[" + pkg.packageName + "]");
+            ipw.increaseIndent();
+            mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
+                    mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
+            ipw.decreaseIndent();
         }
     }
 
     private void dumpCompilerStatsLPr(PrintWriter pw, String packageName) {
-        try (final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ")) {
-            ipw.println();
-            ipw.println("Compiler stats:");
-            ipw.increaseIndent();
-            Collection<PackageParser.Package> packages = null;
-            if (packageName != null) {
-                PackageParser.Package targetPackage = mPackages.get(packageName);
-                if (targetPackage != null) {
-                    packages = Collections.singletonList(targetPackage);
-                } else {
-                    ipw.println("Unable to find package: " + packageName);
-                    return;
-                }
+        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
+        ipw.println();
+        ipw.println("Compiler stats:");
+        ipw.increaseIndent();
+        Collection<PackageParser.Package> packages = null;
+        if (packageName != null) {
+            PackageParser.Package targetPackage = mPackages.get(packageName);
+            if (targetPackage != null) {
+                packages = Collections.singletonList(targetPackage);
             } else {
-                packages = mPackages.values();
+                ipw.println("Unable to find package: " + packageName);
+                return;
             }
+        } else {
+            packages = mPackages.values();
+        }
 
-            for (PackageParser.Package pkg : packages) {
-                ipw.println("[" + pkg.packageName + "]");
-                ipw.increaseIndent();
+        for (PackageParser.Package pkg : packages) {
+            ipw.println("[" + pkg.packageName + "]");
+            ipw.increaseIndent();
 
-                CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
-                if (stats == null) {
-                    ipw.println("(No recorded stats)");
-                } else {
-                    stats.dump(ipw);
-                }
-                ipw.decreaseIndent();
+            CompilerStats.PackageStats stats = getCompilerPackageStats(pkg.packageName);
+            if (stats == null) {
+                ipw.println("(No recorded stats)");
+            } else {
+                stats.dump(ipw);
             }
+            ipw.decreaseIndent();
         }
     }
 
@@ -24053,6 +24093,33 @@
         }
     }
 
+    @Override
+    public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
+        enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp");
+        synchronized (mPackages) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mDefaultPermissionPolicy.grantDefaultPermissionsToActiveLuiApp(
+                        packageName, userId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
+    @Override
+    public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
+        enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps");
+        synchronized (mPackages) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                mDefaultPermissionPolicy.revokeDefaultPermissionsFromLuiApps(packageNames, userId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
     private static void enforceSystemOrPhoneCaller(String tag) {
         int callingUid = Binder.getCallingUid();
         if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index fce8285..5fc5bac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -25,7 +25,7 @@
  */
 public class PackageManagerServiceCompilerMapping {
     // Names for compilation reasons.
-    static final String REASON_STRINGS[] = {
+    public static final String REASON_STRINGS[] = {
             "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
     };
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 5060c4d..853081a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -331,18 +331,18 @@
                 ZipEntry entry = it.next();
                 if (entry.getName().endsWith(".dex")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
-                        Slog.wtf(TAG, "APK " + fileName + " has compressed dex code " +
+                        Slog.w(TAG, "APK " + fileName + " has compressed dex code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & 0x3) != 0) {
-                        Slog.wtf(TAG, "APK " + fileName + " has unaligned dex code " +
+                        Slog.w(TAG, "APK " + fileName + " has unaligned dex code " +
                                 entry.getName());
                     }
                 } else if (entry.getName().endsWith(".so")) {
                     if (entry.getMethod() != ZipEntry.STORED) {
-                        Slog.wtf(TAG, "APK " + fileName + " has compressed native code " +
+                        Slog.w(TAG, "APK " + fileName + " has compressed native code " +
                                 entry.getName());
                     } else if ((entry.getDataOffset() & (0x1000 - 1)) != 0) {
-                        Slog.wtf(TAG, "APK " + fileName + " has unaligned native code " +
+                        Slog.w(TAG, "APK " + fileName + " has unaligned native code " +
                                 entry.getName());
                     }
                 }
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index c362274..9ca02ba 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -559,7 +559,7 @@
                                     android.provider.Settings.Global.AIRPLANE_MODE_ON, 0);
                             // Post the intent.
                             Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-                            intent.putExtra("state", 0);
+                            intent.putExtra("state", false);
                             context.sendBroadcastAsUser(intent, UserHandle.ALL);
                         }
                     }
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 41ed6f2..cdafe57 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -27,19 +27,18 @@
 import android.content.pm.dex.ArtManager.ProfileType;
 import android.content.pm.dex.ArtManagerInternal;
 import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.content.pm.dex.PackageOptimizationInfo;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Handler;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.system.Os;
 import android.util.ArrayMap;
 import android.util.Slog;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
@@ -47,9 +46,8 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
-
+import com.android.server.pm.PackageManagerServiceCompilerMapping;
 import dalvik.system.DexFile;
-
 import dalvik.system.VMRuntime;
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -88,6 +86,10 @@
 
     private final Handler mHandler;
 
+    static {
+        verifyTronLoggingConstants();
+    }
+
     public ArtManagerService(IPackageManager pm, Installer installer, Object installLock) {
         mPackageManager = pm;
         mInstaller = installer;
@@ -423,6 +425,100 @@
         return result;
     }
 
+    // Constants used for logging compilation filter to TRON.
+    // DO NOT CHANGE existing values.
+    //
+    // NOTE: '-1' value is reserved for the case where we cannot produce a valid
+    // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
+    // ActivityMetricsLoggers.
+    private static final int TRON_COMPILATION_FILTER_ERROR = 0;
+    private static final int TRON_COMPILATION_FILTER_UNKNOWN = 1;
+    private static final int TRON_COMPILATION_FILTER_ASSUMED_VERIFIED = 2;
+    private static final int TRON_COMPILATION_FILTER_EXTRACT = 3;
+    private static final int TRON_COMPILATION_FILTER_VERIFY = 4;
+    private static final int TRON_COMPILATION_FILTER_QUICKEN = 5;
+    private static final int TRON_COMPILATION_FILTER_SPACE_PROFILE = 6;
+    private static final int TRON_COMPILATION_FILTER_SPACE = 7;
+    private static final int TRON_COMPILATION_FILTER_SPEED_PROFILE = 8;
+    private static final int TRON_COMPILATION_FILTER_SPEED = 9;
+    private static final int TRON_COMPILATION_FILTER_EVERYTHING_PROFILE = 10;
+    private static final int TRON_COMPILATION_FILTER_EVERYTHING = 11;
+    private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK = 12;
+    private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK = 13;
+    private static final int TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK = 14;
+
+    // Constants used for logging compilation reason to TRON.
+    // DO NOT CHANGE existing values.
+    //
+    // NOTE: '-1' value is reserved for the case where we cannot produce a valid
+    // PackageOptimizationInfo because the ArtManagerInternal is not ready to be used by the
+    // ActivityMetricsLoggers.
+    private static final int TRON_COMPILATION_REASON_ERROR = 0;
+    private static final int TRON_COMPILATION_REASON_UNKNOWN = 1;
+    private static final int TRON_COMPILATION_REASON_FIRST_BOOT = 2;
+    private static final int TRON_COMPILATION_REASON_BOOT = 3;
+    private static final int TRON_COMPILATION_REASON_INSTALL = 4;
+    private static final int TRON_COMPILATION_REASON_BG_DEXOPT = 5;
+    private static final int TRON_COMPILATION_REASON_AB_OTA = 6;
+    private static final int TRON_COMPILATION_REASON_INACTIVE = 7;
+    private static final int TRON_COMPILATION_REASON_SHARED = 8;
+
+    /**
+     * Convert the compilation reason to an int suitable to be logged to TRON.
+     */
+    private static int getCompilationReasonTronValue(String compilationReason) {
+        switch (compilationReason) {
+            case "unknown" : return TRON_COMPILATION_REASON_UNKNOWN;
+            case "error" : return TRON_COMPILATION_REASON_ERROR;
+            case "first-boot" : return TRON_COMPILATION_REASON_FIRST_BOOT;
+            case "boot" : return TRON_COMPILATION_REASON_BOOT;
+            case "install" : return TRON_COMPILATION_REASON_INSTALL;
+            case "bg-dexopt" : return TRON_COMPILATION_REASON_BG_DEXOPT;
+            case "ab-ota" : return TRON_COMPILATION_REASON_AB_OTA;
+            case "inactive" : return TRON_COMPILATION_REASON_INACTIVE;
+            case "shared" : return TRON_COMPILATION_REASON_SHARED;
+            default: return TRON_COMPILATION_REASON_UNKNOWN;
+        }
+    }
+
+    /**
+     * Convert the compilation filter to an int suitable to be logged to TRON.
+     */
+    private static int getCompilationFilterTronValue(String compilationFilter) {
+        switch (compilationFilter) {
+            case "error" : return TRON_COMPILATION_FILTER_ERROR;
+            case "unknown" : return TRON_COMPILATION_FILTER_UNKNOWN;
+            case "assume-verified" : return TRON_COMPILATION_FILTER_ASSUMED_VERIFIED;
+            case "extract" : return TRON_COMPILATION_FILTER_EXTRACT;
+            case "verify" : return TRON_COMPILATION_FILTER_VERIFY;
+            case "quicken" : return TRON_COMPILATION_FILTER_QUICKEN;
+            case "space-profile" : return TRON_COMPILATION_FILTER_SPACE_PROFILE;
+            case "space" : return TRON_COMPILATION_FILTER_SPACE;
+            case "speed-profile" : return TRON_COMPILATION_FILTER_SPEED_PROFILE;
+            case "speed" : return TRON_COMPILATION_FILTER_SPEED;
+            case "everything-profile" : return TRON_COMPILATION_FILTER_EVERYTHING_PROFILE;
+            case "everything" : return TRON_COMPILATION_FILTER_EVERYTHING;
+            case "run-from-apk" : return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK;
+            case "run-from-apk-fallback" :
+                return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
+            case "run-from-vdex-fallback" :
+                return TRON_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
+            default: return TRON_COMPILATION_FILTER_UNKNOWN;
+        }
+    }
+
+    private static void verifyTronLoggingConstants() {
+        for (int i = 0; i < PackageManagerServiceCompilerMapping.REASON_STRINGS.length; i++) {
+            String reason = PackageManagerServiceCompilerMapping.REASON_STRINGS[i];
+            int value = getCompilationReasonTronValue(reason);
+            if (value == TRON_COMPILATION_REASON_ERROR
+                    || value == TRON_COMPILATION_REASON_UNKNOWN) {
+                throw new IllegalArgumentException("Compilation reason not configured for TRON "
+                        + "logging: " + reason);
+            }
+        }
+    }
+
     private class ArtManagerInternalImpl extends ArtManagerInternal {
         @Override
         public PackageOptimizationInfo getPackageOptimizationInfo(
@@ -445,7 +541,11 @@
                 compilationReason = "error";
             }
 
-            return new PackageOptimizationInfo(compilationFilter, compilationReason);
+            int compilationFilterTronValue = getCompilationFilterTronValue(compilationFilter);
+            int compilationReasonTronValue = getCompilationReasonTronValue(compilationReason);
+
+            return new PackageOptimizationInfo(
+                    compilationFilterTronValue, compilationReasonTronValue);
         }
     }
 }
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 3116480..518d464 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -972,6 +972,7 @@
                 grantRuntimePermissions(imsServicePackage, MICROPHONE_PERMISSIONS, userId);
                 grantRuntimePermissions(imsServicePackage, LOCATION_PERMISSIONS, userId);
                 grantRuntimePermissions(imsServicePackage, CAMERA_PERMISSIONS, userId);
+                grantRuntimePermissions(imsServicePackage, CONTACTS_PERMISSIONS, userId);
             }
         }
     }
@@ -1010,6 +1011,32 @@
         }
     }
 
+    public void grantDefaultPermissionsToActiveLuiApp(String packageName, int userId) {
+        Log.i(TAG, "Granting permissions to active LUI app for user:" + userId);
+        if (packageName == null) {
+            return;
+        }
+        PackageParser.Package luiAppPackage = getSystemPackage(packageName);
+        if (luiAppPackage != null
+                && doesPackageSupportRuntimePermissions(luiAppPackage)) {
+            grantRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
+        }
+    }
+
+    public void revokeDefaultPermissionsFromLuiApps(String[] packageNames, int userId) {
+        Log.i(TAG, "Revoke permissions from LUI apps for user:" + userId);
+        if (packageNames == null) {
+            return;
+        }
+        for (String packageName : packageNames) {
+            PackageParser.Package luiAppPackage = getSystemPackage(packageName);
+            if (luiAppPackage != null
+                    && doesPackageSupportRuntimePermissions(luiAppPackage)) {
+                revokeRuntimePermissions(luiAppPackage, CAMERA_PERMISSIONS, true, userId);
+            }
+        }
+    }
+
     public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
         Log.i(TAG, "Granting permissions to default browser for user:" + userId);
         if (packageName == null) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4dc68ac..fd02347 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1330,6 +1330,9 @@
             mHandler.post(mHiddenNavPanic);
         }
 
+        // Abort possibly stuck animations.
+        mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
         // Latch power key state to detect screenshot chord.
         if (interactive && !mScreenshotChordPowerKeyTriggered
                 && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
@@ -3027,7 +3030,7 @@
 
         // Show IME over the keyguard if the target allows it
         boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this)
-                && showImeOverKeyguard;;
+                && showImeOverKeyguard;
 
         if (isKeyguardLocked() && isKeyguardOccluded()) {
             // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded.
@@ -4362,6 +4365,9 @@
      * given the situation with the keyguard.
      */
     void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {
+        // Abort possibly stuck animations.
+        mHandler.post(mWindowManagerFuncs::triggerAnimationFailsafe);
+
         if (respectKeyguard) {
             if (isKeyguardShowingAndNotOccluded()) {
                 // don't launch home if keyguard showing
@@ -7175,7 +7181,7 @@
     }
 
     @Override
-    public int rotationForOrientationLw(int orientation, int lastRotation) {
+    public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
         if (false) {
             Slog.v(TAG, "rotationForOrientationLw(orient="
                         + orientation + ", last=" + lastRotation
@@ -7196,7 +7202,11 @@
             }
 
             final int preferredRotation;
-            if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
+            if (!defaultDisplay) {
+                // For secondary displays we ignore things like displays sensors, docking mode and
+                // rotation lock, and always prefer a default rotation.
+                preferredRotation = Surface.ROTATION_0;
+            } else if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
                 // Ignore sensor when lid switch is open and rotation is forced.
                 preferredRotation = mLidOpenRotation;
             } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index ec0521d..0a6ae4e 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -407,7 +407,10 @@
         /**
          * Returns true if this window has been shown on screen at some time in
          * the past.  Must be called with the window manager lock held.
+         *
+         * @deprecated Use {@link #isDrawnLw} or any of the other drawn/visibility methods.
          */
+        @Deprecated
         public boolean hasDrawnLw();
 
         /**
@@ -649,6 +652,12 @@
                     return Integer.toString(lens);
             }
         }
+
+        /**
+         * Hint to window manager that the user has started a navigation action that should
+         * abort animations that have no timeout, in case they got stuck.
+         */
+        void triggerAnimationFailsafe();
     }
 
     /** Window has been added to the screen. */
@@ -1426,10 +1435,13 @@
      * @param orientation An orientation constant, such as
      * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE}.
      * @param lastRotation The most recently used rotation.
+     * @param defaultDisplay Flag indicating whether the rotation is computed for the default
+     *                       display. Currently for all non-default displays sensors, docking mode,
+     *                       rotation lock and other factors are ignored.
      * @return The surface rotation to use.
      */
     public int rotationForOrientationLw(@ActivityInfo.ScreenOrientation int orientation,
-            int lastRotation);
+            int lastRotation, boolean defaultDisplay);
 
     /**
      * Given an orientation constant and a rotation, returns true if the rotation
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 062b154..81a8c55 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -213,7 +213,8 @@
                     // There are no longer any keyguard windows on secondary displays, so pass
                     // INVALID_DISPLAY. All that means is that showWhenLocked activities on
                     // secondary displays now get to show.
-                    ActivityManager.getService().setLockScreenShown(true, INVALID_DISPLAY);
+                    ActivityManager.getService().setLockScreenShown(true /* keyguardShowing */,
+                            false /* aodShowing */, INVALID_DISPLAY);
                 } catch (RemoteException e) {
                     // Local call.
                 }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 941cd44..e56caf8 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -19,6 +19,8 @@
 import android.app.ActivityManager;
 import android.content.Context;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.security.IKeystoreService;
 import android.util.Slog;
 
 import com.android.internal.policy.IKeyguardService;
@@ -51,11 +53,16 @@
     private final LockPatternUtils mLockPatternUtils;
     private final StateCallback mCallback;
 
+    IKeystoreService mKeystoreService;
+
     public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) {
         mLockPatternUtils = new LockPatternUtils(context);
         mCurrentUserId = ActivityManager.getCurrentUser();
         mCallback = callback;
 
+        mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager
+                .getService("android.security.keystore"));
+
         try {
             service.addStateMonitorCallback(this);
         } catch (RemoteException e) {
@@ -86,6 +93,12 @@
     @Override // Binder interface
     public void onShowingStateChanged(boolean showing) {
         mIsShowing = showing;
+
+        try {
+            mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error informing keystore of screen lock", e);
+        }
     }
 
     @Override // Binder interface
@@ -130,4 +143,4 @@
         pw.println(prefix + "mTrusted=" + mTrusted);
         pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java
index d2d0e60..180f343 100644
--- a/services/core/java/com/android/server/security/VerityUtils.java
+++ b/services/core/java/com/android/server/security/VerityUtils.java
@@ -26,6 +26,7 @@
 import android.util.apk.ApkSignatureVerifier;
 import android.util.apk.ByteBufferFactory;
 import android.util.apk.SignatureNotFoundException;
+import android.util.Pair;
 import android.util.Slog;
 
 import java.io.FileDescriptor;
@@ -59,12 +60,15 @@
                 return SetupResult.skipped();
             }
 
-            shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash);
+            Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath,
+                    signedRootHash);
+            shm = result.first;
+            int contentSize = result.second;
             FileDescriptor rfd = shm.getFileDescriptor();
             if (rfd == null || !rfd.valid()) {
                 return SetupResult.failed();
             }
-            return SetupResult.ok(Os.dup(rfd));
+            return SetupResult.ok(Os.dup(rfd), contentSize);
         } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException |
                 SignatureNotFoundException | ErrnoException e) {
             Slog.e(TAG, "Failed to set up apk verity: ", e);
@@ -85,10 +89,20 @@
     }
 
     /**
-     * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given
-     * apk, in the form that can immediately be used for fsverity setup.
+     * {@see ApkSignatureVerifier#getVerityRootHash(String)}.
      */
-    private static SharedMemory generateApkVerityIntoSharedMemory(
+    public static byte[] getVerityRootHash(@NonNull String apkPath)
+            throws IOException, SignatureNotFoundException, SecurityException {
+        return ApkSignatureVerifier.getVerityRootHash(apkPath);
+    }
+
+    /**
+     * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains
+     * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used
+     * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has
+     * length equals to the returned {@code Integer}.
+     */
+    private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory(
             String apkPath, byte[] expectedRootHash)
             throws IOException, SecurityException, DigestException, NoSuchAlgorithmException,
                    SignatureNotFoundException {
@@ -101,6 +115,7 @@
             throw new SecurityException("Locally generated verity root hash does not match");
         }
 
+        int contentSize = shmBufferFactory.getBufferLimit();
         SharedMemory shm = shmBufferFactory.releaseSharedMemory();
         if (shm == null) {
             throw new IllegalStateException("Failed to generate verity tree into shared memory");
@@ -108,7 +123,7 @@
         if (!shm.setProtect(PROT_READ)) {
             throw new SecurityException("Failed to set up shared memory correctly");
         }
-        return shm;
+        return Pair.create(shm, contentSize);
     }
 
     public static class SetupResult {
@@ -123,22 +138,24 @@
 
         private final int mCode;
         private final FileDescriptor mFileDescriptor;
+        private final int mContentSize;
 
-        public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) {
-            return new SetupResult(RESULT_OK, fileDescriptor);
+        public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) {
+            return new SetupResult(RESULT_OK, fileDescriptor, contentSize);
         }
 
         public static SetupResult skipped() {
-            return new SetupResult(RESULT_SKIPPED, null);
+            return new SetupResult(RESULT_SKIPPED, null, -1);
         }
 
         public static SetupResult failed() {
-            return new SetupResult(RESULT_FAILED, null);
+            return new SetupResult(RESULT_FAILED, null, -1);
         }
 
-        private SetupResult(int code, FileDescriptor fileDescriptor) {
+        private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) {
             this.mCode = code;
             this.mFileDescriptor = fileDescriptor;
+            this.mContentSize = contentSize;
         }
 
         public boolean isFailed() {
@@ -152,6 +169,10 @@
         public @NonNull FileDescriptor getUnownedFileDescriptor() {
             return mFileDescriptor;
         }
+
+        public int getContentSize() {
+            return mContentSize;
+        }
     }
 
     /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */
@@ -188,5 +209,9 @@
             mShm = null;
             return tmp;
         }
+
+        public int getBufferLimit() {
+            return mBuffer == null ? -1 : mBuffer.limit();
+        }
     }
 }
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index 0b7d9d0..fd0b6f1 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -168,7 +168,8 @@
     }
 
     @Override
-    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException {
+    public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token)
+            throws RemoteException {
         verifyCaller(pkg);
         enforceAccess(pkg, uri);
         int user = Binder.getCallingUserHandle().getIdentifier();
@@ -210,7 +211,8 @@
     }
 
     @Override
-    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException {
+    public int checkSlicePermission(Uri uri, String pkg, int pid, int uid,
+            String[] autoGrantPermissions) throws RemoteException {
         if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
                 == PERMISSION_GRANTED) {
             return SliceManager.PERMISSION_GRANTED;
@@ -218,6 +220,11 @@
         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;
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index b3d28fc..753394d 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -39,6 +39,7 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.FileUtils;
 import android.os.IBinder;
 import android.os.IStatsCompanionService;
 import android.os.IStatsManager;
@@ -68,14 +69,21 @@
 import com.android.internal.os.KernelWakelockReader;
 import com.android.internal.os.KernelWakelockStats;
 import com.android.internal.os.PowerProfile;
+import com.android.internal.util.DumpUtils;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
+import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
+import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -89,14 +97,17 @@
      * How long to wait on an individual subsystem to return its stats.
      */
     private static final long EXTERNAL_STATS_SYNC_TIMEOUT_MILLIS = 2000;
+    private static final long MILLIS_IN_A_DAY = TimeUnit.DAYS.toMillis(1);
 
     public static final String RESULT_RECEIVER_CONTROLLER_KEY = "controller_activity";
+    public static final String CONFIG_DIR = "/data/misc/stats-service";
 
     static final String TAG = "StatsCompanionService";
     static final boolean DEBUG = false;
 
     public static final int CODE_DATA_BROADCAST = 1;
     public static final int CODE_SUBSCRIBER_BROADCAST = 1;
+    public static final int DEATH_THRESHOLD = 10;
 
     private final Context mContext;
     private final AlarmManager mAlarmManager;
@@ -119,6 +130,10 @@
             new StatFs(Environment.getRootDirectory().getAbsolutePath());
     private final StatFs mStatFsTemp =
             new StatFs(Environment.getDownloadCacheDirectory().getAbsolutePath());
+    @GuardedBy("sStatsdLock")
+    private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
+    @GuardedBy("sStatsdLock")
+    private final HashMap<Long, String> mDeletedFiles = new HashMap<>();
 
     private KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
     private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
@@ -156,7 +171,7 @@
                         informAllUidsLocked(context);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Failed to inform statsd latest update of all apps", e);
-                        forgetEverything();
+                        forgetEverythingLocked();
                     }
                 }
             }
@@ -466,34 +481,32 @@
     }
 
     @Override // Binder call
-    public void setPullingAlarms(long timestampMs, long intervalMs) {
-        enforceCallingPermission();
-        if (DEBUG)
-            Slog.d(TAG, "Setting pulling alarm for " + timestampMs + " every " + intervalMs + "ms");
-        final long callingToken = Binder.clearCallingIdentity();
-        try {
-            // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
-            // only fire when it awakens.
-            // This alarm is inexact, leaving its exactness completely up to the OS optimizations.
-            // TODO: totally inexact means that stats per bucket could be quite off. Is this okay?
-            mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, timestampMs, intervalMs,
-                    mPullingAlarmIntent);
-        } finally {
-            Binder.restoreCallingIdentity(callingToken);
-        }
+    public void setPullingAlarm(long nextPullTimeMs) {
+      enforceCallingPermission();
+      if (DEBUG)
+        Slog.d(TAG,
+            "Setting pulling alarm in about " + (nextPullTimeMs - SystemClock.elapsedRealtime()));
+      final long callingToken = Binder.clearCallingIdentity();
+      try {
+        // using ELAPSED_REALTIME, not ELAPSED_REALTIME_WAKEUP, so if device is asleep, will
+        // only fire when it awakens.
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME, nextPullTimeMs, mPullingAlarmIntent);
+      } finally {
+        Binder.restoreCallingIdentity(callingToken);
+      }
     }
 
     @Override // Binder call
-    public void cancelPullingAlarms() {
-        enforceCallingPermission();
-        if (DEBUG)
-            Slog.d(TAG, "Cancelling pulling alarm");
-        final long callingToken = Binder.clearCallingIdentity();
-        try {
-            mAlarmManager.cancel(mPullingAlarmIntent);
-        } finally {
-            Binder.restoreCallingIdentity(callingToken);
-        }
+    public void cancelPullingAlarm() {
+      enforceCallingPermission();
+      if (DEBUG)
+        Slog.d(TAG, "Cancelling pulling alarm");
+      final long callingToken = Binder.clearCallingIdentity();
+      try {
+        mAlarmManager.cancel(mPullingAlarmIntent);
+      } finally {
+        Binder.restoreCallingIdentity(callingToken);
+      }
     }
 
     private void addNetworkStats(
@@ -753,7 +766,7 @@
         });
     }
 
-    private void pullWifiActivityEnergyInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
+    private void pullWifiActivityInfo(int tagId, List<StatsLogEventWrapper> pulledData) {
         long token = Binder.clearCallingIdentity();
         if (mWifiManager == null) {
             mWifiManager =
@@ -921,8 +934,8 @@
                 pullKernelUidCpuActiveTime(tagId, ret);
                 break;
             }
-            case StatsLog.WIFI_ACTIVITY_ENERGY_INFO: {
-                pullWifiActivityEnergyInfo(tagId, ret);
+            case StatsLog.WIFI_ACTIVITY_INFO: {
+                pullWifiActivityInfo(tagId, ret);
                 break;
             }
             case StatsLog.MODEM_ACTIVITY_INFO: {
@@ -1057,7 +1070,7 @@
                     sStatsd.asBinder().linkToDeath(new StatsdDeathRecipient(), 0);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "linkToDeath(StatsdDeathRecipient) failed", e);
-                    forgetEverything();
+                    forgetEverythingLocked();
                 }
                 // Setup broadcast receiver for updates.
                 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
@@ -1089,7 +1102,7 @@
                 Slog.i(TAG, "Told statsd that StatsCompanionService is alive.");
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to inform statsd that statscompanion is ready", e);
-                forgetEverything();
+                forgetEverythingLocked();
             }
         }
     }
@@ -1098,18 +1111,60 @@
         @Override
         public void binderDied() {
             Slog.i(TAG, "Statsd is dead - erase all my knowledge.");
-            forgetEverything();
+            synchronized (sStatsdLock) {
+                long now = SystemClock.elapsedRealtime();
+                for (Long timeMillis : mDeathTimeMillis) {
+                    long ageMillis = now - timeMillis;
+                    if (ageMillis > MILLIS_IN_A_DAY) {
+                        mDeathTimeMillis.remove(timeMillis);
+                    }
+                }
+                for (Long timeMillis : mDeletedFiles.keySet()) {
+                    long ageMillis = now - timeMillis;
+                    if (ageMillis > MILLIS_IN_A_DAY * 7) {
+                        mDeletedFiles.remove(timeMillis);
+                    }
+                }
+                mDeathTimeMillis.add(now);
+                if (mDeathTimeMillis.size() >= DEATH_THRESHOLD) {
+                    mDeathTimeMillis.clear();
+                    File[] configs = FileUtils.listFilesOrEmpty(new File(CONFIG_DIR));
+                    if (configs.length > 0) {
+                        String fileName = configs[0].getName();
+                        if (configs[0].delete()) {
+                            mDeletedFiles.put(now, fileName);
+                        }
+                    }
+                }
+                forgetEverythingLocked();
+            }
         }
     }
 
-    private void forgetEverything() {
+    private void forgetEverythingLocked() {
+        sStatsd = null;
+        mContext.unregisterReceiver(mAppUpdateReceiver);
+        mContext.unregisterReceiver(mUserUpdateReceiver);
+        mContext.unregisterReceiver(mShutdownEventReceiver);
+        cancelAnomalyAlarm();
+        cancelPullingAlarm();
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+
         synchronized (sStatsdLock) {
-            sStatsd = null;
-            mContext.unregisterReceiver(mAppUpdateReceiver);
-            mContext.unregisterReceiver(mUserUpdateReceiver);
-            mContext.unregisterReceiver(mShutdownEventReceiver);
-            cancelAnomalyAlarm();
-            cancelPullingAlarms();
+            writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
+            if (mDeletedFiles.size() > 0) {
+                writer.println("  timestamp, deleted file name");
+            }
+            long lastBootMillis =
+                    SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
+            for (Long elapsedMillis : mDeletedFiles.keySet()) {
+                long deletionMillis = lastBootMillis + elapsedMillis;
+                writer.println("  " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 0678d08..8af1101 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -23,7 +23,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
-import android.hardware.fingerprint.IFingerprintDialogReceiver;
+import android.hardware.biometrics.IBiometricDialogReceiver;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -547,7 +547,7 @@
     }
 
     @Override
-    public void showFingerprintDialog(Bundle bundle, IFingerprintDialogReceiver receiver) {
+    public void showFingerprintDialog(Bundle bundle, IBiometricDialogReceiver receiver) {
         if (mBar != null) {
             try {
                 mBar.showFingerprintDialog(bundle, receiver);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 8901b04..d123099c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1428,7 +1428,7 @@
 
     void clearWallpaperLocked(boolean defaultFailed, int which, int userId, IRemoteCallback reply) {
         if (which != FLAG_SYSTEM && which != FLAG_LOCK) {
-            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to read");
+            throw new IllegalArgumentException("Must specify exactly one kind of wallpaper to clear");
         }
 
         WallpaperData wallpaper = null;
diff --git a/services/core/java/com/android/server/wm/AlertWindowNotification.java b/services/core/java/com/android/server/wm/AlertWindowNotification.java
index 9b787de..9177d25 100644
--- a/services/core/java/com/android/server/wm/AlertWindowNotification.java
+++ b/services/core/java/com/android/server/wm/AlertWindowNotification.java
@@ -37,6 +37,8 @@
 import android.graphics.drawable.Drawable;
 
 import android.net.Uri;
+import android.os.Bundle;
+
 import com.android.internal.R;
 import com.android.server.policy.IconUtilities;
 
@@ -109,6 +111,8 @@
 
         final String message = context.getString(R.string.alert_windows_notification_message,
                 appName);
+        Bundle extras = new Bundle();
+        extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[] {mPackageName});
         final Notification.Builder builder = new Notification.Builder(context, mNotificationTag)
                 .setOngoing(true)
                 .setContentTitle(
@@ -118,6 +122,7 @@
                 .setColor(context.getColor(R.color.system_notification_accent_color))
                 .setStyle(new Notification.BigTextStyle().bigText(message))
                 .setLocalOnly(true)
+                .addExtras(extras)
                 .setContentIntent(getContentIntent(context, mPackageName));
 
         if (aInfo != null) {
diff --git a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
index ae343da..416469b 100644
--- a/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
+++ b/services/core/java/com/android/server/wm/AnimatingAppWindowTokenRegistry.java
@@ -19,6 +19,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import java.io.PrintWriter;
 import java.util.ArrayList;
 
 /**
@@ -88,4 +89,13 @@
         }
         mTmpRunnableList.clear();
     }
+
+    void dump(PrintWriter pw, String header, String prefix) {
+        if (!mAnimatingTokens.isEmpty() || !mFinishedTokens.isEmpty()) {
+            pw.print(prefix); pw.println(header);
+            prefix = prefix + "  ";
+            pw.print(prefix); pw.print("mAnimatingTokens="); pw.println(mAnimatingTokens);
+            pw.print(prefix); pw.print("mFinishedTokens="); pw.println(mFinishedTokens);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 41ae48a..c6f156b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -34,6 +34,8 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
 import static android.view.WindowManager.TRANSIT_TASK_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
 import static android.view.WindowManager.TRANSIT_UNSET;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_CLOSE;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE;
@@ -114,6 +116,7 @@
 import android.view.animation.ScaleAnimation;
 import android.view.animation.TranslateAnimation;
 
+import com.android.internal.R;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.server.AttributeCache;
 import com.android.server.wm.WindowManagerService.H;
@@ -1652,14 +1655,26 @@
                         + " Callers=" + Debug.getCallers(3));
             }
         } else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS && enter) {
-
-            a = loadAnimationRes("android", enter
-                    ? com.android.internal.R.anim.task_open_enter_cross_profile_apps
-                    : com.android.internal.R.anim.task_open_exit);
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.task_open_enter_cross_profile_apps);
             Slog.v(TAG,
                     "applyAnimation NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS:"
                             + " anim=" + a + " transit=" + appTransitionToString(transit)
-                            + " isEntrance=" + enter + " Callers=" + Debug.getCallers(3));
+                            + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
+        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_OPEN && enter) {
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.activity_translucent_open_enter);
+            Slog.v(TAG,
+                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_OPEN:"
+                            + " anim=" + a + " transit=" + appTransitionToString(transit)
+                            + " isEntrance=true" + " Callers=" + Debug.getCallers(3));
+        } else if (transit == TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE && !enter) {
+            a = loadAnimationRes("android",
+                    com.android.internal.R.anim.activity_translucent_close_exit);
+            Slog.v(TAG,
+                    "applyAnimation TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE:"
+                            + " anim=" + a + " transit=" + appTransitionToString(transit)
+                            + " isEntrance=false" + " Callers=" + Debug.getCallers(3));
         } else {
             int animAttr = 0;
             switch (transit) {
@@ -2001,6 +2016,12 @@
             case TRANSIT_KEYGUARD_UNOCCLUDE: {
                 return "TRANSIT_KEYGUARD_UNOCCLUDE";
             }
+            case TRANSIT_TRANSLUCENT_ACTIVITY_OPEN: {
+                return "TRANSIT_TRANSLUCENT_ACTIVITY_OPEN";
+            }
+            case TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE: {
+                return "TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE";
+            }
             default: {
                 return "<UNKNOWN>";
             }
@@ -2173,16 +2194,20 @@
                 || transit == TRANSIT_KEYGUARD_UNOCCLUDE;
     }
 
-    private static boolean isTaskTransit(int transit) {
-        return transit == TRANSIT_TASK_OPEN
+    static boolean isTaskTransit(int transit) {
+        return isTaskOpenTransit(transit)
                 || transit == TRANSIT_TASK_CLOSE
-                || transit == TRANSIT_TASK_OPEN_BEHIND
                 || transit == TRANSIT_TASK_TO_BACK
-                || transit == TRANSIT_TASK_TO_FRONT
                 || transit == TRANSIT_TASK_IN_PLACE;
     }
 
-    private static boolean isActivityTransit(int transit) {
+    private static  boolean isTaskOpenTransit(int transit) {
+        return transit == TRANSIT_TASK_OPEN
+                || transit == TRANSIT_TASK_OPEN_BEHIND
+                || transit == TRANSIT_TASK_TO_FRONT;
+    }
+
+    static boolean isActivityTransit(int transit) {
         return transit == TRANSIT_ACTIVITY_OPEN
                 || transit == TRANSIT_ACTIVITY_CLOSE
                 || transit == TRANSIT_ACTIVITY_RELAUNCH;
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 85436da..f19c554 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1485,7 +1485,7 @@
         if (w == null || winHint != null && w != winHint) {
             return;
         }
-        final boolean surfaceReady = w.hasDrawnLw()  // Regular case
+        final boolean surfaceReady = w.isDrawnLw()  // Regular case
                 || w.mWinAnimator.mSurfaceDestroyDeferred  // The preserved surface is still ready.
                 || w.isDragResizeChanged();  // Waiting for relayoutWindow to call preserveSurface.
         final boolean needsLetterbox = w.isLetterboxedAppWindow() && fillsParent() && surfaceReady;
@@ -1660,6 +1660,10 @@
                 true /* topToBottom */);
     }
 
+    SurfaceControl getAppAnimationLayer() {
+        return getAppAnimationLayer(needsZBoost());
+    }
+
     @Override
     public SurfaceControl getAnimationLeashParent() {
         // All normal app transitions take place in an animation layer which is below the pinned
@@ -1855,7 +1859,7 @@
         leash.setLayer(layer);
 
         final DisplayContent dc = getDisplayContent();
-        dc.assignStackOrdering(t);
+        dc.assignStackOrdering();
         if (mAnimatingAppWindowTokenRegistry != null) {
             mAnimatingAppWindowTokenRegistry.notifyStarting(this);
         }
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index d000bb6..1f95868 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -110,6 +110,11 @@
         SurfaceAnimator mSurfaceAnimator;
 
         /**
+         * Determines whether the dim layer should animate before destroying.
+         */
+        boolean mAnimateExit = true;
+
+        /**
          * Used for Dims not associated with a WindowContainer. See {@link Dimmer#dimAbove} for
          * details on Dim lifecycle.
          */
@@ -260,6 +265,12 @@
         }
     }
 
+    void dontAnimateExit() {
+        if (mDimState != null) {
+            mDimState.mAnimateExit = false;
+        }
+    }
+
     /**
      * Call after invoking {@link WindowContainer#prepareSurfaces} on children as
      * described in {@link #resetDimStates}.
@@ -274,7 +285,11 @@
         }
 
         if (!mDimState.mDimming) {
-            startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+            if (!mDimState.mAnimateExit) {
+                t.destroy(mDimState.mDimLayer);
+            } else {
+                startDimExit(mLastRequestedDimContainer, mDimState.mSurfaceAnimator, t);
+            }
             mDimState = null;
             return false;
         } else {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a7eac55..c9ff9e3 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -909,6 +909,7 @@
         return mRotation;
     }
 
+    @VisibleForTesting
     void setRotation(int newRotation) {
         mRotation = newRotation;
     }
@@ -974,7 +975,11 @@
         final int oldRotation = mRotation;
         final int lastOrientation = mLastOrientation;
         final boolean oldAltOrientation = mAltOrientation;
-        int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation);
+        final int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+                isDefaultDisplay);
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="
+                + mDisplayId + " based on lastOrientation=" + lastOrientation
+                + " and oldRotation=" + oldRotation);
         boolean mayRotateSeamlessly = mService.mPolicy.shouldRotateSeamlessly(oldRotation,
                 rotation);
 
@@ -1010,7 +1015,8 @@
         final boolean altOrientation = !mService.mPolicy.rotationHasCompatibleMetricsLw(
                 lastOrientation, rotation);
 
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Selected orientation " + lastOrientation
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+                + " selected orientation " + lastOrientation
                 + ", got rotation " + rotation + " which has "
                 + (altOrientation ? "incompatible" : "compatible") + " metrics");
 
@@ -1019,7 +1025,8 @@
             return false;
         }
 
-        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Rotation changed to " + rotation
+        if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+                + " rotation changed to " + rotation
                 + (altOrientation ? " (alt)" : "") + " from " + oldRotation
                 + (oldAltOrientation ? " (alt)" : "") + ", lastOrientation=" + lastOrientation);
 
@@ -1647,8 +1654,8 @@
 
         if (mService.mDisplayFrozen) {
             if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                        "Display is frozen, return " + mLastWindowForcedOrientation);
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+                        + " is frozen, return " + mLastWindowForcedOrientation);
                 // If the display is frozen, some activities may be in the middle of restarting, and
                 // thus have removed their old window. If the window has the flag to hide the lock
                 // screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -1660,8 +1667,8 @@
                 // window. We don't want to check the show when locked window directly though as
                 // things aren't stable while the display is frozen, for example the window could be
                 // momentarily unavailable due to activity relaunch.
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display is frozen while keyguard locked, "
-                        + "return " + mLastOrientation);
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Display id=" + mDisplayId
+                        + " is frozen while keyguard locked, return " + mLastOrientation);
                 return mLastOrientation;
             }
         } else {
@@ -3180,6 +3187,7 @@
          * A control placed at the appropriate level for transitions to occur.
          */
         SurfaceControl mAppAnimationLayer = null;
+        SurfaceControl mBoostedAppAnimationLayer = null;
 
         /**
          * Given that the split-screen divider does not have an AppWindowToken, it
@@ -3502,19 +3510,22 @@
                 // In a car, you cannot physically rotate the screen, so it doesn't make sense to
                 // allow anything but the default orientation.
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                        "Forcing UNSPECIFIED orientation in car. Ignoring " + orientation);
+                        "Forcing UNSPECIFIED orientation in car for display id=" + mDisplayId
+                                + ". Ignoring " + orientation);
                 return SCREEN_ORIENTATION_UNSPECIFIED;
             }
 
             if (orientation != SCREEN_ORIENTATION_UNSET
                     && orientation != SCREEN_ORIENTATION_BEHIND) {
                 if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                        "App is requesting an orientation, return " + orientation);
+                        "App is requesting an orientation, return " + orientation
+                                + " for display id=" + mDisplayId);
                 return orientation;
             }
 
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                    "No app is requesting an orientation, return " + mLastOrientation);
+                    "No app is requesting an orientation, return " + mLastOrientation
+                            + " for display id=" + mDisplayId);
             // The next app has not been requested to be visible, so we keep the current orientation
             // to prevent freezing/unfreezing the display too early.
             return mLastOrientation;
@@ -3531,12 +3542,14 @@
         }
 
         void assignStackOrdering(SurfaceControl.Transaction t) {
+
             final int HOME_STACK_STATE = 0;
             final int NORMAL_STACK_STATE = 1;
             final int ALWAYS_ON_TOP_STATE = 2;
 
             int layer = 0;
             int layerForAnimationLayer = 0;
+            int layerForBoostedAnimationLayer = 0;
 
             for (int state = 0; state <= ALWAYS_ON_TOP_STATE; state++) {
                 for (int i = 0; i < mChildren.size(); i++) {
@@ -3558,16 +3571,22 @@
                         // highest animating stack and no higher.
                         layerForAnimationLayer = layer++;
                     }
+                    if (state != ALWAYS_ON_TOP_STATE) {
+                        layerForBoostedAnimationLayer = layer++;
+                    }
                 }
             }
             if (mAppAnimationLayer != null) {
                 t.setLayer(mAppAnimationLayer, layerForAnimationLayer);
             }
+            if (mBoostedAppAnimationLayer != null) {
+                t.setLayer(mBoostedAppAnimationLayer, layerForBoostedAnimationLayer);
+            }
         }
 
         @Override
-        SurfaceControl getAppAnimationLayer() {
-            return mAppAnimationLayer;
+        SurfaceControl getAppAnimationLayer(boolean boosted) {
+            return boosted ? mBoostedAppAnimationLayer : mAppAnimationLayer;
         }
 
         SurfaceControl getSplitScreenDividerAnchor() {
@@ -3581,16 +3600,22 @@
                 mAppAnimationLayer = makeChildSurface(null)
                         .setName("animationLayer")
                         .build();
+                mBoostedAppAnimationLayer = makeChildSurface(null)
+                        .setName("boostedAnimationLayer")
+                        .build();
                 mSplitScreenDividerAnchor = makeChildSurface(null)
                         .setName("splitScreenDividerAnchor")
                         .build();
                 getPendingTransaction()
                         .show(mAppAnimationLayer)
+                        .show(mBoostedAppAnimationLayer)
                         .show(mSplitScreenDividerAnchor);
                 scheduleAnimation();
             } else {
                 mAppAnimationLayer.destroy();
                 mAppAnimationLayer = null;
+                mBoostedAppAnimationLayer.destroy();
+                mBoostedAppAnimationLayer = null;
                 mSplitScreenDividerAnchor.destroy();
                 mSplitScreenDividerAnchor = null;
             }
@@ -3693,7 +3718,8 @@
                         return SCREEN_ORIENTATION_UNSET;
                     }
                 }
-                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req);
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, win + " forcing orientation to " + req
+                        + " for display id=" + mDisplayId);
                 return (mLastWindowForcedOrientation = req);
             }
 
@@ -3872,7 +3898,7 @@
         super.prepareSurfaces();
     }
 
-    void assignStackOrdering(SurfaceControl.Transaction t) {
-        mTaskStackContainers.assignStackOrdering(t);
+    void assignStackOrdering() {
+        mTaskStackContainers.assignStackOrdering(getPendingTransaction());
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 19d6691..7274aee 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -34,6 +34,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Binder;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -41,7 +42,6 @@
 import android.util.Slog;import android.util.proto.ProtoOutputStream;
 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;
@@ -51,6 +51,7 @@
 import com.google.android.collect.Sets;
 
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -61,15 +62,17 @@
  * window manager when the animation is completed. In addition, window manager may also notify the
  * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
  */
-public class RecentsAnimationController {
+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 long FAILSAFE_DELAY = 1000;
 
     private final WindowManagerService mService;
     private final IRecentsAnimationRunner mRunner;
     private final RecentsAnimationCallbacks mCallbacks;
     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
     private final int mDisplayId;
+    private final Runnable mFailsafeRunnable = this::cancelAnimation;
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mHomeAppToken;
@@ -223,6 +226,13 @@
             return;
         }
 
+        try {
+            mRunner.asBinder().linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            cancelAnimation();
+            return;
+        }
+
         // Adjust the wallpaper visibility for the showing home activity
         final AppWindowToken recentsComponentAppToken =
                 dc.getHomeStack().getTopChild().getTopFullscreenAppToken();
@@ -296,6 +306,7 @@
                 // We've already canceled the animation
                 return;
             }
+            mService.mH.removeCallbacks(mFailsafeRunnable);
             mCanceled = true;
             try {
                 mRunner.onAnimationCanceled();
@@ -308,20 +319,34 @@
         mCallbacks.onAnimationFinished(false /* moveHomeToTop */);
     }
 
-    void cleanupAnimation() {
+    void cleanupAnimation(boolean moveHomeToTop) {
         if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations="
                 + mPendingAnimations.size());
         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
             adapter.mTask.setCanAffectSystemUiFlags(true);
+            if (moveHomeToTop) {
+                adapter.mTask.dontAnimateDimExit();
+            }
             adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
         }
         mPendingAnimations.clear();
 
+        mRunner.asBinder().unlinkToDeath(this, 0);
+
         mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
         mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
     }
 
+    void scheduleFailsafe() {
+        mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
+    }
+
+    @Override
+    public void binderDied() {
+        cancelAnimation();
+    }
+
     void checkAnimationReady(WallpaperController wallpaperController) {
         if (mPendingStart) {
             final boolean wallpaperReady = !isHomeAppOverWallpaper()
@@ -400,9 +425,11 @@
             if (mainWindow == null) {
                 return null;
             }
+            final Rect insets = new Rect(mainWindow.mContentInsets);
+            InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
                     !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
-                    mainWindow.mContentInsets, mTask.getPrefixOrderIndex(), position, bounds,
+                    insets, mTask.getPrefixOrderIndex(), position, bounds,
                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
             return mTarget;
         }
diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java
index c590067..3be7b235 100644
--- a/services/core/java/com/android/server/wm/RemoteAnimationController.java
+++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java
@@ -16,15 +16,17 @@
 
 package com.android.server.wm;
 
+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.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.AnimationAdapterProto.REMOTE;
-import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Binder;
 import android.os.Handler;
+import android.os.IBinder.DeathRecipient;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Slog;
@@ -37,6 +39,7 @@
 
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
+import com.android.server.wm.utils.InsetUtils;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -45,7 +48,7 @@
 /**
  * Helper class to run app animations in a remote process.
  */
-class RemoteAnimationController {
+class RemoteAnimationController implements DeathRecipient {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
     private static final long TIMEOUT_MS = 2000;
 
@@ -54,12 +57,10 @@
     private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
     private final Rect mTmpRect = new Rect();
     private final Handler mHandler;
-    private FinishedCallback mFinishedCallback;
+    private final Runnable mTimeoutRunnable = this::cancelAnimation;
 
-    private final Runnable mTimeoutRunnable = () -> {
-        onAnimationFinished();
-        invokeAnimationCancelled();
-    };
+    private FinishedCallback mFinishedCallback;
+    private boolean mCanceled;
 
     RemoteAnimationController(WindowManagerService service,
             RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
@@ -88,7 +89,7 @@
      * Called when the transition is ready to be started, and all leashes have been set up.
      */
     void goodToGo() {
-        if (mPendingAnimations.isEmpty()) {
+        if (mPendingAnimations.isEmpty() || mCanceled) {
             onAnimationFinished();
             return;
         }
@@ -105,8 +106,8 @@
         }
         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             try {
-                mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
-                        mFinishedCallback);
+                mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
+                mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to start remote animation", e);
                 onAnimationFinished();
@@ -118,6 +119,17 @@
         }
     }
 
+    private void cancelAnimation() {
+        synchronized (mService.getWindowManagerLock()) {
+            if (mCanceled) {
+                return;
+            }
+            mCanceled = true;
+        }
+        onAnimationFinished();
+        invokeAnimationCancelled();
+    }
+
     private void writeStartDebugStatement() {
         Slog.i(TAG, "Starting remote animation");
         final StringWriter sw = new StringWriter();
@@ -132,11 +144,18 @@
     private RemoteAnimationTarget[] 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();
             if (target != null) {
                 targets.add(target);
             } else {
+
+                // We can't really start an animation but we still need to make sure to finish the
+                // pending animation that was started by SurfaceAnimator
+                if (wrapper.mCapturedFinishCallback != null) {
+                    wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
+                }
                 mPendingAnimations.remove(i);
             }
         }
@@ -145,6 +164,7 @@
 
     private void onAnimationFinished() {
         mHandler.removeCallbacks(mTimeoutRunnable);
+        mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
         synchronized (mService.mWindowMap) {
             releaseFinishedCallback();
             mService.openSurfaceTransaction();
@@ -184,6 +204,11 @@
         mService.sendSetRunningRemoteAnimation(pid, running);
     }
 
+    @Override
+    public void binderDied() {
+        cancelAnimation();
+    }
+
     private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
 
         RemoteAnimationController mOuter;
@@ -194,12 +219,17 @@
 
         @Override
         public void onAnimationFinished() throws RemoteException {
-            if (mOuter != null) {
-                mOuter.onAnimationFinished();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                if (mOuter != null) {
+                    mOuter.onAnimationFinished();
 
-                // In case the client holds on to the finish callback, make sure we don't leak
-                // RemoteAnimationController which in turn would leak the runner on the client.
-                mOuter = null;
+                    // In case the client holds on to the finish callback, make sure we don't leak
+                    // RemoteAnimationController which in turn would leak the runner on the client.
+                    mOuter = null;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
@@ -235,9 +265,11 @@
                     || mCapturedLeash == null) {
                 return null;
             }
+            final Rect insets = new Rect(mainWindow.mContentInsets);
+            InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
             mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
                     mCapturedLeash, !mAppWindowToken.fillsParent(),
-                    mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
+                    mainWindow.mWinAnimator.mLastClipRect, insets,
                     mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
                     task.getWindowConfiguration(), false /*isNotInRecents*/);
             return mTarget;
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index f51a6a9..e97b366 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -79,17 +79,25 @@
         }
 
         // Top
-        c.clipRect(new Rect(0, 0, dw, mThickness), Region.Op.REPLACE);
+        c.save();
+        c.clipRect(new Rect(0, 0, dw, mThickness));
         c.drawColor(Color.RED);
+        c.restore();
         // Left
-        c.clipRect(new Rect(0, 0, mThickness, dh), Region.Op.REPLACE);
+        c.save();
+        c.clipRect(new Rect(0, 0, mThickness, dh));
         c.drawColor(Color.RED);
+        c.restore();
         // Right
-        c.clipRect(new Rect(dw - mThickness, 0, dw, dh), Region.Op.REPLACE);
+        c.save();
+        c.clipRect(new Rect(dw - mThickness, 0, dw, dh));
         c.drawColor(Color.RED);
+        c.restore();
         // Bottom
-        c.clipRect(new Rect(0, dh - mThickness, dw, dh), Region.Op.REPLACE);
+        c.save();
+        c.clipRect(new Rect(0, dh - mThickness, dw, dh));
         c.drawColor(Color.RED);
+        c.restore();
 
         mSurface.unlockCanvasAndPost(c);
     }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index e4722f9..e8d3210 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -645,6 +645,10 @@
         return mCanAffectSystemUiFlags;
     }
 
+    void dontAnimateDimExit() {
+        mDimmer.dontAnimateExit();
+    }
+
     @Override
     public String toString() {
         return "{taskId=" + mTaskId + " appTokens=" + mChildren + " mdr=" + mDeferRemoval + "}";
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 970a8d7..5f9d679 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -27,7 +27,6 @@
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.Color;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.os.Environment;
@@ -45,6 +44,7 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.policy.WindowManagerPolicy.StartingSurface;
 import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
+import com.android.server.wm.utils.InsetUtils;
 
 import com.google.android.collect.Sets;
 
@@ -273,7 +273,7 @@
             return null;
         }
         return new TaskSnapshot(buffer, top.getConfiguration().orientation,
-                getInsetsFromTaskBounds(mainWindow, task),
+                getInsets(mainWindow),
                 isLowRamDevice /* reduced */, scaleFraction /* scale */,
                 true /* isRealSnapshot */);
     }
@@ -282,11 +282,11 @@
         return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
     }
 
-    private Rect getInsetsFromTaskBounds(WindowState state, Task task) {
+    private Rect getInsets(WindowState state) {
         // XXX(b/72757033): These are insets relative to the window frame, but we're really
         // interested in the insets relative to the task bounds.
-        Rect insets = minRect(state.mContentInsets, state.mStableInsets);
-        insets = maxRect(insets, state.mAppToken.getLetterboxInsets());
+        final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
+        InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
         return insets;
     }
 
@@ -297,13 +297,6 @@
                 Math.min(rect1.bottom, rect2.bottom));
     }
 
-    private Rect maxRect(Rect rect1, Rect rect2) {
-        return new Rect(Math.max(rect1.left, rect2.left),
-                Math.max(rect1.top, rect2.top),
-                Math.max(rect1.right, rect2.right),
-                Math.max(rect1.bottom, rect2.bottom));
-    }
-
     /**
      * Retrieves all closing tasks based on the list of closing apps during an app transition.
      */
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 900e2df..62754ad 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1387,6 +1387,7 @@
                 token.dump(pw, "    ", dumpAll);
             }
         }
+        mAnimatingAppWindowTokenRegistry.dump(pw, "AnimatingApps:", prefix);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 28fdaae..60e7c0d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1125,12 +1125,15 @@
     }
 
     /**
+     * @param boosted If true, returns an animation layer that happens above all {@link TaskStack}s
+     *                Otherwise, the layer will be positioned above all animating
+     *                {@link TaskStack}s.
      * @return The layer on which all app animations are happening.
      */
-    SurfaceControl getAppAnimationLayer() {
+    SurfaceControl getAppAnimationLayer(boolean boosted) {
         final WindowContainer parent = getParent();
         if (parent != null) {
-            return parent.getAppAnimationLayer();
+            return parent.getAppAnimationLayer(boosted);
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 6bceda5..6686b80 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -419,4 +419,10 @@
      * @see android.view.IWindowManager#lockNow
      */
     public abstract void lockNow();
+
+    /**
+     * Return the user that owns the given window, {@link android.os.UserHandle#USER_NULL} if
+     * the window token is not found.
+     */
+    public abstract int getWindowOwnerUserId(IBinder windowToken);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 449aa2c..f1cd46b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -25,12 +25,10 @@
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.content.Intent.EXTRA_USER_HANDLE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.Process.SYSTEM_UID;
 import static android.os.Process.myPid;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.os.UserHandle.USER_NULL;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.DOCKED_INVALID;
@@ -181,7 +179,6 @@
 import android.util.MergedConfiguration;
 import android.util.Pair;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.TimeUtils;
@@ -2423,8 +2420,8 @@
         final int oldRotation = defaultDisplayContent.getRotation();
         final boolean oldAltOrientation = defaultDisplayContent.getAltOrientation();
 
-        final int rotation = mPolicy.rotationForOrientationLw(lastOrientation,
-                oldRotation);
+        final int rotation = mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,
+                true /* defaultDisplay */);
         boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw(
                 lastOrientation, rotation);
         if (oldRotation == rotation && oldAltOrientation == altOrientation) {
@@ -2700,10 +2697,10 @@
         }
     }
 
-    public void cleanupRecentsAnimation() {
+    public void cleanupRecentsAnimation(boolean moveHomeToTop) {
         synchronized (mWindowMap) {
             if (mRecentsAnimationController != null) {
-                mRecentsAnimationController.cleanupAnimation();
+                mRecentsAnimationController.cleanupAnimation(moveHomeToTop);
                 mRecentsAnimationController = null;
                 mAppTransition.updateBooster();
             }
@@ -2793,6 +2790,11 @@
         mTaskSnapshotController.screenTurningOff(listener);
     }
 
+    @Override
+    public void triggerAnimationFailsafe() {
+        mH.sendEmptyMessage(H.ANIMATION_FAILSAFE);
+    }
+
     /**
      * Starts deferring layout passes. Useful when doing multiple changes but to optimize
      * performance, only one layout pass should be done. This can be called multiple times, and
@@ -4566,6 +4568,7 @@
         public static final int NOTIFY_KEYGUARD_TRUSTED_CHANGED = 57;
         public static final int SET_HAS_OVERLAY_UI = 58;
         public static final int SET_RUNNING_REMOTE_ANIMATION = 59;
+        public static final int ANIMATION_FAILSAFE = 60;
 
         /**
          * Used to denote that an integer field in a message will not be used.
@@ -4984,6 +4987,14 @@
                     mAmInternal.setRunningRemoteAnimation(msg.arg1, msg.arg2 == 1);
                 }
                 break;
+                case ANIMATION_FAILSAFE: {
+                    synchronized (mWindowMap) {
+                        if (mRecentsAnimationController != null) {
+                            mRecentsAnimationController.scheduleFailsafe();
+                        }
+                    }
+                }
+                break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG_WM, "handleMessage: exit");
@@ -7351,6 +7362,17 @@
         public void lockNow() {
             WindowManagerService.this.lockNow(null);
         }
+
+        @Override
+        public int getWindowOwnerUserId(IBinder token) {
+            synchronized (mWindowMap) {
+                WindowState window = mWindowMap.get(token);
+                if (window != null) {
+                    return UserHandle.getUserId(window.mOwnerUid);
+                }
+                return UserHandle.USER_NULL;
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 3256762..b85f4a4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -16,12 +16,13 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SNAPSHOT;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_SPLASH_SCREEN;
 import static android.app.ActivityManagerInternal.APP_TRANSITION_WINDOWS_DRAWN;
 
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+import static android.view.WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
@@ -42,6 +43,7 @@
 import static android.view.WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN;
 import static android.view.WindowManager.TRANSIT_WALLPAPER_OPEN;
 import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit;
+import static com.android.server.wm.AppTransition.isTaskTransit;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -61,16 +63,15 @@
 import android.view.Display;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
-import android.view.SurfaceControl;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.TransitionType;
 import android.view.animation.Animation;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
-import java.util.ArrayList;
 import java.util.function.Predicate;
 
 /**
@@ -288,6 +289,7 @@
         final boolean closingAppHasWallpaper = canBeWallpaperTarget(mService.mClosingApps)
                 && hasWallpaperTarget;
 
+        transit = maybeUpdateTransitToTranslucentAnim(transit);
         transit = maybeUpdateTransitToWallpaper(transit, openingAppHasWallpaper,
                 closingAppHasWallpaper);
 
@@ -676,8 +678,9 @@
                 transit = TRANSIT_WALLPAPER_CLOSE;
                 if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: "
                         + AppTransition.appTransitionToString(transit));
-            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() &&
-                    openingApps.contains(wallpaperTarget.mAppToken)) {
+            } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw()
+                    && openingApps.contains(wallpaperTarget.mAppToken)
+                    && transit != TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE) {
                 // We are transitioning from an activity without
                 // a wallpaper to now showing the wallpaper
                 transit = TRANSIT_WALLPAPER_OPEN;
@@ -688,6 +691,50 @@
         return transit;
     }
 
+    /**
+     * There are cases where we open/close a new task/activity, but in reality only a translucent
+     * activity on top of existing activities is opening/closing. For that one, we have a different
+     * animation because non of the task/activity animations actually work well with translucent
+     * apps.
+     *
+     * @param transit The current transition type.
+     * @return The current transition type or
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE}/
+     *         {@link WindowManager#TRANSIT_TRANSLUCENT_ACTIVITY_OPEN} if appropriate for the
+     *         situation.
+     */
+    @VisibleForTesting
+    int maybeUpdateTransitToTranslucentAnim(int transit) {
+        final boolean taskOrActivity = AppTransition.isTaskTransit(transit)
+                || AppTransition.isActivityTransit(transit);
+        boolean allOpeningVisible = true;
+        boolean allTranslucentOpeningApps = !mService.mOpeningApps.isEmpty();
+        for (int i = mService.mOpeningApps.size() - 1; i >= 0; i--) {
+            final AppWindowToken token = mService.mOpeningApps.valueAt(i);
+            if (!token.isVisible()) {
+                allOpeningVisible = false;
+                if (token.fillsParent()) {
+                    allTranslucentOpeningApps = false;
+                }
+            }
+        }
+        boolean allTranslucentClosingApps = !mService.mClosingApps.isEmpty();
+        for (int i = mService.mClosingApps.size() - 1; i >= 0; i--) {
+            if (mService.mClosingApps.valueAt(i).fillsParent()) {
+                allTranslucentClosingApps = false;
+                break;
+            }
+        }
+
+        if (taskOrActivity && allTranslucentClosingApps && allOpeningVisible) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE;
+        }
+        if (taskOrActivity && allTranslucentOpeningApps && mService.mClosingApps.isEmpty()) {
+            return TRANSIT_TRANSLUCENT_ACTIVITY_OPEN;
+        }
+        return transit;
+    }
+
     private boolean canBeWallpaperTarget(ArraySet<AppWindowToken> apps) {
         for (int i = apps.size() - 1; i >= 0; i--) {
             if (apps.valueAt(i).windowsCanBeWallpaperTarget()) {
diff --git a/services/core/java/com/android/server/wm/utils/InsetUtils.java b/services/core/java/com/android/server/wm/utils/InsetUtils.java
new file mode 100644
index 0000000..b4a998a
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/InsetUtils.java
@@ -0,0 +1,38 @@
+/*
+ * 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.wm.utils;
+
+import android.graphics.Rect;
+
+/**
+ * Utility methods to handle insets represented as rects.
+ */
+public class InsetUtils {
+
+    private InsetUtils() {
+    }
+
+    /**
+     * Adds {@code insetsToAdd} to {@code inOutInsets}.
+     */
+    public static void addInsets(Rect inOutInsets, Rect insetsToAdd) {
+        inOutInsets.left += insetsToAdd.left;
+        inOutInsets.top += insetsToAdd.top;
+        inOutInsets.right += insetsToAdd.right;
+        inOutInsets.bottom += insetsToAdd.bottom;
+    }
+}
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index ecf1a33..f7ca363 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -231,6 +231,13 @@
             return nullptr;
         }
         bandConfigHal = module.bands[0];
+
+        /* Prefer FM to workaround possible program list fetching limitation
+         * (if tuner scans only configured band for programs). */
+        auto fmIt = std::find_if(module.bands.begin(), module.bands.end(),
+            [](const BandConfig & band) { return utils::isFm(band.type); });
+        if (fmIt != module.bands.end()) bandConfigHal = *fmIt;
+
         if (bandConfigHal.spacings.size() > 1) {
             bandConfigHal.spacings = hidl_vec<uint32_t>({ *std::min_element(
                     bandConfigHal.spacings.begin(), bandConfigHal.spacings.end()) });
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index 847222a..61b48c2 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -380,6 +380,7 @@
     auto jProduct = make_javastr(env, prop10.product);
     auto jVersion = make_javastr(env, prop10.version);
     auto jSerial = make_javastr(env, prop10.serial);
+    constexpr bool isInitializationRequired = true;
     bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
     auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr;
 
@@ -394,9 +395,9 @@
     return make_javaref(env, env->NewObject(gjni.ModuleProperties.clazz,
             gjni.ModuleProperties.cstor, moduleId, jServiceName.get(), prop10.classId,
             jImplementor.get(), jProduct.get(), jVersion.get(), jSerial.get(), prop10.numTuners,
-            prop10.numAudioSources, prop10.supportsCapture, jBands.get(), isBgScanSupported,
-            jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(), nullptr,
-            jVendorInfo.get()));
+            prop10.numAudioSources, isInitializationRequired, prop10.supportsCapture, jBands.get(),
+            isBgScanSupported, jSupportedProgramTypes.get(), jSupportedIdentifierTypes.get(),
+            nullptr, jVendorInfo.get()));
 }
 
 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
@@ -712,7 +713,7 @@
     gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass);
     gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
             "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
-            "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
+            "Ljava/lang/String;IIZZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
             "[I[ILjava/util/Map;Ljava/util/Map;)V");
 
     auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
diff --git a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
index e9d4482..ccb4f59 100644
--- a/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
+++ b/services/core/jni/com_android_server_UsbAlsaJackDetector.cpp
@@ -19,6 +19,7 @@
 
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
+#include "android-base/strings.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
 
@@ -33,24 +34,36 @@
 
 #define DRIVER_NAME "/dev/usb_accessory"
 
-#define USB_IN_JACK_NAME "USB in Jack"
-#define USB_OUT_JACK_NAME "USB out Jack"
+#define USB_IN_JACK_SUFFIX "Input Jack"
+#define USB_OUT_JACK_SUFFIX "Output Jack"
 
 namespace android
 {
 
-static jboolean is_jack_connected(jint card, const char* control) {
+static struct mixer_ctl* find_mixer_with_suffix(struct mixer* card_mixer, const char* suffix) {
+    int id = 0;
+    struct mixer_ctl* ctl;
+    while ((ctl = mixer_get_ctl(card_mixer, id++)) != NULL) {
+        const char* name = mixer_ctl_get_name(ctl);
+        if (android::base::EndsWith(name, suffix)) {
+          return ctl;
+        }
+    }
+    return NULL;
+}
+
+static jboolean is_jack_connected(jint card, const char* suffix) {
   struct mixer* card_mixer = mixer_open(card);
   if (card_mixer == NULL) {
     return true;
   }
-  struct mixer_ctl* ctl = mixer_get_ctl_by_name(card_mixer, control);
+  struct mixer_ctl* ctl = find_mixer_with_suffix(card_mixer, suffix);
   if (!ctl) {
     return true;
   }
   mixer_ctl_update(ctl);
   int val = mixer_ctl_get_value(ctl, 0);
-  ALOGI("JACK %s - value %d\n", control, val);
+  ALOGI("%s - value %d\n", mixer_ctl_get_name(ctl), val);
   mixer_close(card_mixer);
 
   return val != 0;
@@ -66,33 +79,31 @@
     }
 
     jboolean has_jack = false;
-    if ((mixer_get_ctl_by_name(card_mixer, USB_IN_JACK_NAME) != NULL) ||
-            (mixer_get_ctl_by_name(card_mixer, USB_OUT_JACK_NAME) != NULL)) {
+    if ((find_mixer_with_suffix(card_mixer, USB_IN_JACK_SUFFIX) != NULL) ||
+            (find_mixer_with_suffix(card_mixer, USB_OUT_JACK_SUFFIX) != NULL)) {
         has_jack = true;
     }
     mixer_close(card_mixer);
     return has_jack;
 }
 
-
 static jboolean android_server_UsbAlsaJackDetector_inputJackConnected(JNIEnv* /* env */,
                                                                       jobject /* thiz */,
                                                                       jint card)
 {
-    return is_jack_connected(card, USB_IN_JACK_NAME);
+    return is_jack_connected(card, USB_IN_JACK_SUFFIX);
 }
 
-
 static jboolean android_server_UsbAlsaJackDetector_outputJackConnected(JNIEnv* /* env */,
                                                                        jobject /* thiz */,
                                                                        jint card)
 {
-    return is_jack_connected(card, USB_OUT_JACK_NAME);
+    return is_jack_connected(card, USB_OUT_JACK_SUFFIX);
 }
 
 static void android_server_UsbAlsaJackDetector_jackDetect(JNIEnv* env,
-                                                                                                        jobject thiz,
-                                                                                                        jint card) {
+                                                          jobject thiz,
+                                                          jint card) {
     jclass jdclass = env->GetObjectClass(thiz);
     jmethodID method_jackDetectCallback = env->GetMethodID(jdclass, "jackDetectCallback", "()Z");
     if (method_jackDetectCallback == NULL) {
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index dcee151..21fea1c 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1125,6 +1125,16 @@
 }
 
 static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
+    gnssHal_V1_1 = IGnss_V1_1::getService();
+    if (gnssHal_V1_1 == nullptr) {
+        ALOGD("gnssHal 1.1 was null, trying 1.0");
+        gnssHal = IGnss_V1_0::getService();
+    } else {
+        gnssHal = gnssHal_V1_1;
+    }
+}
+
+static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) {
     method_reportLocation = env->GetMethodID(clazz, "reportLocation",
             "(ZLandroid/location/Location;)V");
     method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
@@ -1175,15 +1185,6 @@
         LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus);
     }
 
-    // TODO(b/31632518)
-    gnssHal_V1_1 = IGnss_V1_1::getService();
-    if (gnssHal_V1_1 == nullptr) {
-        ALOGD("gnssHal 1.1 was null, trying 1.0");
-        gnssHal = IGnss_V1_0::getService();
-    } else {
-        gnssHal = gnssHal_V1_1;
-    }
-
     if (gnssHal != nullptr) {
       gnssHalDeathRecipient = new GnssDeathRecipient();
       hardware::Return<bool> linked = gnssHal->linkToDeath(
@@ -2068,6 +2069,8 @@
     {"native_is_gnss_configuration_supported", "()Z",
             reinterpret_cast<void *>(
                     android_location_gpsLocationProvider_is_gnss_configuration_supported)},
+    {"native_init_once", "()V", reinterpret_cast<void *>(
+            android_location_GnssLocationProvider_init_once)},
     {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)},
     {"native_cleanup", "()V", reinterpret_cast<void *>(
             android_location_GnssLocationProvider_cleanup)},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 4020a52..1c9782f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -78,23 +78,6 @@
         return false;
     }
 
-    @Override
-    public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist,
-            boolean parent) {
-        return false;
-    }
-
-    @Override
-    public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId,
-            boolean parent) {
-        return null;
-    }
-
-    @Override
-    public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) {
-        return false;
-    }
-
     public boolean isUsingUnifiedPassword(ComponentName who) {
         return true;
     }
@@ -122,12 +105,12 @@
     }
 
     @Override
-    public List<String> setMeteredDataDisabled(ComponentName admin, List<String> packageNames) {
+    public List<String> setMeteredDataDisabledPackages(ComponentName admin, List<String> packageNames) {
         return packageNames;
     }
 
     @Override
-    public List<String> getMeteredDataDisabled(ComponentName admin) {
+    public List<String> getMeteredDataDisabledPackages(ComponentName admin) {
         return new ArrayList<>();
     }
 
@@ -163,7 +146,7 @@
     }
 
     @Override
-    public boolean isMeteredDataDisabledForUser(ComponentName admin,
+    public boolean isMeteredDataDisabledPackageForUser(ComponentName admin,
             String packageName, int userId) {
         return false;
     }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 1e216a3..90e8a9c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -51,6 +51,7 @@
 import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
 import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
+import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
@@ -590,7 +591,8 @@
         List<String> mLockTaskPackages = new ArrayList<>();
 
         // Bitfield of feature flags to be enabled during LockTask mode.
-        int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
+        // We default on the power button menu, in order to be consistent with pre-P behaviour.
+        int mLockTaskFeatures = DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
 
         boolean mStatusBarDisabled = false;
 
@@ -818,7 +820,6 @@
         private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length";
         private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length";
         private static final String ATTR_VALUE = "value";
-        private static final String TAG_PASSWORD_BLACKLIST = "password-blacklist";
         private static final String TAG_PASSWORD_QUALITY = "password-quality";
         private static final String TAG_POLICIES = "policies";
         private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS =
@@ -959,9 +960,6 @@
         // Default title of confirm credentials screen
         String organizationName = null;
 
-        // The blacklist data is stored in a file whose name is stored in the XML
-        String passwordBlacklistFile = null;
-
         // The component name of the backup transport which has to be used if backups are mandatory
         // or null if backups are not mandatory.
         ComponentName mandatoryBackupTransport = null;
@@ -1051,11 +1049,6 @@
                     out.endTag(null, TAG_MIN_PASSWORD_NONLETTER);
                 }
             }
-            if (passwordBlacklistFile != null) {
-                out.startTag(null, TAG_PASSWORD_BLACKLIST);
-                out.attribute(null, ATTR_VALUE, passwordBlacklistFile);
-                out.endTag(null, TAG_PASSWORD_BLACKLIST);
-            }
             if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) {
                 out.startTag(null, TAG_MAX_TIME_TO_UNLOCK);
                 out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock));
@@ -1311,8 +1304,6 @@
                 } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) {
                     minimumPasswordMetrics.nonLetter = Integer.parseInt(
                             parser.getAttributeValue(null, ATTR_VALUE));
-                } else if (TAG_PASSWORD_BLACKLIST.equals(tag)) {
-                    passwordBlacklistFile = parser.getAttributeValue(null, ATTR_VALUE);
                 }else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) {
                     maximumTimeToUnlock = Long.parseLong(
                             parser.getAttributeValue(null, ATTR_VALUE));
@@ -1587,8 +1578,6 @@
                     pw.println(minimumPasswordMetrics.symbols);
             pw.print(prefix); pw.print("minimumPasswordNonLetter=");
                     pw.println(minimumPasswordMetrics.nonLetter);
-            pw.print(prefix); pw.print("passwordBlacklist=");
-                    pw.println(passwordBlacklistFile != null);
             pw.print(prefix); pw.print("maximumTimeToUnlock=");
                     pw.println(maximumTimeToUnlock);
             pw.print(prefix); pw.print("strongAuthUnlockTimeout=");
@@ -1855,10 +1844,6 @@
             return new LockPatternUtils(mContext);
         }
 
-        PasswordBlacklist newPasswordBlacklist(File file) {
-            return new PasswordBlacklist(file);
-        }
-
         boolean storageManagerIsFileBasedEncryptionEnabled() {
             return StorageManager.isFileEncryptedNativeOnly();
         }
@@ -2026,8 +2011,9 @@
             Settings.Global.putString(mContext.getContentResolver(), name, value);
         }
 
-        void settingsSystemPutString(String name, String value) {
-            Settings.System.putString(mContext.getContentResolver(), name, value);
+        void settingsSystemPutStringForUser(String name, String value, int userId) {
+          Settings.System.putStringForUser(
+              mContext.getContentResolver(), name, value, userId);
         }
 
         void securityLogSetLoggingEnabledProperty(boolean enabled) {
@@ -4410,136 +4396,6 @@
         }
     }
 
-    /* @return the password blacklist set by the admin or {@code null} if none. */
-    PasswordBlacklist getAdminPasswordBlacklistLocked(@NonNull ActiveAdmin admin) {
-        final int userId = UserHandle.getUserId(admin.getUid());
-        return admin.passwordBlacklistFile == null ? null : new PasswordBlacklist(
-                new File(getPolicyFileDirectory(userId), admin.passwordBlacklistFile));
-    }
-
-    private static final String PASSWORD_BLACKLIST_FILE_PREFIX = "password-blacklist-";
-    private static final String PASSWORD_BLACKLIST_FILE_SUFFIX = "";
-
-    @Override
-    public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist,
-            boolean parent) {
-        if (!mHasFeature) {
-            return false;
-        }
-        Preconditions.checkNotNull(who, "who is null");
-
-        synchronized (this) {
-            final ActiveAdmin admin = getActiveAdminForCallerLocked(
-                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
-            final int userId = mInjector.userHandleGetCallingUserId();
-            PasswordBlacklist adminBlacklist = getAdminPasswordBlacklistLocked(admin);
-
-            if (blacklist == null || blacklist.isEmpty()) {
-                // Remove the adminBlacklist
-                admin.passwordBlacklistFile = null;
-                saveSettingsLocked(userId);
-                if (adminBlacklist != null) {
-                    adminBlacklist.delete();
-                }
-                return true;
-            }
-
-            // Validate server side
-            Preconditions.checkNotNull(name, "name is null");
-            DevicePolicyManager.enforcePasswordBlacklistSize(blacklist);
-
-            // Blacklist is case insensitive so normalize to lower case
-            final int blacklistSize = blacklist.size();
-            for (int i = 0; i < blacklistSize; ++i) {
-                blacklist.set(i, blacklist.get(i).toLowerCase());
-            }
-
-            final boolean isNewBlacklist = adminBlacklist == null;
-            if (isNewBlacklist) {
-                // Create a new file for the blacklist. There could be multiple admins, each setting
-                // different blacklists, to restrict a user's credential, for example a managed
-                // profile can impose restrictions on its parent while the parent is already
-                // restricted by its own admin. A deterministic naming scheme would be fragile if
-                // new types of admin are introduced so we generate and save the file name instead.
-                // This isn't a temporary file but it reuses the name generation logic
-                final File file;
-                try {
-                    file = File.createTempFile(PASSWORD_BLACKLIST_FILE_PREFIX,
-                            PASSWORD_BLACKLIST_FILE_SUFFIX, getPolicyFileDirectory(userId));
-                } catch (IOException e) {
-                    Slog.e(LOG_TAG, "Failed to make a file for the blacklist", e);
-                    return false;
-                }
-                adminBlacklist = mInjector.newPasswordBlacklist(file);
-            }
-
-            if (adminBlacklist.savePasswordBlacklist(name, blacklist)) {
-                if (isNewBlacklist) {
-                    // The blacklist was saved so point the admin to the file
-                    admin.passwordBlacklistFile = adminBlacklist.getFile().getName();
-                    saveSettingsLocked(userId);
-                }
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId,
-            boolean parent) {
-        if (!mHasFeature) {
-            return null;
-        }
-        Preconditions.checkNotNull(who, "who is null");
-        enforceFullCrossUsersPermission(userId);
-        synchronized (this) {
-            final ActiveAdmin admin = getActiveAdminForCallerLocked(
-                    who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
-            final PasswordBlacklist blacklist = getAdminPasswordBlacklistLocked(admin);
-            if (blacklist == null) {
-                return null;
-            }
-            return blacklist.getName();
-        }
-    }
-
-    @Override
-    public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) {
-        if (!mHasFeature) {
-            return false;
-        }
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.TEST_BLACKLISTED_PASSWORD, null);
-        return isPasswordBlacklistedInternal(userId, password);
-    }
-
-    private boolean isPasswordBlacklistedInternal(@UserIdInt int userId, String password) {
-        Preconditions.checkNotNull(password, "Password is null");
-        enforceFullCrossUsersPermission(userId);
-
-        // Normalize to lower case for case insensitive blacklist match
-        final String lowerCasePassword = password.toLowerCase();
-
-        synchronized (this) {
-            final List<ActiveAdmin> admins =
-                    getActiveAdminsForLockscreenPoliciesLocked(userId, /* parent */ false);
-            final int N = admins.size();
-            for (int i = 0; i < N; i++) {
-                final PasswordBlacklist blacklist
-                        = getAdminPasswordBlacklistLocked(admins.get(i));
-                if (blacklist != null) {
-                    if (blacklist.isPasswordBlacklisted(lowerCasePassword)) {
-                        return true;
-                    }
-                }
-            }
-        }
-
-        return false;
-    }
-
     @Override
     public boolean isActivePasswordSufficient(int userHandle, boolean parent) {
         if (!mHasFeature) {
@@ -4935,11 +4791,6 @@
                     return false;
                 }
             }
-
-            if (isPasswordBlacklistedInternal(userHandle, password)) {
-                Slog.w(LOG_TAG, "resetPassword: the password is blacklisted");
-                return false;
-            }
         }
 
         DevicePolicyData policy = getUserData(userHandle);
@@ -5531,10 +5382,11 @@
                         .setAttestationChallenge(null)
                         .build();
 
-                final boolean generationResult = keyChain.generateKeyPair(algorithm,
+                final int generationResult = keyChain.generateKeyPair(algorithm,
                     new ParcelableKeyGenParameterSpec(noAttestationSpec));
-                if (!generationResult) {
-                    Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
+                if (generationResult != KeyChain.KEY_GEN_SUCCESS) {
+                    Log.e(LOG_TAG, String.format(
+                            "KeyChain failed to generate a keypair, error %d.", generationResult));
                     return false;
                 }
 
@@ -5547,12 +5399,17 @@
 
                 final byte[] attestationChallenge = keySpec.getAttestationChallenge();
                 if (attestationChallenge != null) {
-                    final boolean attestationResult = keyChain.attestKey(
+                    final int attestationResult = keyChain.attestKey(
                             alias, attestationChallenge, attestationUtilsFlags, attestationChain);
-                    if (!attestationResult) {
+                    if (attestationResult != KeyChain.KEY_ATTESTATION_SUCCESS) {
                         Log.e(LOG_TAG, String.format(
-                                "Attestation for %s failed, deleting key.", alias));
+                                "Attestation for %s failed (rc=%d), deleting key.",
+                                alias, attestationResult));
                         keyChain.removeKeyPair(alias);
+                        if (attestationResult == KeyChain.KEY_ATTESTATION_CANNOT_ATTEST_IDS) {
+                            throw new UnsupportedOperationException(
+                                    "Device does not support Device ID attestation.");
+                        }
                         return false;
                     }
                 }
@@ -8738,6 +8595,7 @@
 
     @Override
     public List getPermittedInputMethodsForCurrentUser() {
+        enforceManageUsers();
         UserInfo currentUser;
         try {
             currentUser = mInjector.getIActivityManager().getCurrentUser();
@@ -9909,6 +9767,9 @@
         boolean hasOverview = (flags & LOCK_TASK_FEATURE_OVERVIEW) != 0;
         Preconditions.checkArgument(hasHome || !hasOverview,
                 "Cannot use LOCK_TASK_FEATURE_OVERVIEW without LOCK_TASK_FEATURE_HOME");
+        boolean hasNotification = (flags & LOCK_TASK_FEATURE_NOTIFICATIONS) != 0;
+        Preconditions.checkArgument(hasHome || !hasNotification,
+            "Cannot use LOCK_TASK_FEATURE_NOTIFICATIONS without LOCK_TASK_FEATURE_HOME");
 
         final int userHandle = mInjector.userHandleGetCallingUserId();
         synchronized (this) {
@@ -10037,15 +9898,17 @@
         Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
 
         synchronized (this) {
-            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER);
+            getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
 
             if (!SYSTEM_SETTINGS_WHITELIST.contains(setting)) {
                 throw new SecurityException(String.format(
                         "Permission denial: device owners cannot update %1$s", setting));
             }
 
-            mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsSystemPutString(
-                    setting, value));
+            final int callingUserId = mInjector.userHandleGetCallingUserId();
+
+            mInjector.binderWithCleanCallingIdentity(() ->
+                mInjector.settingsSystemPutStringForUser(setting, value, callingUserId));
         }
     }
 
@@ -11448,7 +11311,7 @@
     }
 
     @Override
-    public List<String> setMeteredDataDisabled(ComponentName who, List<String> packageNames) {
+    public List<String> setMeteredDataDisabledPackages(ComponentName who, List<String> packageNames) {
         Preconditions.checkNotNull(who);
         Preconditions.checkNotNull(packageNames);
 
@@ -11498,7 +11361,7 @@
     }
 
     @Override
-    public List<String> getMeteredDataDisabled(ComponentName who) {
+    public List<String> getMeteredDataDisabledPackages(ComponentName who) {
         Preconditions.checkNotNull(who);
 
         if (!mHasFeature) {
@@ -11513,7 +11376,7 @@
     }
 
     @Override
-    public boolean isMeteredDataDisabledForUser(ComponentName who,
+    public boolean isMeteredDataDisabledPackageForUser(ComponentName who,
             String packageName, int userId) {
         Preconditions.checkNotNull(who);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java b/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java
deleted file mode 100644
index a17a107..0000000
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.devicepolicy;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.AtomicFile;
-import android.util.Slog;
-
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.List;
-
-/**
- * Manages the blacklisted passwords.
- *
- * This caller must ensure synchronized access.
- */
-public class PasswordBlacklist {
-    private static final String TAG = "PasswordBlacklist";
-
-    private final AtomicFile mFile;
-
-    /**
-     * Create an object to manage the password blacklist.
-     *
-     * This is a lightweight operation to prepare variables but not perform any IO.
-     */
-    public PasswordBlacklist(File file) {
-        mFile = new AtomicFile(file, "device-policy");
-    }
-
-    /**
-     * Atomically replace the blacklist.
-     *
-     * Pass {@code null} for an empty list.
-     */
-    public boolean savePasswordBlacklist(@NonNull String name, @NonNull List<String> blacklist) {
-        FileOutputStream fos = null;
-        try {
-            fos = mFile.startWrite();
-            final DataOutputStream out = buildStreamForWriting(fos);
-            final Header header = new Header(Header.VERSION_1, name, blacklist.size());
-            header.write(out);
-            final int blacklistSize = blacklist.size();
-            for (int i = 0; i < blacklistSize; ++i) {
-                out.writeUTF(blacklist.get(i));
-            }
-            out.flush();
-            mFile.finishWrite(fos);
-            return true;
-        } catch (IOException e) {
-            mFile.failWrite(fos);
-            return false;
-        }
-    }
-
-    /** @return the name of the blacklist or {@code null} if none set. */
-    public String getName() {
-        try (DataInputStream in = openForReading()) {
-            return Header.read(in).mName;
-        } catch (IOException e) {
-            Slog.wtf(TAG, "Failed to read blacklist file", e);
-        }
-        return null;
-    }
-
-    /** @return the number of blacklisted passwords. */
-    public int getSize() {
-        final int blacklistSize;
-        try (DataInputStream in = openForReading()) {
-            return Header.read(in).mSize;
-        } catch (IOException e) {
-            Slog.wtf(TAG, "Failed to read blacklist file", e);
-        }
-        return 0;
-    }
-
-    /** @return whether the password matches an blacklisted item. */
-    public boolean isPasswordBlacklisted(@NonNull String password) {
-        final int blacklistSize;
-        try (DataInputStream in = openForReading()) {
-            final Header header = Header.read(in);
-            for (int i = 0; i < header.mSize; ++i) {
-                if (in.readUTF().equals(password)) {
-                    return true;
-                }
-            }
-        } catch (IOException e) {
-            Slog.wtf(TAG, "Failed to read blacklist file", e);
-            // Fail safe and block all passwords. Setting a new blacklist should resolve this
-            // problem which can be identified by examining the log.
-            return true;
-        }
-        return false;
-    }
-
-    /** Delete the blacklist completely from disk. */
-    public void delete() {
-        mFile.delete();
-    }
-
-    /** Get the file the blacklist is stored in. */
-    public File getFile() {
-        return mFile.getBaseFile();
-    }
-
-    private DataOutputStream buildStreamForWriting(FileOutputStream fos) {
-        return new DataOutputStream(new BufferedOutputStream(fos));
-    }
-
-    private DataInputStream openForReading() throws IOException {
-        return new DataInputStream(new BufferedInputStream(mFile.openRead()));
-    }
-
-    /**
-     * Helper to read and write the header of the blacklist file.
-     */
-    private static class Header {
-        static final int VERSION_1 = 1;
-
-        final int mVersion; // File format version
-        final String mName;
-        final int mSize;
-
-        Header(int version, String name, int size) {
-            mVersion = version;
-            mName = name;
-            mSize = size;
-        }
-
-        void write(DataOutputStream out) throws IOException {
-            out.writeInt(mVersion);
-            out.writeUTF(mName);
-            out.writeInt(mSize);
-        }
-
-        static Header read(DataInputStream in) throws IOException {
-            final int version = in.readInt();
-            final String name = in.readUTF();
-            final int size = in.readInt();
-            return new Header(version, name, size);
-        }
-    }
-}
diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java
index d41fbce..ca8f727 100644
--- a/services/net/java/android/net/apf/ApfGenerator.java
+++ b/services/net/java/android/net/apf/ApfGenerator.java
@@ -367,7 +367,7 @@
      */
     public boolean setApfVersion(int version) {
         // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h
-        return version == 2;
+        return version >= 2;
     }
 
     private void addInstruction(Instruction instruction) {
diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java
new file mode 100644
index 0000000..9f26fa1
--- /dev/null
+++ b/services/net/java/android/net/util/InterfaceSet.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 android.net.util;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.StringJoiner;
+
+
+/**
+ * @hide
+ */
+public class InterfaceSet {
+    public final Set<String> ifnames;
+
+    public InterfaceSet(String... names) {
+        final Set<String> nameSet = new HashSet<>();
+        for (String name : names) {
+            if (name != null) nameSet.add(name);
+        }
+        ifnames = Collections.unmodifiableSet(nameSet);
+    }
+
+    @Override
+    public String toString() {
+        final StringJoiner sj = new StringJoiner(",", "[", "]");
+        for (String ifname : ifnames) sj.add(ifname);
+        return sj.toString();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return obj != null
+                && obj instanceof InterfaceSet
+                && ifnames.equals(((InterfaceSet)obj).ifnames);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
index f603a09..fa41220 100644
--- a/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
+++ b/services/robotests/src/com/android/server/backup/PerformBackupTaskTest.java
@@ -147,6 +147,15 @@
 
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
+
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
+        // We need to mock BMS timeout parameters before initializing the BackupHandler since
+        // the constructor of BackupHandler relies on the timeout parameters.
+        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
         mBackupHandler = new BackupHandler(mBackupManagerService, backupLooper);
 
         mBackupManager = spy(FakeIBackupManager.class);
@@ -157,7 +166,8 @@
                 mTransportManager,
                 packageManager,
                 mBackupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getBaseStateDir()).thenReturn(mBaseStateDir);
         when(mBackupManagerService.getDataDir()).thenReturn(dataDir);
         when(mBackupManagerService.getBackupManagerBinder()).thenReturn(mBackupManager);
diff --git a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
index 03792b1..92d6bbd 100644
--- a/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
+++ b/services/robotests/src/com/android/server/backup/restore/ActiveRestoreSessionTest.java
@@ -41,12 +41,14 @@
 import android.app.backup.RestoreSet;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
+import android.os.Handler;
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
 import com.android.server.EventLogTags;
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -115,6 +117,15 @@
 
         Looper backupLooper = startBackupThreadAndGetLooper();
         mShadowBackupLooper = shadowOf(backupLooper);
+
+        Handler mainHandler = new Handler(Looper.getMainLooper());
+        BackupAgentTimeoutParameters agentTimeoutParameters =
+                new BackupAgentTimeoutParameters(mainHandler, application.getContentResolver());
+        agentTimeoutParameters.start();
+
+        // We need to mock BMS timeout parameters before initializing the BackupHandler since
+        // the constructor of BackupHandler relies on it.
+        when(mBackupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
         BackupHandler backupHandler = new BackupHandler(mBackupManagerService, backupLooper);
 
         mWakeLock = createBackupWakeLock(application);
@@ -125,7 +136,8 @@
                 mTransportManager,
                 application.getPackageManager(),
                 backupHandler,
-                mWakeLock);
+                mWakeLock,
+                agentTimeoutParameters);
         when(mBackupManagerService.getPendingRestores()).thenReturn(new ArrayDeque<>());
     }
 
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index c210fde..5a886e3 100644
--- a/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -28,6 +28,7 @@
 import android.os.PowerManager;
 import android.util.SparseArray;
 
+import com.android.server.backup.BackupAgentTimeoutParameters;
 import com.android.server.backup.BackupManagerService;
 import com.android.server.backup.TransportManager;
 import com.android.server.backup.internal.BackupHandler;
@@ -43,7 +44,8 @@
             TransportManager transportManager,
             PackageManager packageManager,
             BackupHandler backupHandler,
-            PowerManager.WakeLock wakeLock) {
+            PowerManager.WakeLock wakeLock,
+            BackupAgentTimeoutParameters agentTimeoutParameters) {
         when(backupManagerService.getContext()).thenReturn(context);
         when(backupManagerService.getTransportManager()).thenReturn(transportManager);
         when(backupManagerService.getPackageManager()).thenReturn(packageManager);
@@ -53,6 +55,7 @@
         when(backupManagerService.getCurrentOperations()).thenReturn(new SparseArray<>());
         when(backupManagerService.getActivityManager()).thenReturn(mock(IActivityManager.class));
         when(backupManagerService.getWakelock()).thenReturn(wakeLock);
+        when(backupManagerService.getAgentTimeoutParameters()).thenReturn(agentTimeoutParameters);
     }
 
     public static PowerManager.WakeLock createBackupWakeLock(Application application) {
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 082827c..0ec16b5 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -14,7 +14,9 @@
      limitations under the License.
 -->
 <configuration description="Runs Frameworks Services Tests.">
-    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="FrameworksServicesTests.apk" />
         <option name="test-file-name" value="JobTestApp.apk" />
@@ -22,7 +24,6 @@
         <option name="test-file-name" value="SuspendTestApp.apk" />
     </target_preparer>
 
-    <option name="test-suite-tag" value="apct" />
     <option name="test-tag" value="FrameworksServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.servicestests" />
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 b452ea5..cda968a7 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -240,7 +240,7 @@
 
         doReturn(displayShouldSleep).when(display).shouldSleep();
         doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(keyguardShowing).when(keyguard).isKeyguardShowing(anyInt());
+        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
 
         mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
         mSupervisor.applySleepTokensLocked(true);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 6fdb029..3c10b03 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -389,6 +389,14 @@
             sentIntents.add(intent);
             return 0;
         }
+
+        @Override
+        void reportGlobalUsageEventLocked(int event) {
+        }
+
+        @Override
+        void reportCurWakefulnessUsageEvent() {
+        }
     }
 
     private static class TestHandler extends Handler {
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 7c3ea4f..e37ed79 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -17,6 +17,7 @@
 package com.android.server.content;
 
 import android.accounts.Account;
+import android.content.ContentResolver;
 import android.os.Bundle;
 import android.os.PersistableBundle;
 import android.test.AndroidTestCase;
@@ -60,7 +61,7 @@
                 "authority1",
                 b1,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         // Same as op1 but different time infos
         SyncOperation op2 = new SyncOperation(account1, 0,
@@ -69,7 +70,7 @@
                 "authority1",
                 b1,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         // Same as op1 but different authority
         SyncOperation op3 = new SyncOperation(account1, 0,
@@ -78,7 +79,7 @@
                 "authority2",
                 b1,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         // Same as op1 but different account
         SyncOperation op4 = new SyncOperation(account2, 0,
@@ -87,7 +88,7 @@
                 "authority1",
                 b1,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         // Same as op1 but different bundle
         SyncOperation op5 = new SyncOperation(account1, 0,
@@ -96,7 +97,7 @@
                 "authority1",
                 b2,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         assertEquals(op1.key, op2.key);
         assertNotSame(op1.key, op3.key);
@@ -117,7 +118,7 @@
                 "authority1",
                 b1,
                 false,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
 
         PersistableBundle pb = op1.toJobInfoExtras();
         SyncOperation op2 = SyncOperation.maybeCreateFromJobExtras(pb);
@@ -145,7 +146,7 @@
         Bundle extras = new Bundle();
         SyncOperation periodic = new SyncOperation(ep, 0, "package", 0, 0, extras, false, true,
                 SyncOperation.NO_JOB_ID, 60000, 10000,
-                /*isAppStandbyExempted=*/ false);
+                ContentResolver.SYNC_EXEMPTION_NONE);
         SyncOperation oneoff = periodic.createOneTimeSyncOperation();
         assertFalse("Conversion to oneoff sync failed.", oneoff.isPeriodic);
         assertEquals("Period not restored", periodic.periodMillis, oneoff.periodMillis);
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
deleted file mode 100644
index 7209c79..0000000
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * 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 com.android.server.content;
-
-import android.accounts.Account;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.ContextWrapper;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.test.AndroidTestCase;
-import android.test.RenamingDelegatingContext;
-import android.test.mock.MockContentResolver;
-import android.test.mock.MockContext;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
-
-import com.android.internal.os.AtomicFile;
-
-import java.io.File;
-import java.io.FileOutputStream;
-
-/**
- * Test for SyncStorageEngine.
- *
- * bit FrameworksServicesTests:com.android.server.content.SyncStorageEngineTest
- *
- * TODO Broken.  Fix it.  b/62485315
- */
-@Suppress
-public class SyncStorageEngineTest extends AndroidTestCase {
-
-    protected Account account1;
-    protected Account account2;
-    protected ComponentName syncService1;
-    protected String authority1 = "testprovider";
-    protected Bundle defaultBundle;
-    protected final int DEFAULT_USER = 0;
-
-    /* Some default poll frequencies. */
-    final long dayPoll = (60 * 60 * 24);
-    final long dayFuzz = 60;
-    final long thousandSecs = 1000;
-    final long thousandSecsFuzz = 100;
-
-    MockContentResolver mockResolver;
-    SyncStorageEngine engine;
-
-    private File getSyncDir() {
-        return new File(new File(getContext().getFilesDir(), "system"), "sync");
-    }
-
-    @Override
-    public void setUp() {
-        account1 = new Account("a@example.com", "example.type");
-        account2 = new Account("b@example.com", "example.type");
-        syncService1 = new ComponentName("com.example", "SyncService");
-        // Default bundle.
-        defaultBundle = new Bundle();
-        defaultBundle.putInt("int_key", 0);
-        defaultBundle.putString("string_key", "hello");
-        // Set up storage engine.
-        mockResolver = new MockContentResolver();
-        engine = SyncStorageEngine.newTestInstance(
-                new TestContext(mockResolver, getContext()));
-    }
-
-    /**
-     * Test that we handle the case of a history row being old enough to purge before the
-     * corresponding sync is finished. This can happen if the clock changes while we are syncing.
-     *
-     */
-    // TODO: this test causes AidlTest to fail. Omit for now
-    // @SmallTest
-    public void testPurgeActiveSync() throws Exception {
-        final Account account = new Account("a@example.com", "example.type");
-        final String authority = "testprovider";
-
-        MockContentResolver mockResolver = new MockContentResolver();
-
-        SyncStorageEngine engine = SyncStorageEngine.newTestInstance(
-                new TestContext(mockResolver, getContext()));
-        long time0 = 1000;
-        SyncOperation op = new SyncOperation(account, 0, 0, "foo",
-                SyncOperation.REASON_PERIODIC,
-                SyncStorageEngine.SOURCE_LOCAL,
-                authority,
-                Bundle.EMPTY, true,
-                /*isAppStandbyExempted=*/ false);
-        long historyId = engine.insertStartSyncEvent(op, time0);
-        long time1 = time0 + SyncStorageEngine.MILLIS_IN_4WEEKS * 2;
-        engine.stopSyncEvent(historyId, time1 - time0, "yay", 0, 0);
-    }
-
-    @LargeTest
-    public void testAuthorityPersistence() throws Exception {
-        final Account account1 = new Account("a@example.com", "example.type");
-        final Account account2 = new Account("b@example.com", "example.type.2");
-        final String authority1 = "testprovider1";
-        final String authority2 = "testprovider2";
-
-        engine.setMasterSyncAutomatically(false, 0);
-
-        engine.setIsSyncable(account1, 0, authority1, 1);
-        engine.setSyncAutomatically(account1, 0, authority1, true);
-
-        engine.setIsSyncable(account2, 0, authority1, 1);
-        engine.setSyncAutomatically(account2, 0, authority1, true);
-
-        engine.setIsSyncable(account1, 0, authority2, 1);
-        engine.setSyncAutomatically(account1, 0, authority2, false);
-
-        engine.setIsSyncable(account2, 0, authority2, 0);
-        engine.setSyncAutomatically(account2, 0, authority2, true);
-
-        engine.writeAllState();
-        engine.clearAndReadState();
-
-        assertEquals(true, engine.getSyncAutomatically(account1, 0, authority1));
-        assertEquals(true, engine.getSyncAutomatically(account2, 0, authority1));
-        assertEquals(false, engine.getSyncAutomatically(account1, 0, authority2));
-        assertEquals(true, engine.getSyncAutomatically(account2, 0, authority2));
-
-        assertEquals(1, engine.getIsSyncable(account1, 0, authority1));
-        assertEquals(1, engine.getIsSyncable(account2, 0, authority1));
-        assertEquals(1, engine.getIsSyncable(account1, 0, authority2));
-        assertEquals(0, engine.getIsSyncable(account2, 0, authority2));
-    }
-
-    @MediumTest
-    public void testListenForTicklesParsing() throws Exception {
-        byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
-                + "<accounts>\n"
-                + "<listenForTickles user=\"0\" enabled=\"false\" />"
-                + "<listenForTickles user=\"1\" enabled=\"true\" />"
-                + "<authority id=\"0\" user=\"0\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
-                + "<authority id=\"1\" user=\"1\" account=\"account1\" type=\"type1\" authority=\"auth1\" />\n"
-                + "</accounts>\n").getBytes();
-
-        MockContentResolver mockResolver = new MockContentResolver();
-        final TestContext testContext = new TestContext(mockResolver, getContext());
-
-        File syncDir = getSyncDir();
-        syncDir.mkdirs();
-        AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
-        FileOutputStream fos = accountInfoFile.startWrite();
-        fos.write(accountsFileData);
-        accountInfoFile.finishWrite(fos);
-
-        SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
-        assertEquals(false, engine.getMasterSyncAutomatically(0));
-        assertEquals(true, engine.getMasterSyncAutomatically(1));
-        assertEquals(true, engine.getMasterSyncAutomatically(2));
-
-    }
-
-    @MediumTest
-    public void testAuthorityRenaming() throws Exception {
-        final Account account1 = new Account("acc1", "type1");
-        final Account account2 = new Account("acc2", "type2");
-        final String authorityContacts = "contacts";
-        final String authorityCalendar = "calendar";
-        final String authorityOther = "other";
-        final String authorityContactsNew = "com.android.contacts";
-        final String authorityCalendarNew = "com.android.calendar";
-
-        MockContentResolver mockResolver = new MockContentResolver();
-
-        final TestContext testContext = new TestContext(mockResolver, getContext());
-
-        byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
-                + "<accounts>\n"
-                + "<authority id=\"0\" account=\"acc1\" type=\"type1\" authority=\"contacts\" />\n"
-                + "<authority id=\"1\" account=\"acc1\" type=\"type1\" authority=\"calendar\" />\n"
-                + "<authority id=\"2\" account=\"acc1\" type=\"type1\" authority=\"other\" />\n"
-                + "<authority id=\"3\" account=\"acc2\" type=\"type2\" authority=\"contacts\" />\n"
-                + "<authority id=\"4\" account=\"acc2\" type=\"type2\" authority=\"calendar\" />\n"
-                + "<authority id=\"5\" account=\"acc2\" type=\"type2\" authority=\"other\" />\n"
-                + "<authority id=\"6\" account=\"acc2\" type=\"type2\" enabled=\"false\""
-                + " authority=\"com.android.calendar\" />\n"
-                + "<authority id=\"7\" account=\"acc2\" type=\"type2\" enabled=\"false\""
-                + " authority=\"com.android.contacts\" />\n"
-                + "</accounts>\n").getBytes();
-
-        File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
-        syncDir.mkdirs();
-        AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
-        FileOutputStream fos = accountInfoFile.startWrite();
-        fos.write(accountsFileData);
-        accountInfoFile.finishWrite(fos);
-
-        SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
-        assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityContacts));
-        assertEquals(false, engine.getSyncAutomatically(account1, 0, authorityCalendar));
-        assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityOther));
-        assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityContactsNew));
-        assertEquals(true, engine.getSyncAutomatically(account1, 0, authorityCalendarNew));
-
-        assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContacts));
-        assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendar));
-        assertEquals(true, engine.getSyncAutomatically(account2, 0, authorityOther));
-        assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityContactsNew));
-        assertEquals(false, engine.getSyncAutomatically(account2, 0, authorityCalendarNew));
-    }
-
-    @SmallTest
-    public void testSyncableMigration() throws Exception {
-        final Account account = new Account("acc", "type");
-
-        MockContentResolver mockResolver = new MockContentResolver();
-
-        final TestContext testContext = new TestContext(mockResolver, getContext());
-
-        byte[] accountsFileData = ("<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
-                + "<accounts>\n"
-                + "<authority id=\"0\" account=\"acc\" authority=\"other1\" />\n"
-                + "<authority id=\"1\" account=\"acc\" type=\"type\" authority=\"other2\" />\n"
-                + "<authority id=\"2\" account=\"acc\" type=\"type\" syncable=\"false\""
-                + " authority=\"other3\" />\n"
-                + "<authority id=\"3\" account=\"acc\" type=\"type\" syncable=\"true\""
-                + " authority=\"other4\" />\n"
-                + "</accounts>\n").getBytes();
-
-        File syncDir = new File(new File(testContext.getFilesDir(), "system"), "sync");
-        syncDir.mkdirs();
-        AtomicFile accountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml"));
-        FileOutputStream fos = accountInfoFile.startWrite();
-        fos.write(accountsFileData);
-        accountInfoFile.finishWrite(fos);
-
-        SyncStorageEngine engine = SyncStorageEngine.newTestInstance(testContext);
-
-        assertEquals(-1, engine.getIsSyncable(account, 0, "other1"));
-        assertEquals(1, engine.getIsSyncable(account, 0, "other2"));
-        assertEquals(0, engine.getIsSyncable(account, 0, "other3"));
-        assertEquals(1, engine.getIsSyncable(account, 0, "other4"));
-    }
-
-    /**
-     * Verify that the API cannot cause a run-time reboot by passing in the empty string as an
-     * authority. The problem here is that
-     * {@link SyncStorageEngine#getOrCreateAuthorityLocked(account, provider)} would register
-     * an empty authority which causes a RTE in {@link SyncManager#scheduleReadyPeriodicSyncs()}.
-     * This is not strictly a SSE test, but it does depend on the SSE data structures.
-     */
-    @SmallTest
-    public void testExpectedIllegalArguments() throws Exception {
-        try {
-            ContentResolver.setSyncAutomatically(account1, "", true);
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.addPeriodicSync(account1, "", Bundle.EMPTY, 84000L);
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.removePeriodicSync(account1, "", Bundle.EMPTY);
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.cancelSync(account1, "");
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.setIsSyncable(account1, "", 0);
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.cancelSync(account1, "");
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.requestSync(account1, "", Bundle.EMPTY);
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        try {
-            ContentResolver.getSyncStatus(account1, "");
-            fail("empty provider string should throw IllegalArgumentException");
-        } catch (IllegalArgumentException expected) {}
-
-        // Make sure we aren't blocking null account/provider for those functions that use it
-        // to specify ALL accounts/providers.
-        ContentResolver.requestSync(null, null, Bundle.EMPTY);
-        ContentResolver.cancelSync(null, null);
-    }
-}
-
-class TestContext extends ContextWrapper {
-
-    ContentResolver mResolver;
-
-    private final Context mRealContext;
-
-    public TestContext(ContentResolver resolver, Context realContext) {
-        super(new RenamingDelegatingContext(new MockContext(), realContext, "test."));
-        mRealContext = realContext;
-        mResolver = resolver;
-    }
-
-    @Override
-    public Resources getResources() {
-        return mRealContext.getResources();
-    }
-
-    @Override
-    public File getFilesDir() {
-        return mRealContext.getFilesDir();
-    }
-
-    @Override
-    public void enforceCallingOrSelfPermission(String permission, String message) {
-    }
-
-    @Override
-    public void sendBroadcast(Intent intent) {
-    }
-
-    @Override
-    public ContentResolver getContentResolver() {
-        return mResolver;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index ab0bfefb..cd39285 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -98,12 +98,6 @@
         this.context = injector.context;
     }
 
-    @Override
-    public boolean isPasswordBlacklisted(int userId, String password) {
-        return false;
-    }
-
-
     public void notifyChangeToContentObserver(Uri uri, int userHandle) {
         ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle));
         if (co != null) {
@@ -220,11 +214,6 @@
         }
 
         @Override
-        PasswordBlacklist newPasswordBlacklist(File file) {
-            return services.passwordBlacklist;
-        }
-
-        @Override
         boolean storageManagerIsFileBasedEncryptionEnabled() {
             return services.storageManager.isFileBasedEncryptionEnabled();
         }
@@ -393,8 +382,8 @@
         }
 
         @Override
-        void settingsSystemPutString(String name, String value) {
-            services.settings.settingsSystemPutString(name, value);
+        void settingsSystemPutStringForUser(String name, String value, int userId) {
+            services.settings.settingsSystemPutStringForUser(name, value, userId);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index a64efb7..b76064b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2113,11 +2113,11 @@
         }
     }
 
-    public void testSetGetMeteredDataDisabled() throws Exception {
+    public void testSetGetMeteredDataDisabledPackages() throws Exception {
         setAsProfileOwner(admin1);
 
         final ArrayList<String> emptyList = new ArrayList<>();
-        assertEquals(emptyList, dpm.getMeteredDataDisabled(admin1));
+        assertEquals(emptyList, dpm.getMeteredDataDisabledPackages(admin1));
 
         // Setup
         final ArrayList<String> pkgsToRestrict = new ArrayList<>();
@@ -2127,40 +2127,40 @@
         pkgsToRestrict.add(package2);
         setupPackageInPackageManager(package1, DpmMockContext.CALLER_USER_HANDLE, 123, 0);
         setupPackageInPackageManager(package2, DpmMockContext.CALLER_USER_HANDLE, 456, 0);
-        List<String> excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+        List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
 
         // Verify
         assertEquals(emptyList, excludedPkgs);
-        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabled(admin1));
+        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabledPackages(admin1));
         verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
                 MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
 
         // Setup
         pkgsToRestrict.remove(package1);
-        excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+        excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
 
         // Verify
         assertEquals(emptyList, excludedPkgs);
-        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabled(admin1));
+        assertEquals(pkgsToRestrict, dpm.getMeteredDataDisabledPackages(admin1));
         verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
                 MockUtils.checkApps(pkgsToRestrict.toArray(new String[0])),
                 eq(DpmMockContext.CALLER_USER_HANDLE));
     }
 
-    public void testSetGetMeteredDataDisabled_deviceAdmin() {
+    public void testSetGetMeteredDataDisabledPackages_deviceAdmin() {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         dpm.setActiveAdmin(admin1, true);
         assertTrue(dpm.isAdminActive(admin1));
         mContext.callerPermissions.remove(permission.MANAGE_DEVICE_ADMINS);
 
         assertExpectException(SecurityException.class,  /* messageRegex= */ NOT_PROFILE_OWNER_MSG,
-                () -> dpm.setMeteredDataDisabled(admin1, new ArrayList<>()));
+                () -> dpm.setMeteredDataDisabledPackages(admin1, new ArrayList<>()));
         assertExpectException(SecurityException.class,  /* messageRegex= */ NOT_PROFILE_OWNER_MSG,
-                () -> dpm.getMeteredDataDisabled(admin1));
+                () -> dpm.getMeteredDataDisabledPackages(admin1));
     }
 
-    public void testGetMeteredDataDisabledForUser() throws Exception {
+    public void testIsMeteredDataDisabledForUserPackage() throws Exception {
         setAsProfileOwner(admin1);
 
         // Setup
@@ -2173,34 +2173,34 @@
         pkgsToRestrict.add(package2);
         setupPackageInPackageManager(package1, DpmMockContext.CALLER_USER_HANDLE, 123, 0);
         setupPackageInPackageManager(package2, DpmMockContext.CALLER_USER_HANDLE, 456, 0);
-        List<String> excludedPkgs = dpm.setMeteredDataDisabled(admin1, pkgsToRestrict);
+        List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
 
         // Verify
         assertEquals(emptyList, excludedPkgs);
         mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
         assertTrue(package1 + "should be restricted",
-                dpm.isMeteredDataDisabledForUser(admin1, package1,
+                dpm.isMeteredDataDisabledPackageForUser(admin1, package1,
                         DpmMockContext.CALLER_USER_HANDLE));
         assertTrue(package2 + "should be restricted",
-                dpm.isMeteredDataDisabledForUser(admin1, package2,
+                dpm.isMeteredDataDisabledPackageForUser(admin1, package2,
                         DpmMockContext.CALLER_USER_HANDLE));
         assertFalse(package3 + "should not be restricted",
-                dpm.isMeteredDataDisabledForUser(admin1, package3,
+                dpm.isMeteredDataDisabledPackageForUser(admin1, package3,
                         DpmMockContext.CALLER_USER_HANDLE));
     }
 
-    public void testGetMeteredDataDisabledForUser_nonSystemUidCaller() throws Exception {
+    public void testIsMeteredDataDisabledForUserPackage_nonSystemUidCaller() throws Exception {
         setAsProfileOwner(admin1);
         assertExpectException(SecurityException.class,
                 /* messageRegex= */ "Only the system can query restricted pkgs",
-                () -> dpm.isMeteredDataDisabledForUser(
+                () -> dpm.isMeteredDataDisabledPackageForUser(
                         admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE));
         dpm.clearProfileOwner(admin1);
 
         setDeviceOwner();
         assertExpectException(SecurityException.class,
                 /* messageRegex= */ "Only the system can query restricted pkgs",
-                () -> dpm.isMeteredDataDisabledForUser(
+                () -> dpm.isMeteredDataDisabledPackageForUser(
                         admin1, "com.example.one", DpmMockContext.CALLER_USER_HANDLE));
         clearDeviceOwner();
     }
@@ -3454,18 +3454,19 @@
                 dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS_FOR_VR, "0"));
     }
 
-    public void testSetSystemSettingFailWithPO() throws Exception {
-        setupProfileOwner();
-        assertExpectException(SecurityException.class, null, () ->
-                dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0"));
-    }
-
-    public void testSetSystemSetting() throws Exception {
+    public void testSetSystemSettingWithDO() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0");
-        verify(getServices().settings).settingsSystemPutString(
-                Settings.System.SCREEN_BRIGHTNESS, "0");
+        verify(getServices().settings).settingsSystemPutStringForUser(
+                Settings.System.SCREEN_BRIGHTNESS, "0", UserHandle.USER_SYSTEM);
+    }
+
+    public void testSetSystemSettingWithPO() throws Exception {
+        setupProfileOwner();
+        dpm.setSystemSetting(admin1, Settings.System.SCREEN_BRIGHTNESS, "0");
+        verify(getServices().settings).settingsSystemPutStringForUser(
+            Settings.System.SCREEN_BRIGHTNESS, "0", DpmMockContext.CALLER_USER_HANDLE);
     }
 
     public void testSetTime() throws Exception {
@@ -3719,7 +3720,8 @@
     }
 
     private void verifyLockTaskState(int userId) throws Exception {
-        verifyLockTaskState(userId, new String[0], DevicePolicyManager.LOCK_TASK_FEATURE_NONE);
+        verifyLockTaskState(userId, new String[0],
+                DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS);
     }
 
     private void verifyLockTaskState(int userId, String[] packages, int flags) throws Exception {
@@ -4190,36 +4192,6 @@
         assertTrue(dpm.clearResetPasswordToken(admin1));
     }
 
-    public void testSetPasswordBlacklistCannotBeCalledByNonAdmin() throws Exception {
-        assertExpectException(SecurityException.class, /* messageRegex= */ null,
-                () -> dpm.setPasswordBlacklist(admin1, null, null));
-        verifyZeroInteractions(getServices().passwordBlacklist);
-    }
-
-    public void testClearingPasswordBlacklistDoesNotCreateNewBlacklist() throws Exception {
-        setupProfileOwner();
-        dpm.setPasswordBlacklist(admin1, null, null);
-        verifyZeroInteractions(getServices().passwordBlacklist);
-    }
-
-    public void testSetPasswordBlacklistCreatesNewBlacklist() throws Exception {
-        final String name = "myblacklist";
-        final List<String> explicit = Arrays.asList("password", "letmein");
-        setupProfileOwner();
-        dpm.setPasswordBlacklist(admin1, name, explicit);
-        verify(getServices().passwordBlacklist).savePasswordBlacklist(name, explicit);
-    }
-
-    public void testSetPasswordBlacklistOnlyConvertsExplicitToLowerCase() throws Exception {
-        final List<String> mixedCase = Arrays.asList("password", "LETMEIN", "FooTBAll");
-        final List<String> lowerCase = Arrays.asList("password", "letmein", "football");
-        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
-        setupDeviceOwner();
-        final String name = "Name of the Blacklist";
-        dpm.setPasswordBlacklist(admin1, name, mixedCase);
-        verify(getServices().passwordBlacklist).savePasswordBlacklist(name, lowerCase);
-    }
-
     public void testIsActivePasswordSufficient() throws Exception {
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         mContext.packageName = admin1.getPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 34c69f5..e753df1 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -96,7 +96,6 @@
     public final IBackupManager ibackupManager;
     public final IAudioService iaudioService;
     public final LockPatternUtils lockPatternUtils;
-    public final PasswordBlacklist passwordBlacklist;
     public final StorageManagerForMock storageManager;
     public final WifiManager wifiManager;
     public final SettingsForMock settings;
@@ -135,7 +134,6 @@
         ibackupManager = mock(IBackupManager.class);
         iaudioService = mock(IAudioService.class);
         lockPatternUtils = mock(LockPatternUtils.class);
-        passwordBlacklist = mock(PasswordBlacklist.class);
         storageManager = mock(StorageManagerForMock.class);
         wifiManager = mock(WifiManager.class);
         settings = mock(SettingsForMock.class);
@@ -419,7 +417,7 @@
         public void settingsGlobalPutString(String name, String value) {
         }
 
-        public void settingsSystemPutString(String name, String value) {
+        public void settingsSystemPutStringForUser(String name, String value, int callingUserId) {
         }
 
         public int settingsGlobalGetInt(String name, int value) {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
deleted file mode 100644
index 1b3fc2c..0000000
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.server.devicepolicy;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Unit tests for {@link PasswordBlacklist}.
- *
- * bit FrameworksServicesTests:com.android.server.devicepolicy.PasswordBlacklistTest
- * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java
- */
-@RunWith(AndroidJUnit4.class)
-public final class PasswordBlacklistTest {
-    private File mBlacklistFile;
-    private PasswordBlacklist mBlacklist;
-
-    @Before
-    public void setUp() throws IOException {
-        mBlacklistFile = File.createTempFile("pwdbl", null);
-        mBlacklist = new PasswordBlacklist(mBlacklistFile);
-    }
-
-    @After
-    public void tearDown() {
-        mBlacklist.delete();
-    }
-
-    @Test
-    public void matchIsExact() {
-        // Note: Case sensitivity is handled by the user of PasswordBlacklist by normalizing the
-        // values stored in and tested against it.
-        mBlacklist.savePasswordBlacklist("matchIsExact", Arrays.asList("password", "qWERty"));
-        assertTrue(mBlacklist.isPasswordBlacklisted("password"));
-        assertTrue(mBlacklist.isPasswordBlacklisted("qWERty"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("Password"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("qwert"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("letmein"));
-    }
-
-    @Test
-    public void matchIsNotRegex() {
-        mBlacklist.savePasswordBlacklist("matchIsNotRegex", Arrays.asList("a+b*"));
-        assertTrue(mBlacklist.isPasswordBlacklisted("a+b*"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("aaaa"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("abbbb"));
-        assertFalse(mBlacklist.isPasswordBlacklisted("aaaa"));
-    }
-
-    @Test
-    public void matchFailsSafe() throws IOException {
-        try (FileOutputStream fos = new FileOutputStream(mBlacklistFile)) {
-            // Write a malformed blacklist file
-            fos.write(17);
-        }
-        assertTrue(mBlacklist.isPasswordBlacklisted("anything"));
-        assertTrue(mBlacklist.isPasswordBlacklisted("at"));
-        assertTrue(mBlacklist.isPasswordBlacklisted("ALL"));
-    }
-
-    @Test
-    public void blacklistCanBeNamed() {
-        final String name = "identifier";
-        mBlacklist.savePasswordBlacklist(name, Arrays.asList("one", "two", "three"));
-        assertEquals(mBlacklist.getName(), name);
-    }
-
-    @Test
-    public void reportsTheCorrectNumberOfEntries() {
-        mBlacklist.savePasswordBlacklist("Count Entries", Arrays.asList("1", "2", "3", "4"));
-        assertEquals(mBlacklist.getSize(), 4);
-    }
-
-    @Test
-    public void reportsBlacklistFile() {
-        assertEquals(mBlacklistFile, mBlacklist.getFile());
-    }
-}
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 d922df3..fb25cf3 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -85,9 +85,6 @@
         255
     };
 
-    private static final int[] MINIMUM_BRIGHTNESS_CURVE_LUX = { 2000, 4000 };
-    private static final float[] MINIMUM_BRIGHTNESS_CURVE_NITS = { 1.0f, 50.0f, 90.0f };
-
     private static final float[] DISPLAY_RANGE_NITS = { 2.685f, 478.5f };
     private static final int[] BACKLIGHT_RANGE = { 1, 255 };
 
@@ -384,19 +381,6 @@
                 com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
                 .thenReturn(mockBrightnessLevelNits);
 
-        int[] mockMinimumBrightnessCurveLux = new int[MINIMUM_BRIGHTNESS_CURVE_LUX.length];
-        for (int i = 0; i < mockMinimumBrightnessCurveLux.length; i++) {
-            mockMinimumBrightnessCurveLux[i] = (int) MINIMUM_BRIGHTNESS_CURVE_LUX[i];
-        }
-        when(mockResources.getIntArray(
-                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveLux))
-                .thenReturn(mockMinimumBrightnessCurveLux);
-        TypedArray mockMinimumBrightnessCurveNits = createFloatTypedArray(
-                MINIMUM_BRIGHTNESS_CURVE_NITS);
-        when(mockResources.obtainTypedArray(
-                com.android.internal.R.array.config_autoBrightnessMinimumBrightnessCurveNits))
-                .thenReturn(mockMinimumBrightnessCurveNits);
-
         TypedArray mockNitsRange = createFloatTypedArray(nitsRange);
         when(mockResources.obtainTypedArray(
                 com.android.internal.R.array.config_screenBrightnessNits))
@@ -435,78 +419,4 @@
         return mockArray;
     }
 
-    private float[] getNearMinimumNits(float epsilon) {
-        float[] lux = new float[MINIMUM_BRIGHTNESS_CURVE_LUX.length + 1];
-        for (int i = 0; i < MINIMUM_BRIGHTNESS_CURVE_LUX.length; i++) {
-            lux[i+1] = MINIMUM_BRIGHTNESS_CURVE_LUX[i];
-        }
-        Spline minimumBrightnessCurve = Spline.createSpline(lux, MINIMUM_BRIGHTNESS_CURVE_NITS);
-        float[] nits = new float[LUX_LEVELS.length];
-        for (int i = 0; i < nits.length; i++) {
-            nits[i] = minimumBrightnessCurve.interpolate(LUX_LEVELS[i]) + epsilon;
-        }
-        return nits;
-    }
-
-    @Test
-    public void testCreateWithTooDarkBrightnessConfigurationThrowsException() {
-        float[] nits = getNearMinimumNits(-0.1f);
-        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        Exception thrown = null;
-        try {
-            BrightnessMappingStrategy.create(res);
-        } catch (IllegalArgumentException e) {
-            thrown = e;
-        }
-        assertNotNull("Failed to throw IllegalArgumentException", thrown);
-    }
-
-    @Test
-    public void testCreationWithBrightEnoughBrightnessConfigurationDoesNotThrowException() {
-        float[] nits = getNearMinimumNits(0);
-        Resources res = createResources(LUX_LEVELS, nits, DISPLAY_RANGE_NITS, BACKLIGHT_RANGE);
-        assertNotNull("Failed to create BrightnessMappingStrategy",
-                BrightnessMappingStrategy.create(res));
-    }
-
-    @Test
-    public void testSettingTooDarkBrightnessConfigurationThrowsException() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
-                BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
-        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
-        float[] lux = new float[LUX_LEVELS.length];
-        for (int i = 0; i < lux.length; i++) {
-            lux[i] = LUX_LEVELS[i];
-        }
-        float[] nits = getNearMinimumNits(-0.1f);
-        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
-                .setCurve(lux, nits)
-                .build();
-        Exception thrown = null;
-        try {
-            strategy.setBrightnessConfiguration(config);
-        } catch (IllegalArgumentException e) {
-            thrown = e;
-        }
-        assertNotNull("Failed to throw IllegalArgumentException", thrown);
-    }
-
-    @Test
-    public void testSettingBrightEnouhgBrightnessConfigurationDoesNotThrowException() {
-        Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS, DISPLAY_RANGE_NITS,
-                BACKLIGHT_RANGE);
-        BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res);
-        assertNotNull("Failed to create BrightnessMappingStrategy", strategy);
-        float[] lux = new float[LUX_LEVELS.length];
-        for (int i = 0; i < lux.length; i++) {
-            lux[i] = LUX_LEVELS[i];
-        }
-        float[] nits = getNearMinimumNits(0);
-        BrightnessConfiguration config = new BrightnessConfiguration.Builder()
-                .setCurve(lux, nits)
-                .build();
-        assertTrue("failed to set brightness configuration",
-            strategy.setBrightnessConfiguration(config));
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
index 0ea2317..b8d2c3e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java
@@ -33,23 +33,28 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.os.FileUtils;
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.RecoveryController;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import android.util.Log;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDb;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnapshotStorage;
 
@@ -59,6 +64,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
@@ -72,8 +78,12 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeySyncTaskTest {
+
+    private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
+    private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
     private static final String WRAPPING_KEY_ALIAS = "KeySyncTaskTest/WrappingKey";
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
     private static final int TEST_USER_ID = 1000;
@@ -90,6 +100,7 @@
 
     @Mock private PlatformKeyManager mPlatformKeyManager;
     @Mock private RecoverySnapshotListenersStorage mSnapshotListenersStorage;
+    @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
 
     private RecoverySnapshotStorage mRecoverySnapshotStorage;
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
@@ -111,7 +122,12 @@
                 new int[] {TYPE_LOCKSCREEN});
         mRecoverableKeyStoreDb.setRecoverySecretTypes(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
                 new int[] {TYPE_LOCKSCREEN});
-        mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+                TEST_ROOT_CERT_ALIAS);
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID2,
+                TEST_ROOT_CERT_ALIAS);
+        mRecoverySnapshotStorage = new RecoverySnapshotStorage(context.getFilesDir());
 
         mKeySyncTask = new KeySyncTask(
                 mRecoverableKeyStoreDb,
@@ -121,7 +137,8 @@
                 TEST_CREDENTIAL_TYPE,
                 TEST_CREDENTIAL,
                 /*credentialUpdated=*/ false,
-                mPlatformKeyManager);
+                mPlatformKeyManager,
+                mTestOnlyInsecureCertificateHelper);
 
         mWrappingKey = generateAndroidKeyStoreKey();
         mEncryptKey = new PlatformEncryptionKey(TEST_GENERATION_ID, mWrappingKey);
@@ -133,6 +150,10 @@
     public void tearDown() {
         mRecoverableKeyStoreDb.close();
         mDatabaseFile.delete();
+
+        File file = new File(InstrumentationRegistry.getTargetContext().getFilesDir(),
+                SNAPSHOT_TOP_LEVEL_DIRECTORY);
+        FileUtils.deleteContentsAndDir(file);
     }
 
     @Test
@@ -247,7 +268,7 @@
                 TEST_APP_KEY_ALIAS,
                 WrappedKey.fromSecretKey(mEncryptKey, applicationKey));
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
 
         mKeySyncTask.run();
@@ -263,7 +284,7 @@
         mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
 
         mKeySyncTask.run();
 
@@ -271,9 +292,103 @@
     }
 
     @Test
+    public void run_InTestModeWithWhitelistedCredentials() throws Exception {
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+        // Enter test mode with whitelisted credentials
+        when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(any())).thenReturn(true);
+        when(mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(anyInt(), any()))
+                .thenReturn(true);
+        mKeySyncTask.run();
+
+        verify(mTestOnlyInsecureCertificateHelper)
+                .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+
+        // run whitelist checks
+        verify(mTestOnlyInsecureCertificateHelper)
+                .doesCredentailSupportInsecureMode(anyInt(), any());
+        verify(mTestOnlyInsecureCertificateHelper)
+                .keepOnlyWhitelistedInsecureKeys(any());
+
+        KeyChainSnapshot keyChainSnapshot = mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID);
+        assertNotNull(keyChainSnapshot); // created snapshot
+        List<WrappedApplicationKey> applicationKeys = keyChainSnapshot.getWrappedApplicationKeys();
+        assertThat(applicationKeys).hasSize(0); // non whitelisted key is not included
+    }
+
+    @Test
+    public void run_InTestModeWithNonWhitelistedCredentials() throws Exception {
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+        // Enter test mode with non whitelisted credentials
+        when(mTestOnlyInsecureCertificateHelper.isTestOnlyCertificate(any())).thenReturn(true);
+        when(mTestOnlyInsecureCertificateHelper.doesCredentailSupportInsecureMode(anyInt(), any()))
+                .thenReturn(false);
+        mKeySyncTask.run();
+
+        assertNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID)); // not created
+        verify(mTestOnlyInsecureCertificateHelper)
+                .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+        verify(mTestOnlyInsecureCertificateHelper)
+                .doesCredentailSupportInsecureMode(anyInt(), any());
+    }
+
+    @Test
+    public void run_doesNotFilterCredentialsAndAliasesInProd() throws Exception {
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+
+        mKeySyncTask.run();
+        assertNotNull(mRecoverySnapshotStorage.get(TEST_RECOVERY_AGENT_UID));
+
+        verify(mTestOnlyInsecureCertificateHelper)
+                .getDefaultCertificateAliasIfEmpty(eq(TEST_ROOT_CERT_ALIAS));
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .isTestOnlyCertificate(eq(TEST_ROOT_CERT_ALIAS));
+
+        // no whitelists check
+        verify(mTestOnlyInsecureCertificateHelper, never())
+                .doesCredentailSupportInsecureMode(anyInt(), any());
+        verify(mTestOnlyInsecureCertificateHelper, never())
+                .keepOnlyWhitelistedInsecureKeys(any());
+    }
+
+    @Test
+    public void run_replacesNullActiveRootAliasWithDefaultValue() throws Exception {
+        mRecoverableKeyStoreDb.setServerParams(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
+        mRecoverableKeyStoreDb.setPlatformKeyGenerationId(TEST_USER_ID, TEST_GENERATION_ID);
+        addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(TEST_USER_ID, TEST_RECOVERY_AGENT_UID,
+                /*alias=*/ null);
+
+        when(mTestOnlyInsecureCertificateHelper.getDefaultCertificateAliasIfEmpty(null))
+                .thenReturn(TEST_ROOT_CERT_ALIAS); // override default.
+        mKeySyncTask.run();
+
+        verify(mTestOnlyInsecureCertificateHelper).getDefaultCertificateAliasIfEmpty(null);
+    }
+
+    @Test
     public void run_sendsEncryptedKeysIfAvailableToSync_withRawPublicKey() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
 
         mRecoverableKeyStoreDb.setServerParams(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
@@ -320,7 +435,7 @@
     @Test
     public void run_sendsEncryptedKeysIfAvailableToSync_withCertPath() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setServerParams(
                 TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_VAULT_HANDLE);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
@@ -339,7 +454,7 @@
     @Test
     public void run_setsCorrectSnapshotVersion() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
@@ -358,7 +473,7 @@
     @Test
     public void run_recreatesMissingSnapshot() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
@@ -385,10 +500,11 @@
                 CREDENTIAL_TYPE_PASSWORD,
                 "password",
                 /*credentialUpdated=*/ false,
-                mPlatformKeyManager);
+                mPlatformKeyManager,
+                mTestOnlyInsecureCertificateHelper);
 
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -411,10 +527,11 @@
                 CREDENTIAL_TYPE_PASSWORD,
                 /*credential=*/ "1234",
                 /*credentialUpdated=*/ false,
-                mPlatformKeyManager);
+                mPlatformKeyManager,
+                mTestOnlyInsecureCertificateHelper);
 
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -438,10 +555,11 @@
                 CREDENTIAL_TYPE_PATTERN,
                 "12345",
                 /*credentialUpdated=*/ false,
-                mPlatformKeyManager);
+                mPlatformKeyManager,
+                mTestOnlyInsecureCertificateHelper);
 
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         SecretKey applicationKey =
                 addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -457,9 +575,9 @@
     @Test
     public void run_sendsEncryptedKeysWithTwoRegisteredAgents() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -479,9 +597,9 @@
                 new int[] {1000});
 
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(true);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -496,9 +614,9 @@
     @Test
     public void run_notifiesNonregisteredAgent() throws Exception {
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         mRecoverableKeyStoreDb.setRecoveryServiceCertPath(
-                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TestData.CERT_PATH_1);
+                TEST_USER_ID, TEST_RECOVERY_AGENT_UID2, TEST_ROOT_CERT_ALIAS, TestData.CERT_PATH_1);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID)).thenReturn(true);
         when(mSnapshotListenersStorage.hasListener(TEST_RECOVERY_AGENT_UID2)).thenReturn(false);
         addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
@@ -519,7 +637,8 @@
           /*credentialType=*/ 3,
           "12345",
           /*credentialUpdated=*/ false,
-          mPlatformKeyManager);
+          mPlatformKeyManager,
+          mTestOnlyInsecureCertificateHelper);
 
       addApplicationKey(TEST_USER_ID, TEST_RECOVERY_AGENT_UID, TEST_APP_KEY_ALIAS);
 
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
index 06b94cb..f4ec867 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java
@@ -27,6 +27,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -44,9 +45,10 @@
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
-import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyDerivationParams;
 import android.security.keystore.recovery.RecoveryCertPath;
+import android.security.keystore.recovery.TrustedRootCertificates;
 import android.security.keystore.recovery.WrappedApplicationKey;
 import android.support.test.filters.SmallTest;
 import android.support.test.InstrumentationRegistry;
@@ -66,6 +68,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
@@ -90,6 +93,10 @@
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
 
     private static final String ROOT_CERTIFICATE_ALIAS = "";
+    private static final String DEFAULT_ROOT_CERT_ALIAS =
+            TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS;
+    private static final String INSECURE_CERTIFICATE_ALIAS =
+            TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS;
     private static final String TEST_SESSION_ID = "karlin";
     private static final byte[] TEST_PUBLIC_KEY = new byte[] {
         (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x07, (byte) 0x2a,
@@ -143,7 +150,7 @@
     private static final String KEY_ALGORITHM = "AES";
     private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
     private static final String WRAPPING_KEY_ALIAS = "RecoverableKeyStoreManagerTest/WrappingKey";
-    private static final String TEST_ROOT_CERT_ALIAS = "";
+    private static final String TEST_DEFAULT_ROOT_CERT_ALIAS = "";
     private static final KeyChainProtectionParams TEST_PROTECTION_PARAMS =
     new KeyChainProtectionParams.Builder()
             .setUserSecretType(TYPE_LOCKSCREEN)
@@ -157,6 +164,7 @@
     @Mock private KeyguardManager mKeyguardManager;
     @Mock private PlatformKeyManager mPlatformKeyManager;
     @Mock private ApplicationKeyStorage mApplicationKeyStorage;
+    @Spy private TestOnlyInsecureCertificateHelper mTestOnlyInsecureCertificateHelper;
 
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
@@ -186,14 +194,14 @@
 
         mRecoverableKeyStoreManager = new RecoverableKeyStoreManager(
                 mMockContext,
-                KeyStore.getInstance(),
                 mRecoverableKeyStoreDb,
                 mRecoverySessionStorage,
                 Executors.newSingleThreadExecutor(),
                 mRecoverySnapshotStorage,
                 mMockListenersStorage,
                 mPlatformKeyManager,
-                mApplicationKeyStorage);
+                mApplicationKeyStorage,
+                mTestOnlyInsecureCertificateHelper);
     }
 
     @After
@@ -203,24 +211,6 @@
     }
 
     @Test
-    public void generateAndStoreKey_storesTheKey() throws Exception {
-        int uid = Binder.getCallingUid();
-        int userId = UserHandle.getCallingUserId();
-
-        mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
-
-        assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull();
-
-        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
-    }
-
-    @Test
-    public void generateAndStoreKey_returnsAKeyOfAppropriateSize() throws Exception {
-        assertThat(mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS))
-                .hasLength(RECOVERABLE_KEY_SIZE_BYTES);
-    }
-
-    @Test
     public void importKey_storesTheKey() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
@@ -257,7 +247,7 @@
     @Test
     public void removeKey_removesAKey() throws Exception {
         int uid = Binder.getCallingUid();
-        mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
+        mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
 
         mRecoverableKeyStoreManager.removeKey(TEST_ALIAS);
 
@@ -268,7 +258,7 @@
     public void removeKey_updatesShouldCreateSnapshot() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
+        mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
         // Pretend that key was synced
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
 
@@ -298,15 +288,79 @@
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
 
-        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
-                TestData.CERT_PATH_1);
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
-                certSerial);
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getDefaultCertificateAliasIfEmpty(ROOT_CERTIFICATE_ALIAS);
+
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
         assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
     }
 
     @Test
+    public void initRecoveryService_triesToFilterRootAlias() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        long certSerial = 1000L;
+        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+        mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+                TestData.getCertXmlWithSerial(certSerial));
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getDefaultCertificateAliasIfEmpty(eq(ROOT_CERTIFICATE_ALIAS));
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+        String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+        assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+
+    }
+
+    @Test
+    public void initRecoveryService_usesProdCertificateForEmptyRootAlias() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        long certSerial = 1000L;
+        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+        mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ "",
+                TestData.getCertXmlWithSerial(certSerial));
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getDefaultCertificateAliasIfEmpty(eq(""));
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+        String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+        assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+    }
+
+    @Test
+    public void initRecoveryService_usesProdCertificateForNullRootAlias() throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        long certSerial = 1000L;
+        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+        mRecoverableKeyStoreManager.initRecoveryService(/*rootCertificateAlias=*/ null,
+                TestData.getCertXmlWithSerial(certSerial));
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getDefaultCertificateAliasIfEmpty(null);
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+
+        String activeRootAlias = mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid);
+        assertThat(activeRootAlias).isEqualTo(DEFAULT_ROOT_CERT_ALIAS);
+    }
+
+    @Test
     public void initRecoveryService_regeneratesCounterId() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
@@ -328,7 +382,8 @@
         byte[] modifiedCertXml = TestData.getCertXml();
         modifiedCertXml[modifiedCertXml.length - 50] ^= 1;  // Flip a bit in the certificate
         try {
-            mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, modifiedCertXml);
+            mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
+                    modifiedCertXml);
             fail("should have thrown");
         } catch (ServiceSpecificException e) {
             assertThat(e.getMessage()).contains("validate cert");
@@ -346,8 +401,9 @@
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial + 1));
 
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
-                .isEqualTo(certSerial + 1);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial + 1);
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
     }
 
     @Test
@@ -361,8 +417,9 @@
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial - 1));
 
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid))
-                .isEqualTo(certSerial);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(certSerial);
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
     }
 
     @Test
@@ -373,7 +430,6 @@
 
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
-        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS,
                 TestData.getCertXmlWithSerial(certSerial));
 
@@ -390,8 +446,10 @@
         mRecoverableKeyStoreManager.initRecoveryService(ROOT_CERTIFICATE_ALIAS, TEST_PUBLIC_KEY);
 
         assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isNull();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isNull();
         assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNotNull();
     }
 
@@ -404,13 +462,31 @@
         mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
                 ROOT_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
 
-        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
-                TestData.CERT_PATH_1);
+        assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isFalse();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                DEFAULT_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
         assertThat(mRecoverableKeyStoreDb.getRecoveryServicePublicKey(userId, uid)).isNull();
     }
 
     @Test
+    public void initRecoveryServiceWithSigFile_usesProdCertificateForNullRootAlias()
+            throws Exception {
+        int uid = Binder.getCallingUid();
+        int userId = UserHandle.getCallingUserId();
+        long certSerial = 1000L;
+        mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
+
+        mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+                /*rootCertificateAlias=*/null, TestData.getCertXml(), TestData.getSigXml());
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getDefaultCertificateAliasIfEmpty(null);
+
+        verify(mTestOnlyInsecureCertificateHelper, atLeast(1))
+                .getRootCertificate(eq(DEFAULT_ROOT_CERT_ALIAS));
+    }
+
+    @Test
     public void initRecoveryServiceWithSigFile_throwsIfNullCertFile() throws Exception {
         try {
             mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
@@ -447,6 +523,18 @@
     }
 
     @Test
+    public void initRecoveryServiceWithSigFile_throwsIfTestAliasUsedWithProdCert()
+            throws Exception {
+        try {
+        mRecoverableKeyStoreManager.initRecoveryServiceWithSigFile(
+                INSECURE_CERTIFICATE_ALIAS, TestData.getCertXml(), TestData.getSigXml());
+            fail("should have thrown");
+        } catch (ServiceSpecificException e) {
+            assertThat(e.getMessage()).contains("signature over the cert file is invalid");
+        }
+    }
+
+    @Test
     public void initRecoveryServiceWithSigFile_throwsIfInvalidFileSignature() throws Exception {
         byte[] modifiedCertXml = TestData.getCertXml();
         modifiedCertXml[modifiedCertXml.length - 1] = 0;  // Change the last new line char to a zero
@@ -479,7 +567,7 @@
     public void startRecoverySessionWithCertPath_storesTheSessionInfo() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
-                TEST_ROOT_CERT_ALIAS,
+                TEST_DEFAULT_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -496,7 +584,7 @@
     public void startRecoverySessionWithCertPath_checksPermissionFirst() throws Exception {
         mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                 TEST_SESSION_ID,
-                TEST_ROOT_CERT_ALIAS,
+                TEST_DEFAULT_ROOT_CERT_ALIAS,
                 RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                 TEST_VAULT_PARAMS,
                 TEST_VAULT_CHALLENGE,
@@ -600,7 +688,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
-                    TEST_ROOT_CERT_ALIAS,
+                    TEST_DEFAULT_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -619,7 +707,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
-                    TEST_ROOT_CERT_ALIAS,
+                    TEST_DEFAULT_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(TestData.CERT_PATH_1),
                     vaultParams,
                     TEST_VAULT_CHALLENGE,
@@ -637,7 +725,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
-                    TEST_ROOT_CERT_ALIAS,
+                    TEST_DEFAULT_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(emptyCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -657,7 +745,7 @@
         try {
             mRecoverableKeyStoreManager.startRecoverySessionWithCertPath(
                     TEST_SESSION_ID,
-                    TEST_ROOT_CERT_ALIAS,
+                    TEST_DEFAULT_ROOT_CERT_ALIAS,
                     RecoveryCertPath.createRecoveryCertPath(shortCertPath),
                     TEST_VAULT_PARAMS,
                     TEST_VAULT_CHALLENGE,
@@ -706,7 +794,8 @@
     }
 
     @Test
-    public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys() throws Exception {
+    public void recoverKeyChainSnapshot_throwsIfFailedToDecryptAllApplicationKeys()
+            throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -786,7 +875,8 @@
     }
 
     @Test
-    public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails() throws Exception {
+    public void recoverKeyChainSnapshot_worksOnOtherApplicationKeysIfOneDecryptionFails()
+            throws Exception {
         mRecoverableKeyStoreManager.startRecoverySession(
                 TEST_SESSION_ID,
                 TEST_PUBLIC_KEY,
@@ -946,12 +1036,12 @@
     public void setRecoverySecretTypes_updatesShouldCreateSnapshot() throws Exception {
         int uid = Binder.getCallingUid();
         int userId = UserHandle.getCallingUserId();
-        int[] types = new int[]{1, 2, 3};
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 });
 
-        mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS);
+        mRecoverableKeyStoreManager.generateKey(TEST_ALIAS);
         // Pretend that key was synced
         mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false);
-        mRecoverableKeyStoreManager.setRecoverySecretTypes(types);
+        mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 });
 
         assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue();
     }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
index 4b059c6..9b2c853 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestData.java
@@ -14,8 +14,12 @@
 import java.security.cert.CertPath;
 import java.security.spec.ECPrivateKeySpec;
 
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
 public final class TestData {
 
+    private static final String KEY_ALGORITHM = "AES";
     private static final long DEFAULT_SERIAL = 1000;
     private static final String CERT_PATH_ENCODING = "PkiPath";
 
@@ -308,4 +312,10 @@
         KeyFactory keyFactory = KeyFactory.getInstance("EC");
         return keyFactory.generatePrivate(new ECPrivateKeySpec(priv, SecureBox.EC_PARAM_SPEC));
     }
+
+    public static SecretKey generateKey() throws Exception {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+        keyGenerator.init(/*keySize=*/ 256);
+        return keyGenerator.generateKey();
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
new file mode 100644
index 0000000..bc50c9e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java
@@ -0,0 +1,128 @@
+package com.android.server.locksettings.recoverablekeystore;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.security.keystore.recovery.TrustedRootCertificates;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.internal.widget.LockPatternUtils;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.security.cert.X509Certificate;
+import java.util.Map;
+import javax.crypto.SecretKey;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TestOnlyInsecureCertificateHelperTest {
+    private final TestOnlyInsecureCertificateHelper mHelper
+            = new TestOnlyInsecureCertificateHelper();
+
+    @Test
+    public void testDoesCredentailSupportInsecureMode_forNonWhitelistedPassword() throws Exception {
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345")).isFalse();
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234")).isFalse();
+    }
+
+    @Test
+    public void testDoesCredentailSupportInsecureMode_forWhitelistedPassword() throws Exception {
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isTrue();
+
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12")).isTrue();
+    }
+
+    @Test
+    public void testDoesCredentailSupportInsecureMode_Pattern() throws Exception {
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
+                TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+        assertThat(mHelper.doesCredentailSupportInsecureMode(
+                LockPatternUtils.CREDENTIAL_TYPE_NONE,
+                TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse();
+    }
+
+    @Test
+    public void testIsTestOnlyCertificate() throws Exception {
+        assertThat(mHelper.isTestOnlyCertificate(
+                TrustedRootCertificates.GOOGLE_CLOUD_KEY_VAULT_SERVICE_V1_ALIAS)).isFalse();
+        assertThat(mHelper.isTestOnlyCertificate(
+                TrustedRootCertificates.TEST_ONLY_INSECURE_CERTIFICATE_ALIAS)).isTrue();
+        assertThat(mHelper.isTestOnlyCertificate(
+                "UNKNOWN_ALIAS")).isFalse();
+    }
+
+    @Test
+    public void testKeepOnlyWhitelistedInsecureKeys_emptyKeysList() throws Exception {
+        Map<String, SecretKey> rawKeys = new HashMap<>();
+        Map<String, SecretKey> expectedResult = new HashMap<>();
+
+        Map<String, SecretKey> filteredKeys =
+                mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+        assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+        assertThat(filteredKeys.entrySet()).containsAllIn(rawKeys.entrySet());
+    }
+
+    @Test
+    public void testKeepOnlyWhitelistedInsecureKeys_singleNonWhitelistedKey() throws Exception {
+        Map<String, SecretKey> rawKeys = new HashMap<>();
+        Map<String, SecretKey> expectedResult = new HashMap<>();
+
+        String alias = "secureAlias";
+        rawKeys.put(alias, TestData.generateKey());
+
+        Map<String, SecretKey> filteredKeys =
+                mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+        assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+    }
+
+    @Test
+    public void testKeepOnlyWhitelistedInsecureKeys_singleWhitelistedKey() throws Exception {
+        Map<String, SecretKey> rawKeys = new HashMap<>();
+        Map<String, SecretKey> expectedResult = new HashMap<>();
+
+        String alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
+        rawKeys.put(alias, TestData.generateKey());
+        expectedResult.put(alias, rawKeys.get(alias));
+
+        Map<String, SecretKey> filteredKeys =
+                mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+        assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+    }
+
+    @Test
+    public void testKeepOnlyWhitelistedInsecureKeys() throws Exception {
+        Map<String, SecretKey> rawKeys = new HashMap<>();
+        Map<String, SecretKey> expectedResult = new HashMap<>();
+
+        String alias = "SECURE_ALIAS" + TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX;
+        rawKeys.put(alias, TestData.generateKey());
+
+        alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "1";
+        rawKeys.put(alias, TestData.generateKey());
+        expectedResult.put(alias, rawKeys.get(alias));
+
+        alias = TrustedRootCertificates.INSECURE_KEY_ALIAS_PREFIX + "2";
+        rawKeys.put(alias, TestData.generateKey());
+        expectedResult.put(alias, rawKeys.get(alias));
+
+        Map<String, SecretKey> filteredKeys =
+                mHelper.keepOnlyWhitelistedInsecureKeys(rawKeys);
+        assertThat(filteredKeys.entrySet()).containsExactlyElementsIn(expectedResult.entrySet());
+        assertThat(rawKeys.entrySet()).containsAllIn(filteredKeys.entrySet());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
new file mode 100644
index 0000000..2f4da86
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/serialization/KeyChainSnapshotSerializerTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.locksettings.recoverablekeystore.serialization;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.security.keystore.recovery.KeyChainProtectionParams;
+import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.locksettings.recoverablekeystore.TestData;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.cert.CertPath;
+import java.util.ArrayList;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyChainSnapshotSerializerTest {
+    private static final int COUNTER_ID = 2134;
+    private static final int SNAPSHOT_VERSION = 125;
+    private static final int MAX_ATTEMPTS = 21;
+    private static final byte[] SERVER_PARAMS = new byte[] { 8, 2, 4 };
+    private static final byte[] KEY_BLOB = new byte[] { 124, 53, 53, 53 };
+    private static final byte[] PUBLIC_KEY_BLOB = new byte[] { 6, 6, 6, 6, 6, 6, 7 };
+    private static final CertPath CERT_PATH = TestData.CERT_PATH_1;
+    private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+    private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PASSWORD;
+    private static final byte[] SALT = new byte[] { 5, 4, 3, 2, 1 };
+    private static final int MEMORY_DIFFICULTY = 45;
+    private static final int ALGORITHM = KeyDerivationParams.ALGORITHM_SCRYPT;
+    private static final byte[] SECRET = new byte[] { 1, 2, 3, 4 };
+
+    private static final String TEST_KEY_1_ALIAS = "key1";
+    private static final byte[] TEST_KEY_1_BYTES = new byte[] { 66, 77, 88 };
+
+    private static final String TEST_KEY_2_ALIAS = "key2";
+    private static final byte[] TEST_KEY_2_BYTES = new byte[] { 99, 33, 11 };
+
+    private static final String TEST_KEY_3_ALIAS = "key3";
+    private static final byte[] TEST_KEY_3_BYTES = new byte[] { 2, 8, 100 };
+
+    @Test
+    public void roundTrip_persistsCounterId() throws Exception {
+        assertThat(roundTrip().getCounterId()).isEqualTo(COUNTER_ID);
+    }
+
+    @Test
+    public void roundTrip_persistsSnapshotVersion() throws Exception {
+        assertThat(roundTrip().getSnapshotVersion()).isEqualTo(SNAPSHOT_VERSION);
+    }
+
+    @Test
+    public void roundTrip_persistsMaxAttempts() throws Exception {
+        assertThat(roundTrip().getMaxAttempts()).isEqualTo(MAX_ATTEMPTS);
+    }
+
+    @Test
+    public void roundTrip_persistsRecoveryKey() throws Exception {
+        assertThat(roundTrip().getEncryptedRecoveryKeyBlob()).isEqualTo(KEY_BLOB);
+    }
+
+    @Test
+    public void roundTrip_persistsServerParams() throws Exception {
+        assertThat(roundTrip().getServerParams()).isEqualTo(SERVER_PARAMS);
+    }
+
+    @Test
+    public void roundTrip_persistsCertPath() throws Exception {
+        assertThat(roundTrip().getTrustedHardwareCertPath()).isEqualTo(CERT_PATH);
+    }
+
+    @Test
+    public void roundTrip_persistsBackendPublicKey() throws Exception {
+        assertThat(roundTrip().getTrustedHardwarePublicKey()).isEqualTo(PUBLIC_KEY_BLOB);
+    }
+
+    @Test
+    public void roundTrip_persistsParamsList() throws Exception {
+        assertThat(roundTrip().getKeyChainProtectionParams()).hasSize(1);
+    }
+
+    @Test
+    public void roundTripParams_persistsUserSecretType() throws Exception {
+        assertThat(roundTripParams().getUserSecretType()).isEqualTo(SECRET_TYPE);
+    }
+
+    @Test
+    public void roundTripParams_persistsLockScreenUi() throws Exception {
+        assertThat(roundTripParams().getLockScreenUiFormat()).isEqualTo(LOCK_SCREEN_UI);
+    }
+
+    @Test
+    public void roundTripParams_persistsSalt() throws Exception {
+        assertThat(roundTripParams().getKeyDerivationParams().getSalt()).isEqualTo(SALT);
+    }
+
+    @Test
+    public void roundTripParams_persistsAlgorithm() throws Exception {
+        assertThat(roundTripParams().getKeyDerivationParams().getAlgorithm()).isEqualTo(ALGORITHM);
+    }
+
+    @Test
+    public void roundTripParams_persistsMemoryDifficulty() throws Exception {
+        assertThat(roundTripParams().getKeyDerivationParams().getMemoryDifficulty())
+                .isEqualTo(MEMORY_DIFFICULTY);
+    }
+
+    @Test
+    public void roundTripParams_doesNotPersistSecret() throws Exception {
+        assertThat(roundTripParams().getSecret()).isEmpty();
+    }
+
+    @Test
+    public void roundTripKeys_hasCorrectLength() throws Exception {
+        assertThat(roundTripKeys()).hasSize(3);
+    }
+
+    @Test
+    public void roundTripKeys_0_persistsAlias() throws Exception {
+        assertThat(roundTripKeys().get(0).getAlias()).isEqualTo(TEST_KEY_1_ALIAS);
+    }
+
+    @Test
+    public void roundTripKeys_0_persistsKeyBytes() throws Exception {
+        assertThat(roundTripKeys().get(0).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_1_BYTES);
+    }
+
+    @Test
+    public void roundTripKeys_1_persistsAlias() throws Exception {
+        assertThat(roundTripKeys().get(1).getAlias()).isEqualTo(TEST_KEY_2_ALIAS);
+    }
+
+    @Test
+    public void roundTripKeys_1_persistsKeyBytes() throws Exception {
+        assertThat(roundTripKeys().get(1).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_2_BYTES);
+    }
+
+    @Test
+    public void roundTripKeys_2_persistsAlias() throws Exception {
+        assertThat(roundTripKeys().get(2).getAlias()).isEqualTo(TEST_KEY_3_ALIAS);
+    }
+
+    @Test
+    public void roundTripKeys_2_persistsKeyBytes() throws Exception {
+        assertThat(roundTripKeys().get(2).getEncryptedKeyMaterial()).isEqualTo(TEST_KEY_3_BYTES);
+    }
+
+    @Test
+    public void serialize_doesNotThrowForNullPublicKey() throws Exception {
+        KeyChainSnapshotSerializer.serialize(
+                createTestKeyChainSnapshotNoPublicKey(), new ByteArrayOutputStream());
+    }
+
+    private static List<WrappedApplicationKey> roundTripKeys() throws Exception {
+        return roundTrip().getWrappedApplicationKeys();
+    }
+
+    private static KeyChainProtectionParams roundTripParams() throws Exception {
+        return roundTrip().getKeyChainProtectionParams().get(0);
+    }
+
+    public static KeyChainSnapshot roundTrip() throws Exception {
+        KeyChainSnapshot snapshot = createTestKeyChainSnapshot();
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        KeyChainSnapshotSerializer.serialize(snapshot, byteArrayOutputStream);
+        return KeyChainSnapshotDeserializer.deserialize(
+                new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshot() throws Exception {
+        return new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+                .setWrappedApplicationKeys(createKeys())
+                .setTrustedHardwareCertPath(CERT_PATH)
+                .setTrustedHardwarePublicKey(PUBLIC_KEY_BLOB)
+                .build();
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshotNoPublicKey() throws Exception {
+        return new KeyChainSnapshot.Builder()
+                .setCounterId(COUNTER_ID)
+                .setSnapshotVersion(SNAPSHOT_VERSION)
+                .setServerParams(SERVER_PARAMS)
+                .setMaxAttempts(MAX_ATTEMPTS)
+                .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                .setKeyChainProtectionParams(createKeyChainProtectionParamsList())
+                .setWrappedApplicationKeys(createKeys())
+                .setTrustedHardwareCertPath(CERT_PATH)
+                .build();
+    }
+
+    private static List<WrappedApplicationKey> createKeys() {
+        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+        return keyList;
+    }
+
+    private static List<KeyChainProtectionParams> createKeyChainProtectionParamsList() {
+        KeyDerivationParams keyDerivationParams =
+                KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+        KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+                .setKeyDerivationParams(keyDerivationParams)
+                .setUserSecretType(SECRET_TYPE)
+                .setLockScreenUiFormat(LOCK_SCREEN_UI)
+                .setSecret(SECRET)
+                .build();
+        ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+                new ArrayList<>(1);
+        keyChainProtectionParamsList.add(keyChainProtectionParams);
+        return keyChainProtectionParamsList;
+    }
+
+    private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(alias)
+                .setEncryptedKeyMaterial(bytes)
+                .build();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
index 37482a3..9b09dd1a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbHelperTest.java
@@ -34,6 +34,7 @@
 
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.KeysEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RecoveryServiceMetadataEntry;
+import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.RootOfTrustEntry;
 import com.android.server.locksettings.recoverablekeystore.storage.RecoverableKeyStoreDbContract.UserMetadataEntry;
 
 @SmallTest
@@ -55,6 +56,7 @@
     private static final String TEST_SECRET_TYPES = "test-secret-types";
     private static final long TEST_COUNTER_ID = -3981205205038476415L;
     private static final byte[] TEST_SERVER_PARAMS = "test-server-params".getBytes(UTF_8);
+    private static final String TEST_ROOT_ALIAS = "root_cert_alias";
     private static final byte[] TEST_CERT_PATH = "test-cert-path".getBytes(UTF_8);
     private static final long TEST_CERT_SERIAL = 1000L;
 
@@ -135,6 +137,32 @@
         checkAllColumns();
     }
 
+    @Test
+    public void onUpgrade_v2_to_v3_to_v4() throws Exception {
+        createV2Tables();
+
+        assertThat(isRootOfTrustTableAvailable()).isFalse(); // V2 doesn't have the table;
+
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 2, /*newVersion=*/ 3);
+
+        assertThat(isRootOfTrustTableAvailable()).isFalse(); // V3 doesn't have the table;
+
+        mDatabaseHelper.onUpgrade(mDatabase, /*oldVersion=*/ 3,
+                RecoverableKeyStoreDbHelper.DATABASE_VERSION);
+        checkAllColumns();
+    }
+
+    private boolean isRootOfTrustTableAvailable() {
+        ContentValues values = new ContentValues();
+        values.put(RootOfTrustEntry.COLUMN_NAME_USER_ID, TEST_USER_ID);
+        values.put(RootOfTrustEntry.COLUMN_NAME_UID, TEST_UID);
+        values.put(RootOfTrustEntry.COLUMN_NAME_ROOT_ALIAS, TEST_ROOT_ALIAS);
+        values.put(RootOfTrustEntry.COLUMN_NAME_CERT_PATH, TEST_CERT_PATH);
+        values.put(RootOfTrustEntry.COLUMN_NAME_CERT_SERIAL, TEST_CERT_SERIAL);
+        return mDatabase.insert(RootOfTrustEntry.TABLE_NAME, /*nullColumnHack=*/ null, values)
+                > -1;
+    }
+
     private void checkAllColumns() throws Exception {
         // Check the table containing encrypted application keys
         ContentValues values = new ContentValues();
@@ -165,6 +193,7 @@
                 TEST_SNAPSHOT_VERSION);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SHOULD_CREATE_SNAPSHOT,
                 TEST_SHOULD_CREATE_SNAPSHOT);
+        values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_ACTIVE_ROOT_OF_TRUST, TEST_ROOT_ALIAS);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_PUBLIC_KEY, TEST_PUBLIC_KEY);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_SECRET_TYPES, TEST_SECRET_TYPES);
         values.put(RecoveryServiceMetadataEntry.COLUMN_NAME_COUNTER_ID, TEST_COUNTER_ID);
@@ -175,5 +204,8 @@
                 mDatabase.insert(RecoveryServiceMetadataEntry.TABLE_NAME, /*nullColumnHack=*/ null,
                         values))
                 .isGreaterThan(-1L);
+
+        // Check the table about recovery service and root of trust data introduced in V4
+        assertThat(isRootOfTrustTableAvailable()).isTrue();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
index 8b01d97..940745e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverableKeyStoreDbTest.java
@@ -49,6 +49,9 @@
 public class RecoverableKeyStoreDbTest {
     private static final String DATABASE_FILE_NAME = "recoverablekeystore.db";
 
+    private static final String TEST_ROOT_CERT_ALIAS = "trusted_root";
+    private static final String TEST_ROOT_CERT_ALIAS2 = "another_trusted_root";
+
     private RecoverableKeyStoreDb mRecoverableKeyStoreDb;
     private File mDatabaseFile;
 
@@ -284,7 +287,8 @@
 
         Map<String, Integer> statuses = mRecoverableKeyStoreDb.getStatusForAllKeys(uid);
         assertThat(statuses).hasSize(3);
-        assertThat(statuses).containsEntry(alias, RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
+        assertThat(statuses).containsEntry(alias,
+                RecoveryController.RECOVERY_STATUS_SYNC_IN_PROGRESS);
         assertThat(statuses).containsEntry(alias2, status);
         assertThat(statuses).containsEntry(alias3, status);
 
@@ -401,26 +405,53 @@
     public void setRecoveryServiceCertPath_replaceOldValue() throws Exception {
         int userId = 12;
         int uid = 10009;
-        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
-        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_2);
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
+                TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
                 TestData.CERT_PATH_2);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_2);
+    }
+
+    @Test
+    public void setRecoveryServiceCertPath_updateValuesForCorrectRootCert() throws Exception {
+        int userId = 12;
+        int uid = 10009;
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
+                TestData.CERT_PATH_1);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS2,
+                TestData.CERT_PATH_1);
+
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS2)).isEqualTo(TestData.CERT_PATH_1);
+
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS2,
+                TestData.CERT_PATH_2);
+
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS2)).isEqualTo(TestData.CERT_PATH_2);
     }
 
     @Test
     public void getRecoveryServiceCertPath_returnsNullIfNoValue() throws Exception {
         int userId = 12;
         int uid = 10009;
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isNull();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isNull();
     }
 
     @Test
     public void getRecoveryServiceCertPath_returnsInsertedValue() throws Exception {
         int userId = 12;
         int uid = 10009;
-        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TestData.CERT_PATH_1);
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid)).isEqualTo(
+        mRecoverableKeyStoreDb.setRecoveryServiceCertPath(userId, uid, TEST_ROOT_CERT_ALIAS,
                 TestData.CERT_PATH_1);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertPath(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(TestData.CERT_PATH_1);
     }
 
     @Test
@@ -428,25 +459,50 @@
         int userId = 12;
         int uid = 10009;
 
-        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1L);
-        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 3L);
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(3L);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 1L);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 3L);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(3L);
+    }
+
+    @Test
+    public void setRecoveryServiceCertSerial_updateValuesForCorrectRootCert() throws Exception {
+        int userId = 12;
+        int uid = 10009;
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS, 1L);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS2, 1L);
+
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(1L);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS2)).isEqualTo(1L);
+
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, TEST_ROOT_CERT_ALIAS2, 3L);
+
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(1L);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS2)).isEqualTo(3L);
     }
 
     @Test
     public void getRecoveryServiceCertSerial_returnsNullIfNoValue() throws Exception {
         int userId = 12;
         int uid = 10009;
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isNull();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isNull();
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS2)).isNull();
     }
 
     @Test
     public void getRecoveryServiceCertSerial_returnsInsertedValue() throws Exception {
         int userId = 12;
         int uid = 10009;
-        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid, 1234L);
-        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid)).isEqualTo(
-                1234L);
+        mRecoverableKeyStoreDb.setRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS, 1234L);
+        assertThat(mRecoverableKeyStoreDb.getRecoveryServiceCertSerial(userId, uid,
+                TEST_ROOT_CERT_ALIAS)).isEqualTo(1234L);
     }
 
     @Test
@@ -480,6 +536,24 @@
     }
 
     @Test
+    public void setActiveRootOfTrust_emptyDefaultValue() throws Exception {
+        int userId = 12;
+        int uid = 10009;
+        assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo(null);
+    }
+
+    @Test
+    public void setActiveRootOfTrust_updateValue() throws Exception {
+        int userId = 12;
+        int uid = 10009;
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(userId, uid, "root");
+        assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo("root");
+
+        mRecoverableKeyStoreDb.setActiveRootOfTrust(userId, uid, "root2");
+        assertThat(mRecoverableKeyStoreDb.getActiveRootOfTrust(userId, uid)).isEqualTo("root2");
+    }
+
+    @Test
     public void setRecoverySecretTypes_emptyDefaultValue() throws Exception {
         int userId = 12;
         int uid = 10009;
@@ -495,11 +569,9 @@
         int[] types2 = new int[]{2};
 
         mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types1);
-        assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
-                types1);
+        assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(types1);
         mRecoverableKeyStoreDb.setRecoverySecretTypes(userId, uid, types2);
-        assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(
-                types2);
+        assertThat(mRecoverableKeyStoreDb.getRecoverySecretTypes(userId, uid)).isEqualTo(types2);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
index c772956..ad14c3a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/storage/RecoverySnapshotStorageTest.java
@@ -1,27 +1,82 @@
 package com.android.server.locksettings.recoverablekeystore.storage;
 
+import static org.junit.Assert.assertArrayEquals;
 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 android.content.Context;
+import android.os.FileUtils;
+import android.security.keystore.recovery.KeyChainProtectionParams;
 import android.security.keystore.recovery.KeyChainSnapshot;
+import android.security.keystore.recovery.KeyDerivationParams;
+import android.security.keystore.recovery.WrappedApplicationKey;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.server.locksettings.recoverablekeystore.TestData;
 
+import com.google.common.io.Files;
+
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertPath;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
+import java.util.List;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class RecoverySnapshotStorageTest {
-    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
-            createMinimalKeyChainSnapshot();
+    private static final int COUNTER_ID = 432546;
+    private static final int MAX_ATTEMPTS = 10;
+    private static final byte[] SERVER_PARAMS = new byte[] { 12, 8, 2, 4, 15, 64 };
+    private static final byte[] KEY_BLOB = new byte[] { 124, 56, 53, 99, 0, 0, 1 };
+    private static final CertPath CERT_PATH = TestData.CERT_PATH_2;
+    private static final int SECRET_TYPE = KeyChainProtectionParams.TYPE_LOCKSCREEN;
+    private static final int LOCK_SCREEN_UI = KeyChainProtectionParams.UI_FORMAT_PATTERN;
+    private static final byte[] SALT = new byte[] { 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1 };
+    private static final int MEMORY_DIFFICULTY = 12;
+    private static final byte[] SECRET = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0 };
 
-    private final RecoverySnapshotStorage mRecoverySnapshotStorage = new RecoverySnapshotStorage();
+    private static final String TEST_KEY_1_ALIAS = "alias1";
+    private static final byte[] TEST_KEY_1_BYTES = new byte[] { 100, 32, 43, 66, 77, 88 };
+
+    private static final String TEST_KEY_2_ALIAS = "alias11";
+    private static final byte[] TEST_KEY_2_BYTES = new byte[] { 100, 0, 0, 99, 33, 11 };
+
+    private static final String TEST_KEY_3_ALIAS = "alias111";
+    private static final byte[] TEST_KEY_3_BYTES = new byte[] { 1, 1, 1, 0, 2, 8, 100 };
+
+    private static final int TEST_UID = 1000;
+    private static final String SNAPSHOT_DIRECTORY = "recoverablekeystore/snapshots";
+    private static final String SNAPSHOT_FILE_PATH = "1000.xml";
+    private static final String SNAPSHOT_TOP_LEVEL_DIRECTORY = "recoverablekeystore";
+
+    private static final KeyChainSnapshot MINIMAL_KEYCHAIN_SNAPSHOT =
+            createTestKeyChainSnapshot(1);
+
+    private Context mContext;
+    private RecoverySnapshotStorage mRecoverySnapshotStorage;
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mRecoverySnapshotStorage = new RecoverySnapshotStorage(mContext.getFilesDir());
+    }
+
+    @After
+    public void tearDown() {
+        File file = new File(mContext.getFilesDir(), SNAPSHOT_TOP_LEVEL_DIRECTORY);
+        FileUtils.deleteContentsAndDir(file);
+    }
 
     @Test
     public void get_isNullForNonExistentSnapshot() {
@@ -30,37 +85,153 @@
 
     @Test
     public void get_returnsSetSnapshot() {
-        int userId = 1000;
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
 
-        mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-
-        assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(userId));
+        assertEquals(MINIMAL_KEYCHAIN_SNAPSHOT, mRecoverySnapshotStorage.get(TEST_UID));
     }
 
     @Test
-    public void remove_removesSnapshots() {
-        int userId = 1000;
+    public void get_readsFromDiskIfNoneInMemory() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+        RecoverySnapshotStorage storage = new RecoverySnapshotStorage(mContext.getFilesDir());
 
-        mRecoverySnapshotStorage.put(userId, MINIMAL_KEYCHAIN_SNAPSHOT);
-        mRecoverySnapshotStorage.remove(userId);
-
-        assertNull(mRecoverySnapshotStorage.get(1000));
+        assertKeyChainSnapshotsAreEqual(MINIMAL_KEYCHAIN_SNAPSHOT, storage.get(TEST_UID));
     }
 
-    private static KeyChainSnapshot createMinimalKeyChainSnapshot() {
+    @Test
+    public void get_deletesFileIfItIsInvalidSnapshot() throws Exception {
+        File folder = new File(mContext.getFilesDir(), SNAPSHOT_DIRECTORY);
+        folder.mkdirs();
+        File file = new File(folder, SNAPSHOT_FILE_PATH);
+        byte[] fileContents = "<keyChainSnapshot></keyChainSnapshot>".getBytes(
+                StandardCharsets.UTF_8);
+        Files.write(fileContents, file);
+        assertTrue(file.exists());
+
+        assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+
+        assertFalse(file.exists());
+    }
+
+    @Test
+    public void put_overwritesOldFiles() {
+        int snapshotVersion = 2;
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        mRecoverySnapshotStorage.put(TEST_UID, createTestKeyChainSnapshot(snapshotVersion));
+
+        KeyChainSnapshot snapshot = new RecoverySnapshotStorage(mContext.getFilesDir())
+                .get(TEST_UID);
+        assertEquals(snapshotVersion, snapshot.getSnapshotVersion());
+    }
+
+    @Test
+    public void put_doesNotThrowIfCannotCreateFiles() throws Exception {
+        File evilFile = new File(mContext.getFilesDir(), "recoverablekeystore");
+        Files.write(new byte[] { 1 }, evilFile);
+
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+    }
+
+    @Test
+    public void remove_removesSnapshotsFromMemory() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+        mRecoverySnapshotStorage.remove(TEST_UID);
+
+        assertNull(mRecoverySnapshotStorage.get(TEST_UID));
+    }
+
+    @Test
+    public void remove_removesSnapshotsFromDisk() {
+        mRecoverySnapshotStorage.put(TEST_UID, MINIMAL_KEYCHAIN_SNAPSHOT);
+
+        new RecoverySnapshotStorage(mContext.getFilesDir()).remove(TEST_UID);
+
+        assertNull(new RecoverySnapshotStorage(mContext.getFilesDir()).get(TEST_UID));
+    }
+
+    private void assertKeyChainSnapshotsAreEqual(KeyChainSnapshot a, KeyChainSnapshot b) {
+        assertEquals(b.getCounterId(), a.getCounterId());
+        assertEquals(b.getSnapshotVersion(), a.getSnapshotVersion());
+        assertArrayEquals(b.getServerParams(), a.getServerParams());
+        assertEquals(b.getMaxAttempts(), a.getMaxAttempts());
+        assertArrayEquals(b.getEncryptedRecoveryKeyBlob(), a.getEncryptedRecoveryKeyBlob());
+        assertEquals(b.getTrustedHardwareCertPath(), a.getTrustedHardwareCertPath());
+
+        List<WrappedApplicationKey> aKeys = a.getWrappedApplicationKeys();
+        List<WrappedApplicationKey> bKeys = b.getWrappedApplicationKeys();
+        assertEquals(bKeys.size(), aKeys.size());
+        for (int i = 0; i < aKeys.size(); i++) {
+            assertWrappedApplicationKeysAreEqual(aKeys.get(i), bKeys.get(i));
+        }
+
+        List<KeyChainProtectionParams> aParams = a.getKeyChainProtectionParams();
+        List<KeyChainProtectionParams> bParams = b.getKeyChainProtectionParams();
+        assertEquals(bParams.size(), aParams.size());
+        for (int i = 0; i < aParams.size(); i++) {
+            assertKeyChainProtectionParamsAreEqual(aParams.get(i), bParams.get(i));
+        }
+    }
+
+    private void assertWrappedApplicationKeysAreEqual(
+            WrappedApplicationKey a, WrappedApplicationKey b) {
+        assertEquals(b.getAlias(), a.getAlias());
+        assertArrayEquals(b.getEncryptedKeyMaterial(), a.getEncryptedKeyMaterial());
+    }
+
+    private void assertKeyChainProtectionParamsAreEqual(
+            KeyChainProtectionParams a, KeyChainProtectionParams b) {
+        assertEquals(b.getUserSecretType(), a.getUserSecretType());
+        assertEquals(b.getLockScreenUiFormat(), a.getLockScreenUiFormat());
+        assertKeyDerivationParamsAreEqual(a.getKeyDerivationParams(), b.getKeyDerivationParams());
+    }
+
+    private void assertKeyDerivationParamsAreEqual(KeyDerivationParams a, KeyDerivationParams b) {
+        assertEquals(b.getAlgorithm(), a.getAlgorithm());
+        assertEquals(b.getMemoryDifficulty(), a.getMemoryDifficulty());
+        assertArrayEquals(b.getSalt(), a.getSalt());
+    }
+
+    private static KeyChainSnapshot createTestKeyChainSnapshot(int snapshotVersion) {
+        KeyDerivationParams keyDerivationParams =
+                KeyDerivationParams.createScryptParams(SALT, MEMORY_DIFFICULTY);
+        KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder()
+                .setKeyDerivationParams(keyDerivationParams)
+                .setUserSecretType(SECRET_TYPE)
+                .setLockScreenUiFormat(LOCK_SCREEN_UI)
+                .setSecret(SECRET)
+                .build();
+        ArrayList<KeyChainProtectionParams> keyChainProtectionParamsList =
+                new ArrayList<>(1);
+        keyChainProtectionParamsList.add(keyChainProtectionParams);
+
+        ArrayList<WrappedApplicationKey> keyList = new ArrayList<>();
+        keyList.add(createKey(TEST_KEY_1_ALIAS, TEST_KEY_1_BYTES));
+        keyList.add(createKey(TEST_KEY_2_ALIAS, TEST_KEY_2_BYTES));
+        keyList.add(createKey(TEST_KEY_3_ALIAS, TEST_KEY_3_BYTES));
+
         try {
             return new KeyChainSnapshot.Builder()
-                    .setCounterId(1)
-                    .setSnapshotVersion(1)
-                    .setServerParams(new byte[0])
-                    .setMaxAttempts(10)
-                    .setEncryptedRecoveryKeyBlob(new byte[0])
-                    .setKeyChainProtectionParams(new ArrayList<>())
-                    .setWrappedApplicationKeys(new ArrayList<>())
-                    .setTrustedHardwareCertPath(TestData.CERT_PATH_1)
+                    .setCounterId(COUNTER_ID)
+                    .setSnapshotVersion(snapshotVersion)
+                    .setServerParams(SERVER_PARAMS)
+                    .setMaxAttempts(MAX_ATTEMPTS)
+                    .setEncryptedRecoveryKeyBlob(KEY_BLOB)
+                    .setKeyChainProtectionParams(keyChainProtectionParamsList)
+                    .setWrappedApplicationKeys(keyList)
+                    .setTrustedHardwareCertPath(CERT_PATH)
                     .build();
         } catch (CertificateException e) {
             throw new RuntimeException(e);
         }
     }
+
+    private static WrappedApplicationKey createKey(String alias, byte[] bytes) {
+        return new WrappedApplicationKey.Builder()
+                .setAlias(alias)
+                .setEncryptedKeyMaterial(bytes)
+                .build();
+    }
 }
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 d702318..ee0ad39 100644
--- a/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/SuspendPackagesTest.java
@@ -16,55 +16,129 @@
 
 package com.android.server.pm;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.os.BaseBundle;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.PersistableBundle;
+import android.os.RemoteException;
 import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReference;
 
 @RunWith(AndroidJUnit4.class)
-@MediumTest
+@LargeTest
 public class SuspendPackagesTest {
     private static final String TEST_APP_PACKAGE_NAME = SuspendTestReceiver.PACKAGE_NAME;
     private static final String[] PACKAGES_TO_SUSPEND = new String[]{TEST_APP_PACKAGE_NAME};
 
+    public static final String INSTRUMENTATION_PACKAGE = "com.android.frameworks.servicestests";
+    public static final String ACTION_REPORT_MY_PACKAGE_SUSPENDED =
+            INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED";
+    public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED =
+            INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED";
+
     private Context mContext;
     private PackageManager mPackageManager;
     private Handler mReceiverHandler;
     private ComponentName mTestReceiverComponent;
+    private AppCommunicationReceiver mAppCommsReceiver;
+
+    private static final class AppCommunicationReceiver extends BroadcastReceiver {
+        private Context context;
+        private boolean registered;
+        private SynchronousQueue<Intent> intentQueue = new SynchronousQueue<>();
+
+        AppCommunicationReceiver(Context context) {
+            this.context = context;
+        }
+
+        void register(Handler handler) {
+            registered = true;
+            final IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_SUSPENDED);
+            intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED);
+            context.registerReceiver(this, intentFilter, null, handler);
+        }
+
+        void unregister() {
+            if (registered) {
+                context.unregisterReceiver(this);
+            }
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            try {
+                intentQueue.offer(intent, 5, TimeUnit.SECONDS);
+            } catch (InterruptedException ie) {
+                throw new RuntimeException("Receiver thread interrupted", ie);
+            }
+        }
+
+        Intent receiveIntentFromApp() {
+            if (!registered) {
+                throw new IllegalStateException("Receiver not registered");
+            }
+            final Intent intent;
+            try {
+                intent = intentQueue.poll(5, TimeUnit.SECONDS);
+            } catch (InterruptedException ie) {
+                throw new RuntimeException("Interrupted while waiting for app broadcast", ie);
+            }
+            assertNotNull("No intent received from app within 5 seconds", intent);
+            return intent;
+        }
+    }
 
     @Before
     public void setUp() {
         mContext = InstrumentationRegistry.getTargetContext();
         mPackageManager = mContext.getPackageManager();
-        mPackageManager.setPackagesSuspended(PACKAGES_TO_SUSPEND, false, null, null, null);
         mReceiverHandler = new Handler(Looper.getMainLooper());
         mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
                 SuspendTestReceiver.class.getCanonicalName());
+        IPackageManager ipm = AppGlobals.getPackageManager();
+        try {
+            // Otherwise implicit broadcasts will not be delivered.
+            ipm.setPackageStoppedState(TEST_APP_PACKAGE_NAME, false, mContext.getUserId());
+        } catch (RemoteException e) {
+            e.rethrowAsRuntimeException();
+        }
+        unsuspendTestPackage();
+        mAppCommsReceiver = new AppCommunicationReceiver(mContext);
     }
 
+    /**
+     * Care should be taken when used with {@link #mAppCommsReceiver} in the same test as both use
+     * the same handler.
+     */
     private Bundle requestAppAction(String action) throws InterruptedException {
         final AtomicReference<Bundle> result = new AtomicReference<>();
         final CountDownLatch receiverLatch = new CountDownLatch(1);
@@ -98,6 +172,24 @@
         assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
     }
 
+    private void unsuspendTestPackage() {
+        final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
+                PACKAGES_TO_SUSPEND, false, null, null, null);
+        assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
+    }
+
+
+    private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
+        if (expected != null) {
+            expected.get(""); // hack to unparcel the bundles.
+        }
+        if (received != null) {
+            received.get("");
+        }
+        assertTrue(message + ": [expected: " + expected + "; received: " + received + "]",
+                BaseBundle.kindofEquals(expected, received));
+    }
+
     @Test
     public void testIsPackageSuspended() {
         suspendTestPackage(null, null);
@@ -109,19 +201,73 @@
     public void testSuspendedStateFromApp() throws Exception {
         Bundle resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
         assertFalse(resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED, true));
-        assertNull(resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+        assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
 
-        final PersistableBundle appExtras = getExtras("appExtras", 20, "20", 0.2);
+        final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2);
         suspendTestPackage(appExtras, null);
 
         resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
         assertTrue("resultFromApp:suspended is false",
                 resultFromApp.getBoolean(SuspendTestReceiver.EXTRA_SUSPENDED));
-        final PersistableBundle receivedAppExtras =
-                resultFromApp.getParcelable(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS);
-        receivedAppExtras.get(""); // hack to unparcel the bundles
-        appExtras.get("");
-        assertTrue("Received app extras " + receivedAppExtras + " different to the ones supplied",
-                BaseBundle.kindofEquals(appExtras, receivedAppExtras));
+        final Bundle receivedAppExtras =
+                resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS);
+        assertSameExtras("Received app extras different to the ones supplied",
+                appExtras, receivedAppExtras);
+    }
+
+    @Test
+    public void testMyPackageSuspendedUnsuspended() {
+        mAppCommsReceiver.register(mReceiverHandler);
+        final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1);
+        suspendTestPackage(appExtras, null);
+        Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertSameExtras("Received app extras different to the ones supplied", appExtras,
+                intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+        unsuspendTestPackage();
+        intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertTrue("MY_PACKAGE_UNSUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_UNSUSPENDED.equals(intentFromApp.getAction()));
+    }
+
+    @Test
+    public void testUpdatingAppExtras() {
+        mAppCommsReceiver.register(mReceiverHandler);
+        final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1,
+                "1", 0.1);
+        suspendTestPackage(extras1, null);
+        Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertSameExtras("Received app extras different to the ones supplied", extras1,
+                intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+        final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
+                "2", 0.2);
+        mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
+        intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
+        assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
+                ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
+        assertSameExtras("Received app extras different to the updated extras", extras2,
+                intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
+    }
+
+    @Test
+    public void testCannotSuspendSelf() {
+        final String[] unchangedPkgs = mPackageManager.setPackagesSuspended(
+                new String[]{mContext.getOpPackageName()}, true, null, null, null);
+        assertTrue(unchangedPkgs.length == 1);
+        assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mAppCommsReceiver.unregister();
+        Thread.sleep(250); // To prevent any race with the next registerReceiver
+    }
+
+    @FunctionalInterface
+    interface Condition {
+        boolean isTrue();
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 601999d..c2a0ccf 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -281,6 +281,7 @@
         MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
         mInjector = new MyInjector(myContext, Looper.getMainLooper());
         mController = setupController();
+        setChargingState(mController, false);
     }
 
     @Test
@@ -381,8 +382,6 @@
 
     @Test
     public void testForcedIdle() throws Exception {
-        setChargingState(mController, false);
-
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
         assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
@@ -395,8 +394,6 @@
 
     @Test
     public void testNotificationEvent() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
         mInjector.mElapsedRealtime = 1;
@@ -410,8 +407,6 @@
 
     @Test
     public void testSlicePinnedEvent() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
         mInjector.mElapsedRealtime = 1;
@@ -425,8 +420,6 @@
 
     @Test
     public void testSlicePinnedPrivEvent() throws Exception {
-        setChargingState(mController, false);
-
         mController.forceIdleState(PACKAGE_1, USER_ID, true);
         reportEvent(mController, SLICE_PINNED_PRIV, mInjector.mElapsedRealtime);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
@@ -434,14 +427,13 @@
 
     @Test
     public void testPredictionTimedout() throws Exception {
-        setChargingState(mController, false);
         // Set it to timeout or usage, so that prediction can override it
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
-                REASON_MAIN_TIMEOUT, 1 * HOUR_MS);
+                REASON_MAIN_TIMEOUT, HOUR_MS);
         assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
 
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_MAIN_PREDICTED, 1 * HOUR_MS);
+                REASON_MAIN_PREDICTED, HOUR_MS);
         assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
 
         // Fast forward 12 hours
@@ -464,7 +456,6 @@
 
     @Test
     public void testOverrides() throws Exception {
-        setChargingState(mController, false);
         // Can force to NEVER
         mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
                 REASON_MAIN_FORCED, 1 * HOUR_MS);
@@ -494,8 +485,6 @@
 
     @Test
     public void testTimeout() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
@@ -505,19 +494,19 @@
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
         // bucketing works after timeout
-        mInjector.mElapsedRealtime = FREQUENT_THRESHOLD - 100;
+        mInjector.mElapsedRealtime = mController.mPredictionTimeoutMillis - 100;
         mController.checkIdleStates(USER_ID);
-        assertBucket(STANDBY_BUCKET_WORKING_SET);
-
-        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
-                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+        // Use recent prediction
         assertBucket(STANDBY_BUCKET_FREQUENT);
+
+        // Way past prediction timeout, use system thresholds
+        mInjector.mElapsedRealtime = RARE_THRESHOLD * 4;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_RARE);
     }
 
     @Test
     public void testCascadingTimeouts() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
@@ -539,8 +528,6 @@
 
     @Test
     public void testOverlappingTimeouts() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
@@ -596,8 +583,6 @@
 
     @Test
     public void testPredictionNotOverridden() throws Exception {
-        setChargingState(mController, false);
-
         reportEvent(mController, USER_INTERACTION, 0);
         assertBucket(STANDBY_BUCKET_ACTIVE);
 
@@ -623,6 +608,31 @@
     }
 
     @Test
+    public void testPredictionStrikesBack() throws Exception {
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Predict to FREQUENT
+        mInjector.mElapsedRealtime = RARE_THRESHOLD;
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_MAIN_PREDICTED, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_FREQUENT);
+
+        // Add a short timeout event
+        mInjector.mElapsedRealtime += 1000;
+        reportEvent(mController, SYSTEM_INTERACTION, mInjector.mElapsedRealtime);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+        mInjector.mElapsedRealtime += 1000;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_ACTIVE);
+
+        // Verify it reverted to predicted
+        mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD / 2;
+        mController.checkIdleStates(USER_ID);
+        assertBucket(STANDBY_BUCKET_FREQUENT);
+    }
+
+    @Test
     public void testAddActiveDeviceAdmin() {
         assertActiveAdmins(USER_ID, (String[]) null);
         assertActiveAdmins(USER_ID2, (String[]) null);
diff --git a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
index 57dd808..6769e40 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DimmerTests.java
@@ -18,8 +18,12 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -99,16 +103,23 @@
     private MockSurfaceBuildingContainer mHost;
     private Dimmer mDimmer;
     private SurfaceControl.Transaction mTransaction;
+    private Dimmer.SurfaceAnimatorStarter mSurfaceAnimatorStarter;
+
+    private static class SurfaceAnimatorStarterImpl implements Dimmer.SurfaceAnimatorStarter {
+        @Override
+        public void startAnimation(SurfaceAnimator surfaceAnimator, SurfaceControl.Transaction t,
+                AnimationAdapter anim, boolean hidden) {
+            surfaceAnimator.mAnimationFinishedCallback.run();
+        }
+    }
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
         mHost = new MockSurfaceBuildingContainer();
-
+        mSurfaceAnimatorStarter = spy(new SurfaceAnimatorStarterImpl());
         mTransaction = mock(SurfaceControl.Transaction.class);
-        mDimmer = new Dimmer(mHost,
-                (surfaceAnimator, t, anim, hidden) -> surfaceAnimator.mAnimationFinishedCallback
-                        .run());
+        mDimmer = new Dimmer(mHost, mSurfaceAnimatorStarter);
     }
 
     @Test
@@ -202,6 +213,8 @@
         mDimmer.resetDimStates();
 
         mDimmer.updateDims(mTransaction, new Rect());
+        verify(mSurfaceAnimatorStarter).startAnimation(any(SurfaceAnimator.class), any(
+                SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
         verify(dimLayer).destroy();
     }
 
@@ -243,6 +256,25 @@
         verify(mTransaction).setPosition(dimLayer, 10, 10);
     }
 
+    @Test
+    public void testRemoveDimImmediately() throws Exception {
+        TestWindowContainer child = new TestWindowContainer();
+        mHost.addChild(child, 0);
+
+        mDimmer.dimAbove(mTransaction, child, 1);
+        SurfaceControl dimLayer = getDimLayer();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mTransaction, times(1)).show(dimLayer);
+
+        reset(mSurfaceAnimatorStarter);
+        mDimmer.dontAnimateExit();
+        mDimmer.resetDimStates();
+        mDimmer.updateDims(mTransaction, new Rect());
+        verify(mSurfaceAnimatorStarter, never()).startAnimation(any(SurfaceAnimator.class), any(
+                SurfaceControl.Transaction.class), any(AnimationAdapter.class), anyBoolean());
+        verify(mTransaction).destroy(dimLayer);
+    }
+
     private SurfaceControl getDimLayer() {
         return mDimmer.mDimState.mDimLayer;
     }
diff --git a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index 64501e4..95361f0 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -17,14 +17,20 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Binder;
+import android.os.IInterface;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.FlakyTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -50,7 +56,7 @@
  * atest FrameworksServicesTests:com.android.server.wm.RemoteAnimationControllerTest
  */
 @SmallTest
-@FlakyTest(detail = "Promote to presubmit if non-flakyness is established")
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class RemoteAnimationControllerTest extends WindowTestsBase {
 
@@ -67,6 +73,7 @@
     public void setUp() throws Exception {
         super.setUp();
         MockitoAnnotations.initMocks(this);
+        when(mMockRunner.asBinder()).thenReturn(new Binder());
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50);
         mAdapter.setCallingPid(123);
         sWm.mH.runWithScissors(() -> {
@@ -166,7 +173,7 @@
     @Test
     public void testZeroAnimations() throws Exception {
         mController.goodToGo();
-        verifyZeroInteractions(mMockRunner);
+        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
     @Test
@@ -175,7 +182,7 @@
         mController.createAnimationAdapter(win.mAppToken,
                 new Point(50, 100), new Rect(50, 100, 150, 150));
         mController.goodToGo();
-        verifyZeroInteractions(mMockRunner);
+        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
     }
 
     @Test
@@ -197,4 +204,21 @@
         assertEquals(1, appsCaptor.getValue().length);
         assertEquals(mMockLeash, appsCaptor.getValue()[0].leash);
     }
+
+    @Test
+    public void testRemovedBeforeStarted() throws Exception {
+        final WindowState win = createWindow(null /* parent */, TYPE_BASE_APPLICATION, "testWin");
+        final AnimationAdapter adapter = mController.createAnimationAdapter(win.mAppToken,
+                new Point(50, 100), new Rect(50, 100, 150, 150));
+        adapter.startAnimation(mMockLeash, mMockTransaction, mFinishedCallback);
+        win.mAppToken.removeImmediately();
+        mController.goodToGo();
+        verifyNoMoreInteractionsExceptAsBinder(mMockRunner);
+        verify(mFinishedCallback).onAnimationFinished(eq(adapter));
+    }
+
+    private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
+        verify(binder, atLeast(0)).asBinder();
+        verifyNoMoreInteractions(binder);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 6d9167f..1af344b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -445,8 +445,7 @@
     }
 
     @Override
-    public int rotationForOrientationLw(int orientation,
-            int lastRotation) {
+    public int rotationForOrientationLw(int orientation, int lastRotation, boolean defaultDisplay) {
         return rotationToReport;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
new file mode 100644
index 0000000..e173b7d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowSurfacePlacerTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
+import static android.view.WindowManager.TRANSIT_TASK_OPEN;
+import static junit.framework.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.WindowManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class WindowSurfacePlacerTest extends WindowTestsBase {
+
+    private WindowSurfacePlacer mWindowSurfacePlacer;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        mWindowSurfacePlacer = new WindowSurfacePlacer(sWm);
+    }
+
+    @Test
+    public void testTranslucentOpen() throws Exception {
+        synchronized (sWm.mWindowMap) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentOpening.setFillsParent(false);
+            translucentOpening.setHidden(true);
+            sWm.mOpeningApps.add(behind);
+            sWm.mOpeningApps.add(translucentOpening);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_OPEN,
+                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_OPEN));
+        }
+    }
+
+    @Test
+    public void testTranslucentClose() throws Exception {
+        synchronized (sWm.mWindowMap) {
+            final AppWindowToken behind = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
+                    WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
+            translucentClosing.setFillsParent(false);
+            sWm.mClosingApps.add(translucentClosing);
+            assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
+                    mWindowSurfacePlacer.maybeUpdateTransitToTranslucentAnim(TRANSIT_TASK_CLOSE));
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 6fa2740..d2eee68 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -173,6 +173,8 @@
 
                 mDisplayContent.removeImmediately();
                 sWm.mInputMethodTarget = null;
+                sWm.mClosingApps.clear();
+                sWm.mOpeningApps.clear();
             }
 
             // Wait until everything is really cleaned up.
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
new file mode 100644
index 0000000..d0f0fe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/InsetUtilsTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.wm.utils;
+
+import static junit.framework.Assert.assertEquals;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class InsetUtilsTest {
+
+    @Test
+    public void testAdd() throws Exception {
+        final Rect rect1 = new Rect(10, 20, 30, 40);
+        final Rect rect2 = new Rect(50, 60, 70, 80);
+        InsetUtils.addInsets(rect1, rect2);
+        assertEquals(new Rect(60, 80, 100, 120), rect1);
+    }
+}
+
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
index 40a34b9..afdde72 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/Android.mk
@@ -17,14 +17,18 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
 
 LOCAL_COMPATIBILITY_SUITE := device-tests
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java
 
 LOCAL_PACKAGE_NAME := SuspendTestApp
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
 include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
index 70a1fd0..ce6a27a 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/AndroidManifest.xml
@@ -21,7 +21,12 @@
         <activity android:name=".SuspendTestActivity"
                   android:exported="true" />
         <receiver android:name=".SuspendTestReceiver"
-                  android:exported="true" />
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MY_PACKAGE_SUSPENDED" />
+                <action android:name="android.intent.action.MY_PACKAGE_UNSUSPENDED" />
+            </intent-filter>
+        </receiver>
     </application>
 
 </manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
index 6f353a0..90a9f01 100644
--- a/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
+++ b/services/tests/servicestests/test-apps/SuspendTestApp/src/com/android/servicestests/apps/suspendtestapp/SuspendTestReceiver.java
@@ -16,12 +16,15 @@
 
 package com.android.servicestests.apps.suspendtestapp;
 
+import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MY_PACKAGE_SUSPENDED;
+import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MY_PACKAGE_UNSUSPENDED;
+import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.Bundle;
-import android.os.PersistableBundle;
 import android.util.Log;
 
 public class SuspendTestReceiver extends BroadcastReceiver {
@@ -34,21 +37,33 @@
     public static final String EXTRA_SUSPENDED_APP_EXTRAS =
             PACKAGE_NAME + ".extra.SUSPENDED_APP_EXTRAS";
 
-    private PackageManager mPm;
-
     @Override
     public void onReceive(Context context, Intent intent) {
-        mPm = context.getPackageManager();
-        Log.d(TAG, "Received request action " + intent.getAction());
+        final PackageManager packageManager = context.getPackageManager();
+        Log.d(TAG, "Received action " + intent.getAction());
+        final Bundle appExtras;
         switch (intent.getAction()) {
             case ACTION_GET_SUSPENDED_STATE:
                 final Bundle result = new Bundle();
-                final boolean suspended = mPm.isPackageSuspended();
-                final PersistableBundle appExtras = mPm.getSuspendedPackageAppExtras();
+                final boolean suspended = packageManager.isPackageSuspended();
+                appExtras = packageManager.getSuspendedPackageAppExtras();
                 result.putBoolean(EXTRA_SUSPENDED, suspended);
-                result.putParcelable(EXTRA_SUSPENDED_APP_EXTRAS, appExtras);
+                result.putBundle(EXTRA_SUSPENDED_APP_EXTRAS, appExtras);
                 setResult(0, null, result);
                 break;
+            case Intent.ACTION_MY_PACKAGE_SUSPENDED:
+                appExtras = intent.getBundleExtra(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS);
+                final Intent reportSuspendIntent = new Intent(ACTION_REPORT_MY_PACKAGE_SUSPENDED)
+                        .putExtra(EXTRA_SUSPENDED_APP_EXTRAS, appExtras)
+                        .setPackage(INSTRUMENTATION_PACKAGE);
+                context.sendBroadcast(reportSuspendIntent);
+                break;
+            case Intent.ACTION_MY_PACKAGE_UNSUSPENDED:
+                final Intent reportUnsuspendIntent =
+                        new Intent(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED)
+                        .setPackage(INSTRUMENTATION_PACKAGE);
+                context.sendBroadcast(reportUnsuspendIntent);
+                break;
             default:
                 Log.e(TAG, "Unknown action: " + intent.getAction());
         }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
index 09d88fd..0815876 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -367,8 +367,8 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
 
         mHelper.setShowBadge(PKG, UID, true);
 
@@ -427,10 +427,10 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, false);
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, false, false);
+        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
 
         mHelper.setShowBadge(PKG, UID, true);
 
@@ -481,7 +481,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
@@ -507,7 +507,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -528,7 +528,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(SOUND_URI, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -569,7 +569,7 @@
         NotificationChannel channel =
                 new NotificationChannel("id", "name", IMPORTANCE_LOW);
         channel.setSound(null, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, true, channel.getId());
 
         loadStreamXml(baos, true);
@@ -593,9 +593,9 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, ncg, true);
         mHelper.createNotificationChannelGroup(PKG, UID, ncg2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel1.getId());
         mHelper.deleteNotificationChannelGroup(PKG, UID, ncg.getId());
@@ -701,7 +701,7 @@
     @Test
     public void testDeletesDefaultChannelAfterChannelIsCreated() throws Exception {
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
 
@@ -721,7 +721,7 @@
         ByteArrayOutputStream baos = writeXmlAndPurge(PKG, UID, false,
                 NotificationChannel.DEFAULT_CHANNEL_ID, "bananas");
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
 
         loadStreamXml(baos, false);
 
@@ -734,36 +734,39 @@
         mHelper.setImportance(PKG, UID, IMPORTANCE_NONE);
 
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_LOW), true, false);
     }
 
     @Test
     public void testCreateChannel_badImportance() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE - 1),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_UNSPECIFIED),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         try {
             mHelper.createNotificationChannel(PKG, UID,
-                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1), true);
+                    new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX + 1),
+                    true, false);
             fail("Was allowed to create a channel with invalid importance");
         } catch (IllegalArgumentException e) {
             // yay
         }
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_NONE), true, false);
         mHelper.createNotificationChannel(PKG, UID,
-                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true);
+                new NotificationChannel("bananas", "bananas", IMPORTANCE_MAX), true, false);
     }
 
 
@@ -777,7 +780,7 @@
         channel.setBypassDnd(true);
         channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
+        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
 
         // same id, try to update all fields
         final NotificationChannel channel2 =
@@ -824,7 +827,7 @@
     public void testUpdate_postUpgrade_noUpdateAppFields() throws Exception {
         final NotificationChannel channel = new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, false);
+        mHelper.createNotificationChannel(PKG, UID, channel, false, false);
         assertTrue(mHelper.canShowBadge(PKG, UID));
         assertEquals(Notification.PRIORITY_DEFAULT, mHelper.getPackagePriority(PKG, UID));
         assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,
@@ -865,7 +868,7 @@
         }
         channel.lockFields(lockMask);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel savedChannel =
                 mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -894,7 +897,7 @@
         }
         channel.lockFields(lockMask);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel savedChannel =
                 mHelper.getNotificationChannel(PKG, UID, channel.getId(), false);
@@ -920,7 +923,7 @@
 
     @Test
     public void testLockFields_soundAndVibration() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setSound(new Uri.Builder().scheme("test").build(),
@@ -944,7 +947,7 @@
 
     @Test
     public void testLockFields_vibrationAndLights() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setVibrationPattern(new long[]{7945, 46 ,246});
@@ -964,7 +967,7 @@
 
     @Test
     public void testLockFields_lightsAndImportance() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
 
         final NotificationChannel update1 = getChannel();
         update1.setLightColor(Color.GREEN);
@@ -984,7 +987,7 @@
 
     @Test
     public void testLockFields_visibilityAndDndAndBadge() throws Exception {
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
         assertEquals(0,
                 mHelper.getNotificationChannel(PKG, UID, getChannel().getId(), false)
                         .getUserLockedFields());
@@ -1029,7 +1032,7 @@
         channel.enableVibration(true);
         channel.setVibrationPattern(new long[]{100, 67, 145, 156});
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
         // Does not return deleted channel
@@ -1058,8 +1061,8 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
         channelMap.put(channel2.getId(), channel2);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
@@ -1091,9 +1094,9 @@
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
         NotificationChannel channel3 =
                 new NotificationChannel("id4", "a", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
         mHelper.deleteNotificationChannel(PKG, UID, channel3.getId());
@@ -1109,14 +1112,14 @@
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel.setVibrationPattern(vibration);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, channel.getId());
 
         NotificationChannel newChannel = new NotificationChannel(
                 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
         newChannel.setVibrationPattern(new long[]{100});
 
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
 
         // No long deleted, using old settings
         compareChannels(channel,
@@ -1128,7 +1131,7 @@
         assertTrue(mHelper.onlyHasDefaultChannel(PKG, UID));
         assertFalse(mHelper.onlyHasDefaultChannel(UPDATED_PKG, UID2));
 
-        mHelper.createNotificationChannel(PKG, UID, getChannel(), true);
+        mHelper.createNotificationChannel(PKG, UID, getChannel(), true, false);
         assertFalse(mHelper.onlyHasDefaultChannel(PKG, UID));
     }
 
@@ -1136,7 +1139,7 @@
     public void testCreateChannel_defaultChannelId() throws Exception {
         try {
             mHelper.createNotificationChannel(PKG, UID, new NotificationChannel(
-                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true);
+                    NotificationChannel.DEFAULT_CHANNEL_ID, "ha", IMPORTANCE_HIGH), true, false);
             fail("Allowed to create default channel");
         } catch (IllegalArgumentException e) {
             // pass
@@ -1150,13 +1153,13 @@
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
         channel.setVibrationPattern(vibration);
 
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
 
         NotificationChannel newChannel = new NotificationChannel(
                 channel.getId(), channel.getName(), NotificationManager.IMPORTANCE_HIGH);
         newChannel.setVibrationPattern(new long[]{100});
 
-        mHelper.createNotificationChannel(PKG, UID, newChannel, true);
+        mHelper.createNotificationChannel(PKG, UID, newChannel, true, false);
 
         // Old settings not overridden
         compareChannels(channel,
@@ -1169,7 +1172,7 @@
         final NotificationChannel channel = new NotificationChannel("id2", "name2",
                  NotificationManager.IMPORTANCE_DEFAULT);
         channel.setSound(sound, mAudioAttributes);
-        mHelper.createNotificationChannel(PKG, UID, channel, true);
+        mHelper.createNotificationChannel(PKG, UID, channel, true, false);
         assertEquals(sound, mHelper.getNotificationChannel(
                 PKG, UID, channel.getId(), false).getSound());
     }
@@ -1181,8 +1184,8 @@
         NotificationChannel channel2 =
                 new NotificationChannel("id2", "name2", IMPORTANCE_LOW);
 
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
-        mHelper.createNotificationChannel(PKG, UID, channel2, false);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
+        mHelper.createNotificationChannel(PKG, UID, channel2, false, false);
 
         mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
 
@@ -1205,9 +1208,9 @@
 
         mHelper.createNotificationChannelGroup(PKG, UID, notDeleted, true);
         mHelper.createNotificationChannelGroup(PKG, UID, deleted, true);
-        mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true);
-        mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true);
-        mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true);
+        mHelper.createNotificationChannel(PKG, UID, nonGroupedNonDeletedChannel, true, false);
+        mHelper.createNotificationChannel(PKG, UID, groupedAndDeleted, true, false);
+        mHelper.createNotificationChannel(PKG, UID, groupedButNotDeleted, true, false);
 
         mHelper.deleteNotificationChannelGroup(PKG, UID, deleted.getId());
 
@@ -1264,14 +1267,14 @@
         // Deleted
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
 
         assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
 
         // Not deleted
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
         assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
@@ -1302,7 +1305,7 @@
     @Test
     public void testOnPackageChange_downgradeTargetSdk() throws Exception {
         // create channel as api 26
-        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true);
+        mHelper.createNotificationChannel(UPDATED_PKG, UID2, getChannel(), true, false);
 
         // install new app version targeting 25
         final ApplicationInfo legacy = new ApplicationInfo();
@@ -1338,7 +1341,7 @@
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup("garbage");
         try {
-            mHelper.createNotificationChannel(PKG, UID, channel1, true);
+            mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
             fail("Created a channel with a bad group");
         } catch (IllegalArgumentException e) {
         }
@@ -1351,7 +1354,7 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
 
         assertEquals(ncg.getId(),
                 mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false).getGroup());
@@ -1369,20 +1372,20 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
         NotificationChannel channel1a =
                 new NotificationChannel("id1a", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1a.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1a, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1a, true, false);
 
         NotificationChannel channel2 =
                 new NotificationChannel("id2", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel2.setGroup(ncg2.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel2, true);
+        mHelper.createNotificationChannel(PKG, UID, channel2, true, false);
 
         NotificationChannel channel3 =
                 new NotificationChannel("id3", "name1", NotificationManager.IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, channel3, true);
+        mHelper.createNotificationChannel(PKG, UID, channel3, true, false);
 
         List<NotificationChannelGroup> actual =
                 mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
@@ -1416,7 +1419,7 @@
         NotificationChannel channel1 =
                 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
         channel1.setGroup(ncg.getId());
-        mHelper.createNotificationChannel(PKG, UID, channel1, true);
+        mHelper.createNotificationChannel(PKG, UID, channel1, true, false);
         mHelper.getNotificationChannelGroups(PKG, UID, true, true).getList();
 
         channel1.setImportance(IMPORTANCE_LOW);
@@ -1436,12 +1439,12 @@
     @Test
     public void testCreateChannel_updateName() throws Exception {
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
         NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("hello", actual.getName());
 
         nc = new NotificationChannel("id", "goodbye", IMPORTANCE_HIGH);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
 
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertEquals("goodbye", actual.getName());
@@ -1455,13 +1458,13 @@
         NotificationChannelGroup group = new NotificationChannelGroup("group", "");
         mHelper.createNotificationChannelGroup(PKG, UID, group, true);
         NotificationChannel nc = new NotificationChannel("id", "hello", IMPORTANCE_DEFAULT);
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
         NotificationChannel actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertNull(actual.getGroup());
 
         nc = new NotificationChannel("id", "hello", IMPORTANCE_HIGH);
         nc.setGroup(group.getId());
-        mHelper.createNotificationChannel(PKG, UID, nc, true);
+        mHelper.createNotificationChannel(PKG, UID, nc, true, false);
 
         actual = mHelper.getNotificationChannel(PKG, UID, "id", false);
         assertNotNull(actual.getGroup());
@@ -1486,7 +1489,7 @@
             int numChannels = ThreadLocalRandom.current().nextInt(1, 10);
             for (int j = 0; j < numChannels; j++) {
                 mHelper.createNotificationChannel(pkgName, UID,
-                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true);
+                        new NotificationChannel("" + j, "a", IMPORTANCE_HIGH), true, false);
             }
             expectedChannels.put(pkgName, numChannels);
         }
@@ -1621,10 +1624,10 @@
         c.setGroup(group.getId());
         NotificationChannel d = new NotificationChannel("d", "d", IMPORTANCE_DEFAULT);
 
-        mHelper.createNotificationChannel(PKG, UID, a, true);
-        mHelper.createNotificationChannel(PKG, UID, b, true);
-        mHelper.createNotificationChannel(PKG, UID, c, true);
-        mHelper.createNotificationChannel(PKG, UID, d, true);
+        mHelper.createNotificationChannel(PKG, UID, a, true, false);
+        mHelper.createNotificationChannel(PKG, UID, b, true, false);
+        mHelper.createNotificationChannel(PKG, UID, c, true, false);
+        mHelper.createNotificationChannel(PKG, UID, d, true, false);
         mHelper.deleteNotificationChannel(PKG, UID, c.getId());
 
         NotificationChannelGroup retrieved = mHelper.getNotificationChannelGroupWithChannels(
@@ -1641,22 +1644,31 @@
 
     @Test
     public void testAndroidPkgCanBypassDnd_creation() {
-
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         test.setBypassDnd(true);
 
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
 
         assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                 .canBypassDnd());
     }
 
     @Test
+    public void testDndPkgCanBypassDnd_creation() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        test.setBypassDnd(true);
+
+        mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+    }
+
+    @Test
     public void testNormalPkgCannotBypassDnd_creation() {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         test.setBypassDnd(true);
 
-        mHelper.createNotificationChannel(PKG, 1000, test, true);
+        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
 
         assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
     }
@@ -1664,11 +1676,11 @@
     @Test
     public void testAndroidPkgCanBypassDnd_update() throws Exception {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, test, true, false);
 
         NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         update.setBypassDnd(true);
-        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true);
+        mHelper.createNotificationChannel(SYSTEM_PKG, SYSTEM_UID, update, true, false);
 
         assertTrue(mHelper.getNotificationChannel(SYSTEM_PKG, SYSTEM_UID, "A", false)
                 .canBypassDnd());
@@ -1678,12 +1690,24 @@
     }
 
     @Test
-    public void testNormalPkgCannotBypassDnd_update() {
+    public void testDndPkgCanBypassDnd_update() throws Exception {
         NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
-        mHelper.createNotificationChannel(PKG, 1000, test, true);
+        mHelper.createNotificationChannel(PKG, UID, test, true, true);
+
         NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
         update.setBypassDnd(true);
-        mHelper.createNotificationChannel(PKG, 1000, update, true);
+        mHelper.createNotificationChannel(PKG, UID, update, true, true);
+
+        assertTrue(mHelper.getNotificationChannel(PKG, UID, "A", false).canBypassDnd());
+    }
+
+    @Test
+    public void testNormalPkgCannotBypassDnd_update() {
+        NotificationChannel test = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        mHelper.createNotificationChannel(PKG, 1000, test, true, false);
+        NotificationChannel update = new NotificationChannel("A", "a", IMPORTANCE_LOW);
+        update.setBypassDnd(true);
+        mHelper.createNotificationChannel(PKG, 1000, update, true, false);
         assertFalse(mHelper.getNotificationChannel(PKG, 1000, "A", false).canBypassDnd());
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
index 36136a8..ce74457 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java
@@ -206,6 +206,31 @@
     }
 
     @Test
+    public void testShouldExitForAlarm_oldAlarm() {
+        // Cal: today 2:15pm
+        Calendar cal = new GregorianCalendar();
+        cal.set(Calendar.HOUR_OF_DAY, 14);
+        cal.set(Calendar.MINUTE, 15);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+
+        // ScheduleInfo: today 12:16pm  - today 3:15pm
+        mScheduleInfo.days = new int[] {getTodayDay()};
+        mScheduleInfo.startHour = 12;
+        mScheduleInfo.endHour = 3;
+        mScheduleInfo.startMinute = 16;
+        mScheduleInfo.endMinute = 15;
+        mScheduleInfo.exitAtAlarm = true;
+        mScheduleInfo.nextAlarm = 1000; // very old alarm
+
+        mScheduleCalendar.setSchedule(mScheduleInfo);
+        assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis()));
+
+        // don't exit for an alarm if it's an old alarm
+        assertFalse(mScheduleCalendar.shouldExitForAlarm(1000));
+    }
+
+    @Test
     public void testMaybeSetNextAlarm_settingOff() {
         mScheduleInfo.exitAtAlarm = false;
         mScheduleInfo.nextAlarm = 0;
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 381e04c..3acc277 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -329,12 +329,12 @@
         mZenModeHelperSpy.mConfig.allowEvents = false;
         mZenModeHelperSpy.mConfig.allowRepeatCallers= false;
 
-        // 2. apply priority only zen - verify ringer is set to silent
+        // 2. apply priority only zen - verify ringer is unchanged
         mZenModeHelperSpy.applyZenToRingerMode();
-        verify(mAudioManager, atLeastOnce()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
+        verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT,
                 mZenModeHelperSpy.TAG);
 
-        // 3. apply zen off - verify zen is set to prevoius ringer (normal)
+        // 3. apply zen off - verify zen is set to previous ringer (normal)
         when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
         mZenModeHelperSpy.mZenMode = Global.ZEN_MODE_OFF;
         mZenModeHelperSpy.applyZenToRingerMode();
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index 1073a80..5e2a364 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -15,8 +15,10 @@
 package com.android.server.slice;
 
 import static android.content.ContentProvider.maybeAddUserId;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -35,9 +37,11 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.IBinder;
+import android.os.Process;
 import android.os.RemoteException;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -63,6 +67,7 @@
     private SliceManagerService mService;
     private PinnedSliceState mCreatedSliceState;
     private IBinder mToken = new Binder();
+    private TestableContext mContextSpy;
 
     @Before
     public void setup() {
@@ -72,7 +77,8 @@
         mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
         mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED);
 
-        mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper()));
+        mContextSpy = spy(mContext);
+        mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper()));
         mCreatedSliceState = mock(PinnedSliceState.class);
         doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI), anyString());
     }
@@ -103,4 +109,23 @@
         verify(mCreatedSliceState, never()).destroy();
     }
 
+    @Test
+    public void testCheckAutoGrantPermissions() throws RemoteException {
+        String[] testPerms = new String[] {
+                "perm1",
+                "perm2",
+        };
+        when(mContextSpy.checkUriPermission(any(), anyInt(), anyInt(), anyInt()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm1", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_DENIED);
+        when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid()))
+                .thenReturn(PERMISSION_GRANTED);
+        mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(),
+                Process.myUid(), testPerms);
+
+        verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid()));
+        verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
+    }
+
 }
\ No newline at end of file
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index fd28b65..271f813 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -70,6 +70,8 @@
     private SparseArray<ArrayMap<String,AppUsageHistory>> mIdleHistory = new SparseArray<>();
     private static final long ONE_MINUTE = 60 * 1000;
 
+    private static final int STANDBY_BUCKET_UNKNOWN = -1;
+
     @VisibleForTesting
     static final String APP_IDLE_FILENAME = "app_idle_stats.xml";
     private static final String TAG_PACKAGES = "packages";
@@ -111,6 +113,9 @@
         long lastUsedScreenTime;
         // Last predicted time using elapsed timebase
         long lastPredictedTime;
+        // Last predicted bucket
+        @UsageStatsManager.StandbyBuckets
+        int lastPredictedBucket = STANDBY_BUCKET_UNKNOWN;
         // Standby bucket
         @UsageStatsManager.StandbyBuckets
         int currentBucket;
@@ -342,6 +347,7 @@
         appUsageHistory.bucketingReason = reason;
         if ((reason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED) {
             appUsageHistory.lastPredictedTime = getElapsedTime(elapsedRealtime);
+            appUsageHistory.lastPredictedBucket = bucket;
         }
         if (DEBUG) {
             Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
@@ -350,6 +356,17 @@
     }
 
     /**
+     * Update the prediction for the app but don't change the actual bucket
+     * @param app The app for which the prediction was made
+     * @param elapsedTimeAdjusted The elapsed time in the elapsed duration timebase
+     * @param bucket The predicted bucket
+     */
+    public void updateLastPrediction(AppUsageHistory app, long elapsedTimeAdjusted, int bucket) {
+        app.lastPredictedTime = elapsedTimeAdjusted;
+        app.lastPredictedBucket = bucket;
+    }
+
+    /**
      * Marks the last time a job was run, with the given elapsedRealtime. The time stored is
      * based on the elapsed timebase.
      * @param packageName
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 9139a4c..5f01518 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -22,7 +22,9 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC_START;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
@@ -30,13 +32,14 @@
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
-
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
@@ -184,6 +187,7 @@
     static final int MSG_ONE_TIME_CHECK_IDLE_STATES = 10;
     /** Check the state of one app: arg1 = userId, arg2 = uid, obj = (String) packageName */
     static final int MSG_CHECK_PACKAGE_IDLE_STATE = 11;
+    static final int MSG_REPORT_EXEMPTED_SYNC_START = 12;
 
     long mCheckIdleIntervalMillis;
     long mAppIdleParoleIntervalMillis;
@@ -200,6 +204,8 @@
     long mPredictionTimeoutMillis;
     /** Maximum time a sync adapter associated with a CP should keep the buckets elevated. */
     long mSyncAdapterTimeoutMillis;
+    /** Maximum time an exempted sync should keep the buckets elevated. */
+    long mExemptedSyncAdapterTimeoutMillis;
     /** Maximum time a system interaction should keep the buckets elevated. */
     long mSystemInteractionTimeoutMillis;
 
@@ -373,6 +379,21 @@
         }
     }
 
+    void reportExemptedSyncStart(String packageName, int userId) {
+        if (!mAppIdleEnabled) return;
+
+        final long elapsedRealtime = mInjector.elapsedRealtime();
+
+        synchronized (mAppIdleLock) {
+            AppUsageHistory appUsage = mAppIdleHistory.reportUsage(packageName, userId,
+                    STANDBY_BUCKET_ACTIVE, REASON_SUB_USAGE_EXEMPTED_SYNC_START,
+                    0,
+                    elapsedRealtime + mExemptedSyncAdapterTimeoutMillis);
+            maybeInformListeners(packageName, userId, elapsedRealtime,
+                    appUsage.currentBucket, appUsage.bucketingReason, false);
+        }
+    }
+
     void setChargingState(boolean charging) {
         synchronized (mAppIdleLock) {
             if (mCharging != charging) {
@@ -538,19 +559,30 @@
                 }
                 final int oldBucket = app.currentBucket;
                 int newBucket = Math.max(oldBucket, STANDBY_BUCKET_ACTIVE); // Undo EXEMPTED
-                boolean predictionLate = false;
+                boolean predictionLate = predictionTimedOut(app, elapsedRealtime);
                 // Compute age-based bucket
                 if (oldMainReason == REASON_MAIN_DEFAULT
                         || oldMainReason == REASON_MAIN_USAGE
                         || oldMainReason == REASON_MAIN_TIMEOUT
-                        || (predictionLate = predictionTimedOut(app, elapsedRealtime))) {
-                    newBucket = getBucketForLocked(packageName, userId,
-                            elapsedRealtime);
-                    if (DEBUG) {
-                        Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+                        || predictionLate) {
+
+                    if (!predictionLate && app.lastPredictedBucket >= STANDBY_BUCKET_ACTIVE
+                            && app.lastPredictedBucket <= STANDBY_BUCKET_RARE) {
+                        newBucket = app.lastPredictedBucket;
+                        reason = REASON_MAIN_PREDICTED | REASON_SUB_PREDICTED_RESTORED;
+                        if (DEBUG) {
+                            Slog.d(TAG, "Restored predicted newBucket = " + newBucket);
+                        }
+                    } else {
+                        newBucket = getBucketForLocked(packageName, userId,
+                                elapsedRealtime);
+                        if (DEBUG) {
+                            Slog.d(TAG, "Evaluated AOSP newBucket = " + newBucket);
+                        }
+                        reason = REASON_MAIN_TIMEOUT;
                     }
-                    reason = REASON_MAIN_TIMEOUT;
                 }
+
                 // Check if the app is within one of the timeouts for forced bucket elevation
                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
                 if (newBucket >= STANDBY_BUCKET_ACTIVE
@@ -587,8 +619,7 @@
 
     /** Returns true if there hasn't been a prediction for the app in a while. */
     private boolean predictionTimedOut(AppIdleHistory.AppUsageHistory app, long elapsedRealtime) {
-        return (app.bucketingReason & REASON_MAIN_MASK) == REASON_MAIN_PREDICTED
-                && app.lastPredictedTime > 0
+        return app.lastPredictedTime > 0
                 && mAppIdleHistory.getElapsedTime(elapsedRealtime)
                     - app.lastPredictedTime > mPredictionTimeoutMillis;
     }
@@ -747,6 +778,8 @@
             case UsageEvents.Event.SYSTEM_INTERACTION: return REASON_SUB_USAGE_SYSTEM_INTERACTION;
             case UsageEvents.Event.USER_INTERACTION: return REASON_SUB_USAGE_USER_INTERACTION;
             case UsageEvents.Event.NOTIFICATION_SEEN: return REASON_SUB_USAGE_NOTIFICATION_SEEN;
+            case UsageEvents.Event.SLICE_PINNED: return REASON_SUB_USAGE_SLICE_PINNED;
+            case UsageEvents.Event.SLICE_PINNED_PRIV: return REASON_SUB_USAGE_SLICE_PINNED_PRIV;
             default: return 0;
         }
     }
@@ -1032,6 +1065,10 @@
             if (predicted) {
                 // Check if the app is within one of the timeouts for forced bucket elevation
                 final long elapsedTimeAdjusted = mAppIdleHistory.getElapsedTime(elapsedRealtime);
+                // In case of not using the prediction, just keep track of it for applying after
+                // ACTIVE or WORKING_SET timeout.
+                mAppIdleHistory.updateLastPrediction(app, elapsedTimeAdjusted, newBucket);
+
                 if (newBucket > STANDBY_BUCKET_ACTIVE
                         && app.bucketActiveTimeoutTime > elapsedTimeAdjusted) {
                     newBucket = STANDBY_BUCKET_ACTIVE;
@@ -1256,6 +1293,11 @@
                 .sendToTarget();
     }
 
+    void postReportExemptedSyncStart(String packageName, int userId) {
+        mHandler.obtainMessage(MSG_REPORT_EXEMPTED_SYNC_START, userId, 0, packageName)
+                .sendToTarget();
+    }
+
     void dumpUser(IndentingPrintWriter idpw, int userId, String pkg) {
         synchronized (mAppIdleLock) {
             mAppIdleHistory.dump(idpw, userId, pkg);
@@ -1470,6 +1512,11 @@
                     checkAndUpdateStandbyState((String) msg.obj, msg.arg1, msg.arg2,
                             mInjector.elapsedRealtime());
                     break;
+
+                case MSG_REPORT_EXEMPTED_SYNC_START:
+                    reportExemptedSyncStart((String) msg.obj, msg.arg1);
+                    break;
+
                 default:
                     super.handleMessage(msg);
                     break;
@@ -1532,6 +1579,7 @@
                 "system_update_usage_duration";
         private static final String KEY_PREDICTION_TIMEOUT = "prediction_timeout";
         private static final String KEY_SYNC_ADAPTER_HOLD_DURATION = "sync_adapter_duration";
+        private static final String KEY_EXEMPTED_SYNC_HOLD_DURATION = "exempted_sync_duration";
         private static final String KEY_SYSTEM_INTERACTION_HOLD_DURATION =
                 "system_interaction_duration";
         public static final long DEFAULT_STRONG_USAGE_TIMEOUT = 1 * ONE_HOUR;
@@ -1539,6 +1587,7 @@
         public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT = 2 * ONE_HOUR;
         public static final long DEFAULT_SYSTEM_INTERACTION_TIMEOUT = 10 * ONE_MINUTE;
         public static final long DEFAULT_SYNC_ADAPTER_TIMEOUT = 10 * ONE_MINUTE;
+        public static final long DEFAULT_EXEMPTED_SYNC_TIMEOUT = 10 * ONE_MINUTE;
 
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -1614,6 +1663,9 @@
                 mSyncAdapterTimeoutMillis = mParser.getDurationMillis
                         (KEY_SYNC_ADAPTER_HOLD_DURATION,
                                 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYNC_ADAPTER_TIMEOUT);
+                mExemptedSyncAdapterTimeoutMillis = mParser.getDurationMillis
+                        (KEY_EXEMPTED_SYNC_HOLD_DURATION,
+                                COMPRESS_TIME ? ONE_MINUTE : DEFAULT_EXEMPTED_SYNC_TIMEOUT);
                 mSystemInteractionTimeoutMillis = mParser.getDurationMillis
                         (KEY_SYSTEM_INTERACTION_HOLD_DURATION,
                                 COMPRESS_TIME ? ONE_MINUTE : DEFAULT_SYSTEM_INTERACTION_TIMEOUT);
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 00826e0..b070e03 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -16,6 +16,7 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -23,10 +24,18 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
+import java.util.List;
+
 class IntervalStats {
     public long beginTime;
     public long endTime;
     public long lastTimeSaved;
+    public long lastInteractiveTime;
+    public long lastNonInteractiveTime;
+    public long interactiveDuration;
+    public int interactiveCount;
+    public long nonInteractiveDuration;
+    public int nonInteractiveCount;
     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     public Configuration activeConfiguration;
@@ -171,6 +180,60 @@
         usageStats.mAppLaunchCount += 1;
     }
 
+    private void commitInteractiveTime(long timeStamp) {
+        if (lastInteractiveTime != 0) {
+            interactiveDuration += timeStamp - lastInteractiveTime;
+            lastInteractiveTime = 0;
+        }
+        if (lastNonInteractiveTime != 0) {
+            nonInteractiveDuration += timeStamp - lastNonInteractiveTime;
+            lastNonInteractiveTime = 0;
+        }
+    }
+
+    void commitTime(long timeStamp) {
+        commitInteractiveTime(timeStamp);
+    }
+
+    void updateScreenInteractive(long timeStamp) {
+        if (lastInteractiveTime != 0) {
+            // Already interactive, just keep running.
+            return;
+        }
+        commitInteractiveTime(timeStamp);
+        lastInteractiveTime = timeStamp;
+        interactiveCount++;
+    }
+
+    void updateScreenNonInteractive(long timeStamp) {
+        if (lastNonInteractiveTime != 0) {
+            // Already non-interactive, just keep running.
+            return;
+        }
+        commitInteractiveTime(timeStamp);
+        lastNonInteractiveTime = timeStamp;
+        nonInteractiveCount++;
+    }
+
+    private void addOneEventStats(List<EventStats> out, int event, int count, long duration) {
+        if (count != 0 || duration != 0) {
+            EventStats ev = new EventStats();
+            ev.mEventType = event;
+            ev.mCount = count;
+            ev.mTotalTime = duration;
+            ev.mBeginTimeStamp = beginTime;
+            ev.mEndTimeStamp = endTime;
+            out.add(ev);
+        }
+    }
+
+    void addEventStatsTo(List<EventStats> out) {
+        addOneEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE, interactiveCount,
+                interactiveDuration);
+        addOneEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE, nonInteractiveCount,
+                nonInteractiveDuration);
+    }
+
     private String getCachedStringRef(String str) {
         final int index = mStringCache.indexOf(str);
         if (index < 0) {
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index b8317b4..1fbc27b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -25,6 +25,7 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.AppStandbyInfo;
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManager;
@@ -486,6 +487,23 @@
     /**
      * Called by the Binder stub.
      */
+    List<EventStats> queryEventStats(int userId, int bucketType, long beginTime,
+            long endTime) {
+        synchronized (mLock) {
+            final long timeNow = checkAndGetTimeLocked();
+            if (!validRange(timeNow, beginTime, endTime)) {
+                return null;
+            }
+
+            final UserUsageStatsService service =
+                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            return service.queryEventStats(bucketType, beginTime, endTime);
+        }
+    }
+
+    /**
+     * Called by the Binder stub.
+     */
     UsageEvents queryEvents(int userId, long beginTime, long endTime,
             boolean shouldObfuscateInstantApps) {
         synchronized (mLock) {
@@ -713,6 +731,28 @@
         }
 
         @Override
+        public ParceledListSlice<EventStats> queryEventStats(int bucketType,
+                long beginTime, long endTime, String callingPackage) throws RemoteException {
+            if (!hasPermission(callingPackage)) {
+                return null;
+            }
+
+            final int userId = UserHandle.getCallingUserId();
+            final long token = Binder.clearCallingIdentity();
+            try {
+                final List<EventStats> results =
+                        UsageStatsService.this.queryEventStats(userId, bucketType,
+                                beginTime, endTime);
+                if (results != null) {
+                    return new ParceledListSlice<>(results);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+            return null;
+        }
+
+        @Override
         public UsageEvents queryEvents(long beginTime, long endTime, String callingPackage) {
             if (!hasPermission(callingPackage)) {
                 return null;
@@ -1239,5 +1279,10 @@
         public void onAdminDataAvailable() {
             mAppStandby.onAdminDataAvailable();
         }
+
+        @Override
+        public void reportExemptedSyncStart(String packageName, int userId) {
+            mAppStandby.postReportExemptedSyncStart(packageName, userId);
+        }
     }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index bcfc421..2287b27 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -27,6 +27,7 @@
 import android.app.usage.UsageStats;
 import android.content.res.Configuration;
 import android.util.ArrayMap;
+import android.util.Pair;
 
 import java.io.IOException;
 import java.net.ProtocolException;
@@ -37,6 +38,9 @@
 final class UsageStatsXmlV1 {
     private static final String TAG = "UsageStatsXmlV1";
 
+    private static final String INTERACTIVE_TAG = "interactive";
+    private static final String NON_INTERACTIVE_TAG = "non-interactive";
+
     private static final String PACKAGES_TAG = "packages";
     private static final String PACKAGE_TAG = "package";
 
@@ -99,6 +103,14 @@
         }
     }
 
+    private static Pair<Integer, Long> loadCountAndTime(XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        int count = XmlUtils.readIntAttribute(parser, COUNT_ATTR, 0);
+        long time = XmlUtils.readLongAttribute(parser, TIME_ATTR, 0);
+        XmlUtils.skipCurrentTag(parser);
+        return new Pair<>(count, time);
+    }
+
     private static void loadChooserCounts(
             XmlPullParser parser, UsageStats usageStats, String action)
             throws XmlPullParserException, IOException {
@@ -202,6 +214,14 @@
         xml.endTag(null, PACKAGE_TAG);
     }
 
+    private static void writeCountAndTime(XmlSerializer xml, String tag, int count, long time)
+            throws IOException {
+        xml.startTag(null, tag);
+        XmlUtils.writeIntAttribute(xml, COUNT_ATTR, count);
+        XmlUtils.writeLongAttribute(xml, TIME_ATTR, time);
+        xml.endTag(null, tag);
+    }
+
     private static void writeChooserCounts(XmlSerializer xml, final UsageStats usageStats)
             throws IOException {
         if (usageStats == null || usageStats.mChooserCounts == null ||
@@ -320,6 +340,18 @@
 
             final String tag = parser.getName();
             switch (tag) {
+                case INTERACTIVE_TAG: {
+                    Pair<Integer, Long> result = loadCountAndTime(parser);
+                    statsOut.interactiveCount = result.first;
+                    statsOut.interactiveDuration = result.second;
+                } break;
+
+                case NON_INTERACTIVE_TAG: {
+                    Pair<Integer, Long> result = loadCountAndTime(parser);
+                    statsOut.nonInteractiveCount = result.first;
+                    statsOut.nonInteractiveDuration = result.second;
+                } break;
+
                 case PACKAGE_TAG:
                     loadUsageStats(parser, statsOut);
                     break;
@@ -346,6 +378,11 @@
     public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
         XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
 
+        writeCountAndTime(xml, INTERACTIVE_TAG, stats.interactiveCount, stats.interactiveDuration);
+
+        writeCountAndTime(xml, NON_INTERACTIVE_TAG, stats.nonInteractiveCount,
+                stats.nonInteractiveDuration);
+
         xml.startTag(null, PACKAGES_TAG);
         final int statsCount = stats.packageStats.size();
         for (int i = 0; i < statsCount; i++) {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index afc3d59..6ad374b 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -17,6 +17,7 @@
 package com.android.server.usage;
 
 import android.app.usage.ConfigurationStats;
+import android.app.usage.EventStats;
 import android.app.usage.TimeSparseArray;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStats;
@@ -191,21 +192,31 @@
         }
 
         for (IntervalStats stats : mCurrentStats) {
-            if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
-                stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
-            } else if (event.mEventType == UsageEvents.Event.CHOOSER_ACTION) {
-                stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
-                String[] annotations = event.mContentAnnotations;
-                if (annotations != null) {
-                    for (String annotation : annotations) {
-                        stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+            switch (event.mEventType) {
+                case UsageEvents.Event.CONFIGURATION_CHANGE: {
+                    stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
+                } break;
+                case UsageEvents.Event.CHOOSER_ACTION: {
+                    stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
+                    String[] annotations = event.mContentAnnotations;
+                    if (annotations != null) {
+                        for (String annotation : annotations) {
+                            stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
+                        }
                     }
-                }
-            } else {
-                stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
-                if (incrementAppLaunch) {
-                    stats.incrementAppLaunchCount(event.mPackage);
-                }
+                } break;
+                case UsageEvents.Event.SCREEN_INTERACTIVE: {
+                    stats.updateScreenInteractive(event.mTimeStamp);
+                } break;
+                case UsageEvents.Event.SCREEN_NON_INTERACTIVE: {
+                    stats.updateScreenNonInteractive(event.mTimeStamp);
+                } break;
+                default: {
+                    stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
+                    if (incrementAppLaunch) {
+                        stats.incrementAppLaunchCount(event.mPackage);
+                    }
+                } break;
             }
         }
 
@@ -246,6 +257,15 @@
                 }
             };
 
+    private static final StatCombiner<EventStats> sEventStatsCombiner =
+            new StatCombiner<EventStats>() {
+                @Override
+                public void combine(IntervalStats stats, boolean mutable,
+                        List<EventStats> accResult) {
+                    stats.addEventStatsTo(accResult);
+                }
+            };
+
     /**
      * Generic query method that selects the appropriate IntervalStats for the specified time range
      * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
@@ -325,6 +345,10 @@
         return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
     }
 
+    List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
+        return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
+    }
+
     UsageEvents queryEvents(final long beginTime, final long endTime,
             boolean obfuscateInstantApps) {
         final ArraySet<String> names = new ArraySet<>();
@@ -448,6 +472,7 @@
             }
 
             stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
+            stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
         }
 
         persistActiveStats();
@@ -640,6 +665,18 @@
         }
     }
 
+    void printEventAggregation(IndentingPrintWriter pw, String label, int count, long duration,
+            boolean prettyDates) {
+        if (count != 0 || duration != 0) {
+            pw.print(label);
+            pw.print(": ");
+            pw.print(count);
+            pw.print("x for ");
+            pw.print(formatElapsedTime(duration, prettyDates));
+            pw.println();
+        }
+    }
+
     void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
             boolean prettyDates, boolean skipEvents, String pkg) {
         if (prettyDates) {
@@ -713,6 +750,13 @@
                 pw.println();
             }
             pw.decreaseIndent();
+            pw.println("event aggregations");
+            pw.increaseIndent();
+            printEventAggregation(pw, "screen-interactive", stats.interactiveCount,
+                    stats.interactiveDuration, prettyDates);
+            printEventAggregation(pw, "screen-non-interactive", stats.nonInteractiveCount,
+                    stats.nonInteractiveDuration, prettyDates);
+            pw.decreaseIndent();
         }
 
         // The last 24 hours of events is already printed in the non checkin dump
@@ -781,6 +825,10 @@
                 return "SLICE_PINNED";
             case UsageEvents.Event.SLICE_PINNED_PRIV:
                 return "SLICE_PINNED_PRIV";
+            case UsageEvents.Event.SCREEN_INTERACTIVE:
+                return "SCREEN_INTERACTIVE";
+            case UsageEvents.Event.SCREEN_NON_INTERACTIVE:
+                return "SCREEN_NON_INTERACTIVE";
             default:
                 return "UNKNOWN";
         }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c79eec0..4683161 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -397,6 +397,15 @@
     public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
 
     /**
+     * Where there is no preloaded voicemail number on a SIM card, specifies the carrier's default
+     * voicemail number for roaming network.
+     * When empty string, no default voicemail number is specified for roaming network.
+     * @hide
+     */
+    public static final String KEY_DEFAULT_VM_NUMBER_ROAMING_STRING =
+            "default_vm_number_roaming_string";
+
+    /**
      * Flag that specifies to use the user's own phone number as the voicemail number when there is
      * no pre-loaded voicemail number on the SIM card.
      * <p>
@@ -951,6 +960,8 @@
      * If user has explicitly disabled some packages in the list, won't re-enable.
      * Other carrier specific apps which are not in this list may be disabled for current carrier,
      * and only be re-enabled when this config for another carrier includes it.
+     *
+     * @hide
      */
     public static final String KEY_ENABLE_APPS_STRING_ARRAY = "enable_apps_string_array";
 
@@ -1929,6 +1940,7 @@
         sDefaults.putBoolean(KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL, true);
         sDefaults.putString(KEY_DEFAULT_VM_NUMBER_STRING, "");
+        sDefaults.putString(KEY_DEFAULT_VM_NUMBER_ROAMING_STRING, "");
         sDefaults.putBoolean(KEY_CONFIG_TELEPHONY_USE_OWN_NUMBER_FOR_VOICEMAIL_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS, true);
         sDefaults.putBoolean(KEY_VILTE_DATA_IS_METERED_BOOL, true);
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 6480aab..6db8e82 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -40,17 +40,19 @@
  */
 public final class LocationAccessPolicy {
     private static final String LOG_TAG = LocationAccessPolicy.class.getSimpleName();
+
     /**
      * API to determine if the caller has permissions to get cell location.
      *
      * @param pkgName Package name of the application requesting access
      * @param uid The uid of the package
      * @param pid The pid of the package
+     * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
      * @return boolean true or false if permissions is granted
      */
     public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
-            int uid, int pid) throws SecurityException {
-        Trace.beginSection("TelephonyLocationCheck");
+            int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
+        Trace.beginSection("TelephonyLohcationCheck");
         try {
             // Always allow the phone process to access location. This avoid breaking legacy code
             // that rely on public-facing APIs to access cell location, and it doesn't create a
@@ -65,9 +67,11 @@
             // where a legacy app the user is not using tracks their location.
             // Granting ACCESS_FINE_LOCATION to an app automatically grants it
             // ACCESS_COARSE_LOCATION.
-
-            if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) ==
-                    PackageManager.PERMISSION_DENIED) {
+            if (throwOnDeniedPermission) {
+                context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                        pid, uid, "canAccessCellLocation");
+            } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
+                    pid, uid) == PackageManager.PERMISSION_DENIED) {
                 return false;
             }
             final int opCode = AppOpsManager.permissionToOpCode(
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
index 9dc07c1..da04a0d 100644
--- a/telephony/java/android/telephony/MbmsDownloadSession.java
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -920,11 +920,11 @@
         try {
             if (!token.createNewFile()) {
                 throw new RuntimeException("Failed to create download token for request "
-                        + request);
+                        + request + ". Token location is " + token.getPath());
             }
         } catch (IOException e) {
             throw new RuntimeException("Failed to create download token for request " + request
-                    + " due to IOException " + e);
+                    + " due to IOException " + e + ". Attempted to write to " + token.getPath());
         }
     }
 
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index 77413d9c..936505c 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -335,7 +335,7 @@
         return this.mCountryIso;
     }
 
-    /** @return whether the subscription is an embedded one. */
+    /** @return whether the subscription is an eUICC one. */
     public boolean isEmbedded() {
         return this.mIsEmbedded;
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 472a6fb..7664b94 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1925,26 +1925,28 @@
      *
      * @param info The subscription to check.
      * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
+     * @throws IllegalArgumentException if this subscription is not embedded.
      */
     public boolean canManageSubscription(SubscriptionInfo info) {
         return canManageSubscription(info, mContext.getPackageName());
     }
 
     /**
-     * Checks whether the given app is authorized to manage the given subscription according to its
-     * metadata. Only supported for embedded subscriptions (if {@code SubscriptionInfo#isEmbedded}
+     * Checks whether the given app is authorized to manage the given subscription. An app can only
+     * be authorized if it is included in the {@link android.telephony.UiccAccessRule} of the
+     * {@link android.telephony.SubscriptionInfo} with the access status.
+     * Only supported for embedded subscriptions (if {@link SubscriptionInfo#isEmbedded}
      * returns true).
      *
      * @param info The subscription to check.
      * @param packageName Package name of the app to check.
-     * @return whether the app is authorized to manage this subscription per its metadata.
-     * @throws UnsupportedOperationException if this subscription is not embedded.
+     * @return whether the app is authorized to manage this subscription per its access rules.
+     * @throws IllegalArgumentException if this subscription is not embedded.
      * @hide
      */
     public boolean canManageSubscription(SubscriptionInfo info, String packageName) {
         if (!info.isEmbedded()) {
-            throw new UnsupportedOperationException("Not an embedded subscription");
+            throw new IllegalArgumentException("Not an embedded subscription");
         }
         if (info.getAccessRules() == null) {
             return false;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index a12a7a0..15e0632 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6349,34 +6349,39 @@
      *
      * @param enable Whether to enable mobile data.
      *
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
      */
-    @Deprecated
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(enable);
+        setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
     }
 
     /**
      * @hide
-     * @deprecated use {@link #setUserMobileDataEnabled(boolean)} instead.
+     * @deprecated use {@link #setDataEnabled(boolean)} instead.
     */
     @SystemApi
     @Deprecated
     @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
     public void setDataEnabled(int subId, boolean enable) {
-        setUserMobileDataEnabled(subId, enable);
+        try {
+            Log.d(TAG, "setDataEnabled: enabled=" + enable);
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                telephony.setUserDataEnabled(subId, enable);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
+        }
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @SystemApi
     @Deprecated
     public boolean getDataEnabled() {
-        return isUserMobileDataEnabled();
+        return isDataEnabled();
     }
 
     /**
@@ -6396,22 +6401,28 @@
      * {@link ConnectivityManager#getRestrictBackgroundStatus}.
      *
      * @return true if mobile data is enabled.
-     *
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
      */
-    @Deprecated
     public boolean isDataEnabled() {
-        return isUserMobileDataEnabled();
+        return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
     }
 
     /**
-     * @deprecated use {@link #isUserMobileDataEnabled()} instead.
+     * @deprecated use {@link #isDataEnabled()} instead.
      * @hide
      */
     @Deprecated
     @SystemApi
     public boolean getDataEnabled(int subId) {
-        return isUserMobileDataEnabled(subId);
+        boolean retVal = false;
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null)
+                retVal = telephony.isUserDataEnabled(subId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
+        } catch (NullPointerException e) {
+        }
+        return retVal;
     }
 
     /** @hide */
@@ -7684,56 +7695,12 @@
     }
 
     /**
-     * Turns mobile data on or off.
-     * If the {@link TelephonyManager} object has been created with
-     * {@link #createForSubscriptionId}, this API applies to the given subId.
-     * Otherwise, it applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires Permission:
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
-     * app has carrier privileges (see {@link #hasCarrierPrivileges}.
-     *
-     * @param enable Whether to enable mobile data.
-     */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    public void setUserMobileDataEnabled(boolean enable) {
-        setUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
-    }
-
-    /**
-     * Returns whether mobile data is enabled or not per user setting. There are other factors
-     * that could disable mobile data, but they are not considered here.
-     *
-     * If this object has been created with {@link #createForSubscriptionId}, applies to the given
-     * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
-     *
-     * <p>Requires one of the following permissions:
-     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
-     * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
-     * calling app has carrier privileges (see {@link #hasCarrierPrivileges}.
-     *
-     * <p>Note that this does not take into account any data restrictions that may be present on the
-     * calling app. Such restrictions may be inspected with
-     * {@link ConnectivityManager#getRestrictBackgroundStatus}.
-     *
-     * @return true if mobile data is enabled.
-     */
-    @RequiresPermission(anyOf = {
-            android.Manifest.permission.ACCESS_NETWORK_STATE,
-            android.Manifest.permission.MODIFY_PHONE_STATE
-    })
-    public boolean isUserMobileDataEnabled() {
-        return isUserMobileDataEnabled(
-                getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
-    }
-
-    /**
      * @hide
-     * Unlike isUserMobileDataEnabled, this API also evaluates carrierDataEnabled,
-     * policyDataEnabled etc to give a final decision.
+     * It's similar to isDataEnabled, but unlike isDataEnabled, this API also evaluates
+     * carrierDataEnabled, policyDataEnabled etc to give a final decision of whether mobile data is
+     * capable of using.
      */
-    public boolean isMobileDataEnabled() {
+    public boolean isDataCapable() {
         boolean retVal = false;
         try {
             int subId = getSubId(SubscriptionManager.getDefaultDataSubscriptionId());
@@ -7748,35 +7715,6 @@
     }
 
     /**
-     * Utility class of {@link #isUserMobileDataEnabled()};
-     */
-    private boolean isUserMobileDataEnabled(int subId) {
-        boolean retVal = false;
-        try {
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                retVal = telephony.isUserDataEnabled(subId);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e);
-        } catch (NullPointerException e) {
-        }
-        return retVal;
-    }
-
-    /** Utility method of {@link #setUserMobileDataEnabled(boolean)} */
-    @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
-    private void setUserMobileDataEnabled(int subId, boolean enable) {
-        try {
-            Log.d(TAG, "setUserMobileDataEnabled: enabled=" + enable);
-            ITelephony telephony = getITelephony();
-            if (telephony != null)
-                telephony.setUserDataEnabled(subId, enable);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#setUserDataEnabled", e);
-        }
-    }
-
-    /**
      * In this mode, modem will not send specified indications when screen is off.
      * @hide
      */
@@ -7814,11 +7752,25 @@
      */
     public static final int INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED    = 0x4;
 
+    /**
+     * The indication for link capacity estimate update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_LINK_CAPACITY_ESTIMATE        = 0x8;
+
+    /**
+     * The indication for physical channel config update.
+     * @hide
+     */
+    public static final int INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG       = 0x10;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "INDICATION_FILTER_" }, value = {
             INDICATION_FILTER_SIGNAL_STRENGTH,
             INDICATION_FILTER_FULL_NETWORK_STATE,
-            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED
+            INDICATION_FILTER_DATA_CALL_DORMANCY_CHANGED,
+            INDICATION_FILTER_LINK_CAPACITY_ESTIMATE,
+            INDICATION_FILTER_PHYSICAL_CHANNEL_CONFIG
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface IndicationFilters{}
diff --git a/telephony/java/android/telephony/euicc/DownloadableSubscription.java b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
index 88db22b..edf3b08 100644
--- a/telephony/java/android/telephony/euicc/DownloadableSubscription.java
+++ b/telephony/java/android/telephony/euicc/DownloadableSubscription.java
@@ -17,6 +17,7 @@
 
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
+import android.app.PendingIntent;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.UiccAccessRule;
@@ -26,7 +27,14 @@
 
 import com.android.internal.util.Preconditions;
 
-/** Information about a subscription which is available for download. */
+/**
+ * Information about a subscription which is downloadable to an eUICC using
+ * {@link EuiccManager#downloadSubscription(DownloadableSubscription, boolean, PendingIntent).
+ *
+ * <p>For example, a DownloadableSubscription can be created through an activation code parsed from
+ * a QR code. A server address can be parsed from the activation code to download more information
+ * about the profile, such as carrier name, access rules, etc.
+ */
 public final class DownloadableSubscription implements Parcelable {
 
     public static final Creator<DownloadableSubscription> CREATOR =
@@ -136,7 +144,15 @@
     /**
      * Create a DownloadableSubscription for the given activation code.
      *
-     * @param encodedActivationCode the activation code to use. Must not be null.
+     * <p>This fills the encodedActivationCode field. Other fields like confirmationCode,
+     * carrierName and accessRules may be filled in the implementation of
+     * {@code android.service.euicc.EuiccService} if exists.
+     *
+     * @param encodedActivationCode the activation code to use. An activation code can be parsed
+     *         from a user scanned QR code. The format of activation code is defined in SGP.22. For
+     *         example, "1$SMDP.GSMA.COM$04386-AGYFT-A74Y8-3F815$1.3.6.1.4.1.31746". For detail, see
+     *         {@code com.android.euicc.data.ActivationCode}. Must not be null.
+     *
      * @return the {@link DownloadableSubscription} which may be passed to
      *     {@link EuiccManager#downloadSubscription}.
      */
@@ -157,6 +173,9 @@
 
     /**
      * Returns the confirmation code.
+     *
+     * <p>As an example, the confirmation code can be input by the user through a carrier app or the
+     * UI component of the eUICC local profile assistant (LPA) application.
      */
     @Nullable
     public String getConfirmationCode() {
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index dff1c6f..b732d4d 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -142,11 +142,9 @@
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DETAILED_CODE";
 
     /**
-     * Key for an extra set on {@link #getDownloadableSubscriptionMetadata} PendingIntent result
+     * Key for an extra set on {@code #getDownloadableSubscriptionMetadata} PendingIntent result
      * callbacks providing the downloadable subscription metadata.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION =
             "android.telephony.euicc.extra.EMBEDDED_SUBSCRIPTION_DOWNLOADABLE_SUBSCRIPTION";
 
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index b0c00c6..fe7533f 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -297,7 +297,9 @@
         for (Uri tempFileUri : tempFiles) {
             if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
                 File tempFile = new File(tempFileUri.getSchemeSpecificPart());
-                tempFile.delete();
+                if (!tempFile.delete()) {
+                    Log.w(LOG_TAG, "Failed to delete temp file at " + tempFile.getPath());
+                }
             }
         }
     }
@@ -474,6 +476,8 @@
 
         if (!MbmsUtils.isContainedIn(
                 MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
+            Log.w(LOG_TAG, "File at " + path + " is not contained in the temp file root," +
+                    " which is " + MbmsUtils.getEmbmsTempFileDirForService(context, serviceId));
             return false;
         }
 
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index 8b81b0d..bcad554 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -26,6 +26,7 @@
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -49,10 +50,11 @@
     /**
      * Handle preinstalled carrier apps which should be disabled until a matching SIM is inserted.
      *
-     * Evaluates the list of applications in config_disabledUntilUsedPreinstalledCarrierApps. We
-     * want to disable each such application which is present on the system image until the user
-     * inserts a SIM which causes that application to gain carrier privilege (indicating a "match"),
-     * without interfering with the user if they opt to enable/disable the app explicitly.
+     * Evaluates the list of applications in
+     * {@link SystemConfig#getDisabledUntilUsedPreinstalledCarrierApps()}. We want to disable each
+     * such application which is present on the system image until the user inserts a SIM which
+     * causes that application to gain carrier privilege (indicating a "match"), without interfering
+     * with the user if they opt to enable/disable the app explicitly.
      *
      * So, for each such app, we either disable until used IFF the app is not carrier privileged AND
      * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if
@@ -76,8 +78,8 @@
             Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                config.getDisabledUntilUsedPreinstalledCarrierApps();
         ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
         disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager,
@@ -102,8 +104,10 @@
             Slog.d(TAG, "disableCarrierAppsUntilPrivileged");
         }
         SystemConfig config = SystemConfig.getInstance();
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                config.getDisabledUntilUsedPreinstalledCarrierApps();
+
+
         ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed =
                 config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps();
         disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
@@ -116,7 +120,7 @@
     public static void disableCarrierAppsUntilPrivileged(String callingPackage,
             IPackageManager packageManager, @Nullable TelephonyManager telephonyManager,
             ContentResolver contentResolver, int userId,
-            String[] systemCarrierAppsDisabledUntilUsed,
+            ArraySet<String> systemCarrierAppsDisabledUntilUsed,
             ArrayMap<String, List<String>> systemCarrierAssociatedAppsDisabledUntilUsed) {
         List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
                 userId, systemCarrierAppsDisabledUntilUsed);
@@ -286,8 +290,8 @@
      */
     public static List<ApplicationInfo> getDefaultCarrierAppCandidates(
             IPackageManager packageManager, int userId) {
-        String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
-                com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);
+        ArraySet<String> systemCarrierAppsDisabledUntilUsed =
+                SystemConfig.getInstance().getDisabledUntilUsedPreinstalledCarrierApps();
         return getDefaultCarrierAppCandidatesHelper(packageManager, userId,
                 systemCarrierAppsDisabledUntilUsed);
     }
@@ -295,14 +299,19 @@
     private static List<ApplicationInfo> getDefaultCarrierAppCandidatesHelper(
             IPackageManager packageManager,
             int userId,
-            String[] systemCarrierAppsDisabledUntilUsed) {
-        if (systemCarrierAppsDisabledUntilUsed == null
-                || systemCarrierAppsDisabledUntilUsed.length == 0) {
+            ArraySet<String> systemCarrierAppsDisabledUntilUsed) {
+        if (systemCarrierAppsDisabledUntilUsed == null) {
             return null;
         }
-        List<ApplicationInfo> apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length);
-        for (int i = 0; i < systemCarrierAppsDisabledUntilUsed.length; i++) {
-            String packageName = systemCarrierAppsDisabledUntilUsed[i];
+
+        int size = systemCarrierAppsDisabledUntilUsed.size();
+        if (size == 0) {
+            return null;
+        }
+
+        List<ApplicationInfo> apps = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            String packageName = systemCarrierAppsDisabledUntilUsed.valueAt(i);
             ApplicationInfo ai =
                     getApplicationInfoIfSystemApp(packageManager, userId, packageName);
             if (ai != null) {
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index ee7084a..d25fd3f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -419,6 +419,8 @@
     int RIL_REQUEST_SET_LOGICAL_TO_PHYSICAL_SLOT_MAPPING = 145;
     int RIL_REQUEST_START_KEEPALIVE = 146;
     int RIL_REQUEST_STOP_KEEPALIVE = 147;
+    int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 148;
+    int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 149;
 
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
 
diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
new file mode 100644
index 0000000..25e1474
--- /dev/null
+++ b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java
@@ -0,0 +1,213 @@
+/*
+ * 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.app.usage;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.INetworkStatsService;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats.Entry;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkStatsManagerTest {
+
+    private @Mock INetworkStatsService mService;
+    private @Mock INetworkStatsSession mStatsSession;
+
+    private NetworkStatsManager mManager;
+
+    // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp.
+    private static final int MATCH_MOBILE_ALL = 1;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService);
+    }
+
+    @Test
+    public void testQueryDetails() throws RemoteException {
+        final String subscriberId = "subid";
+        final long startTime = 1;
+        final long endTime = 100;
+        final int uid1 = 10001;
+        final int uid2 = 10002;
+        final int uid3 = 10003;
+
+        Entry uid1Entry1 = new Entry("if1", uid1,
+                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                100, 10, 200, 20, 0);
+
+        Entry uid1Entry2 = new Entry(
+                "if2", uid1,
+                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                100, 10, 200, 20, 0);
+
+        Entry uid2Entry1 = new Entry("if1", uid2,
+                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                150, 10, 250, 20, 0);
+
+        Entry uid2Entry2 = new Entry(
+                "if2", uid2,
+                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
+                150, 10, 250, 20, 0);
+
+        NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
+        history1.recordData(10, 20, uid1Entry1);
+        history1.recordData(20, 30, uid1Entry2);
+
+        NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2);
+        history1.recordData(30, 40, uid2Entry1);
+        history1.recordData(35, 45, uid2Entry2);
+
+
+        when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+        when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 });
+
+        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
+                eq(android.net.NetworkStats.TAG_NONE),
+                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+                .then((InvocationOnMock inv) -> {
+                    NetworkTemplate template = inv.getArgument(0);
+                    assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
+                    assertEquals(subscriberId, template.getSubscriberId());
+                    return history1;
+                });
+
+        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
+                eq(android.net.NetworkStats.TAG_NONE),
+                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
+                .then((InvocationOnMock inv) -> {
+                    NetworkTemplate template = inv.getArgument(0);
+                    assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
+                    assertEquals(subscriberId, template.getSubscriberId());
+                    return history2;
+                });
+
+
+        NetworkStats stats = mManager.queryDetails(
+                ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime);
+
+        NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+
+        // First 2 buckets exactly match entry timings
+        assertTrue(stats.getNextBucket(bucket));
+        assertEquals(10, bucket.getStartTimeStamp());
+        assertEquals(20, bucket.getEndTimeStamp());
+        assertBucketMatches(uid1Entry1, bucket);
+
+        assertTrue(stats.getNextBucket(bucket));
+        assertEquals(20, bucket.getStartTimeStamp());
+        assertEquals(30, bucket.getEndTimeStamp());
+        assertBucketMatches(uid1Entry2, bucket);
+
+        // 30 -> 40: contains uid2Entry1 and half of uid2Entry2
+        assertTrue(stats.getNextBucket(bucket));
+        assertEquals(30, bucket.getStartTimeStamp());
+        assertEquals(40, bucket.getEndTimeStamp());
+        assertEquals(225, bucket.getRxBytes());
+        assertEquals(15, bucket.getRxPackets());
+        assertEquals(375, bucket.getTxBytes());
+        assertEquals(30, bucket.getTxPackets());
+
+        // 40 -> 50: contains half of uid2Entry2
+        assertTrue(stats.getNextBucket(bucket));
+        assertEquals(40, bucket.getStartTimeStamp());
+        assertEquals(50, bucket.getEndTimeStamp());
+        assertEquals(75, bucket.getRxBytes());
+        assertEquals(5, bucket.getRxPackets());
+        assertEquals(125, bucket.getTxBytes());
+        assertEquals(10, bucket.getTxPackets());
+
+        assertFalse(stats.hasNextBucket());
+    }
+
+    @Test
+    public void testQueryDetails_NoSubscriberId() throws RemoteException {
+        final long startTime = 1;
+        final long endTime = 100;
+        final int uid1 = 10001;
+        final int uid2 = 10002;
+
+        when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
+        when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 });
+
+        NetworkStats stats = mManager.queryDetails(
+                ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);
+
+        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
+                anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()))
+                .thenReturn(new NetworkStatsHistory(10, 0));
+
+        verify(mStatsSession, times(1)).getHistoryIntervalForUid(
+                argThat((NetworkTemplate t) ->
+                        // No subscriberId: MATCH_MOBILE_WILDCARD template
+                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
+                eq(android.net.NetworkStats.TAG_NONE),
+                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+
+        verify(mStatsSession, times(1)).getHistoryIntervalForUid(
+                argThat((NetworkTemplate t) ->
+                        // No subscriberId: MATCH_MOBILE_WILDCARD template
+                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
+                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
+                eq(android.net.NetworkStats.TAG_NONE),
+                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));
+
+        assertFalse(stats.hasNextBucket());
+    }
+
+    private void assertBucketMatches(Entry expected,
+            NetworkStats.Bucket actual) {
+        assertEquals(expected.uid, actual.getUid());
+        assertEquals(expected.rxBytes, actual.getRxBytes());
+        assertEquals(expected.rxPackets, actual.getRxPackets());
+        assertEquals(expected.txBytes, actual.getTxBytes());
+        assertEquals(expected.txPackets, actual.getTxPackets());
+    }
+}
diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java
index 6bdfdc6..85e8361 100644
--- a/tests/net/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/net/java/android/net/IpSecAlgorithmTest.java
@@ -22,8 +22,12 @@
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+
+import java.util.AbstractMap.SimpleEntry;
 import java.util.Arrays;
+import java.util.Map.Entry;
 import java.util.Random;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -40,19 +44,29 @@
     };
 
     @Test
-    public void testDefaultTruncLen() throws Exception {
-        IpSecAlgorithm explicit =
+    public void testNoTruncLen() throws Exception {
+        Entry<String, Integer>[] authAndAeadList =
+                new Entry[] {
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_MD5, 128),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA1, 160),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512),
+                    new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224)
+                };
+
+        // Expect auth and aead algorithms to throw errors if trunclen is omitted.
+        for (Entry<String, Integer> algData : authAndAeadList) {
+            try {
                 new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8), 256);
-        IpSecAlgorithm implicit =
-                new IpSecAlgorithm(
-                        IpSecAlgorithm.AUTH_HMAC_SHA256, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
-        assertTrue(
-                "Default Truncation Length Incorrect, Explicit: "
-                        + explicit
-                        + "implicit: "
-                        + implicit,
-                IpSecAlgorithm.equals(explicit, implicit));
+                        algData.getKey(), Arrays.copyOf(KEY_MATERIAL, algData.getValue() / 8));
+                fail("Expected exception on unprovided auth trunclen");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+
+        // Ensure crypt works with no truncation length supplied.
+        new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8));
     }
 
     @Test
diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java
index f186ee5..771faaf 100644
--- a/tests/net/java/android/net/IpSecConfigTest.java
+++ b/tests/net/java/android/net/IpSecConfigTest.java
@@ -62,7 +62,8 @@
         c.setAuthentication(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_HMAC_MD5,
-                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0}));
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0},
+                        128));
         c.setAuthenticatedEncryption(
                 new IpSecAlgorithm(
                         IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index 0ca20de..970596d 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -179,7 +179,7 @@
 
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket(TEST_UDP_ENCAP_PORT);
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();
@@ -202,7 +202,7 @@
         IpSecManager.UdpEncapsulationSocket encapSocket =
                 mIpSecManager.openUdpEncapsulationSocket();
 
-        assertNotNull(encapSocket.getSocket());
+        assertNotNull(encapSocket.getFileDescriptor());
         assertEquals(TEST_UDP_ENCAP_PORT, encapSocket.getPort());
 
         encapSocket.close();
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index c866371..941c94d 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -315,28 +315,26 @@
 
     @Test
     public void testEqualsNetCapabilities() {
-        int CAPABILITY = NET_CAPABILITY_MMS;  // An arbitrary not mutable capability.
-
         NetworkCapabilities nc1 = new NetworkCapabilities();
         NetworkCapabilities nc2 = new NetworkCapabilities();
         assertTrue(nc1.equalsNetCapabilities(nc2));
         assertEquals(nc1, nc2);
 
-        nc1.addCapability(CAPABILITY);
+        nc1.addCapability(NET_CAPABILITY_MMS);
         assertFalse(nc1.equalsNetCapabilities(nc2));
         assertNotEquals(nc1, nc2);
-        nc2.addCapability(CAPABILITY);
+        nc2.addCapability(NET_CAPABILITY_MMS);
         assertTrue(nc1.equalsNetCapabilities(nc2));
         assertEquals(nc1, nc2);
 
-        nc1.addUnwantedCapability(CAPABILITY);
+        nc1.addUnwantedCapability(NET_CAPABILITY_INTERNET);
         assertFalse(nc1.equalsNetCapabilities(nc2));
-        nc2.addUnwantedCapability(CAPABILITY);
+        nc2.addUnwantedCapability(NET_CAPABILITY_INTERNET);
         assertTrue(nc1.equalsNetCapabilities(nc2));
 
-        nc1.removeCapability(CAPABILITY);
+        nc1.removeCapability(NET_CAPABILITY_INTERNET);
         assertFalse(nc1.equalsNetCapabilities(nc2));
-        nc2.removeCapability(CAPABILITY);
+        nc2.removeCapability(NET_CAPABILITY_INTERNET);
         assertTrue(nc1.equalsNetCapabilities(nc2));
     }
 
diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java
new file mode 100644
index 0000000..8012838
--- /dev/null
+++ b/tests/net/java/android/net/util/InterfaceSetTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.net.util;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class InterfaceSetTest {
+    @Test
+    public void testNullNamesIgnored() {
+        final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null);
+        assertEquals(2, set.ifnames.size());
+        assertTrue(set.ifnames.contains("if1"));
+        assertTrue(set.ifnames.contains("if2"));
+    }
+
+    @Test
+    public void testToString() {
+        final InterfaceSet set = new InterfaceSet("if1", "if2");
+        final String setString = set.toString();
+        assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]"));
+    }
+
+    @Test
+    public void testToString_Empty() {
+        final InterfaceSet set = new InterfaceSet(null, null);
+        assertEquals("[]", set.toString());
+    }
+
+    @Test
+    public void testEquals() {
+        assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1"));
+        assertEquals(new InterfaceSet(null, null), new InterfaceSet());
+        assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2")));
+        assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1")));
+        assertFalse(new InterfaceSet().equals(null));
+    }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index e692652..27f90f2 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -27,8 +27,11 @@
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
 import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.notNull;
 import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyString;
@@ -36,6 +39,7 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -45,18 +49,29 @@
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
+import android.net.INetd;
 import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
-import android.net.NetworkRequest;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.net.NetworkUtils;
+import android.net.RouteInfo;
+import android.net.ip.RouterAdvertisementDaemon;
+import android.net.util.InterfaceParams;
+import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
@@ -74,10 +89,16 @@
 import android.telephony.CarrierConfigManager;
 import android.test.mock.MockContentResolver;
 
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.StateMachine;
 import com.android.internal.util.test.BroadcastInterceptingContext;
 import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.tethering.IControlsTethering;
+import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadHardwareInterface;
+import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
 import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
 
 import org.junit.After;
 import org.junit.Before;
@@ -86,13 +107,21 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.util.ArrayList;
 import java.util.Vector;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class TetheringTest {
+    private static final int IFINDEX_OFFSET = 100;
+
     private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
+    private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0";
+    private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0";
+    private static final String TEST_USB_IFNAME = "test_rndis0";
+    private static final String TEST_WLAN_IFNAME = "test_wlan0";
 
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
@@ -103,16 +132,21 @@
     @Mock private MockableSystemProperties mSystemProperties;
     @Mock private OffloadHardwareInterface mOffloadHardwareInterface;
     @Mock private Resources mResources;
-    @Mock private TetheringDependencies mTetheringDependencies;
     @Mock private UsbManager mUsbManager;
     @Mock private WifiManager mWifiManager;
     @Mock private CarrierConfigManager mCarrierConfigManager;
+    @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
+    @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
+    @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+    @Mock private INetd mNetd;
+
+    private final MockTetheringDependencies mTetheringDependencies =
+            new MockTetheringDependencies();
 
     // Like so many Android system APIs, these cannot be mocked because it is marked final.
     // We have to use the real versions.
     private final PersistableBundle mCarrierConfig = new PersistableBundle();
     private final TestLooper mLooper = new TestLooper();
-    private final String mTestIfname = "test_wlan0";
 
     private Vector<Intent> mIntents;
     private BroadcastInterceptingContext mServiceContext;
@@ -146,23 +180,122 @@
         }
     }
 
+    public class MockTetheringDependencies extends TetheringDependencies {
+        private StateMachine upstreamNetworkMonitorMasterSM;
+        private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList;
+
+        @Override
+        public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) {
+            return mOffloadHardwareInterface;
+        }
+
+        @Override
+        public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx,
+                StateMachine target, SharedLog log, int what) {
+            upstreamNetworkMonitorMasterSM = target;
+            return mUpstreamNetworkMonitor;
+        }
+
+        @Override
+        public IPv6TetheringCoordinator getIPv6TetheringCoordinator(
+                ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) {
+            ipv6CoordinatorNotifyList = notifyList;
+            return mIPv6TetheringCoordinator;
+        }
+
+        @Override
+        public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
+            return mRouterAdvertisementDaemon;
+        }
+
+        @Override
+        public INetd getNetdService() {
+            return mNetd;
+        }
+
+        @Override
+        public InterfaceParams getInterfaceParams(String ifName) {
+            final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME,
+                    TEST_MOBILE_IFNAME };
+            final int index = ArrayUtils.indexOf(ifaces, ifName);
+            assertTrue("Non-mocked interface: " + ifName, index >= 0);
+            return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
+                    MacAddress.ALL_ZEROS_ADDRESS);
+        }
+    }
+
+    private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
+            boolean with464xlat) {
+        final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null);
+        info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(TEST_MOBILE_IFNAME);
+
+        if (withIPv4) {
+            prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME));
+        }
+
+        if (withIPv6) {
+            prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2"));
+            prop.addLinkAddress(
+                    new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"),
+                            NetworkConstants.RFC7421_PREFIX_LENGTH));
+            prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME));
+        }
+
+        if (with464xlat) {
+            final LinkProperties stackedLink = new LinkProperties();
+            stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME);
+            stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+                    NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME));
+
+            prop.addStackedLink(stackedLink);
+        }
+
+
+        final NetworkCapabilities capabilities = new NetworkCapabilities()
+                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);;
+        return new NetworkState(info, prop, capabilities, new Network(100), null, "netid");
+    }
+
+    private static NetworkState buildMobileIPv4UpstreamState() {
+        return buildMobileUpstreamState(true, false, false);
+    }
+
+    private static NetworkState buildMobileIPv6UpstreamState() {
+        return buildMobileUpstreamState(false, true, false);
+    }
+
+    private static NetworkState buildMobileDualStackUpstreamState() {
+        return buildMobileUpstreamState(true, true, false);
+    }
+
+    private static NetworkState buildMobile464xlatUpstreamState() {
+        return buildMobileUpstreamState(false, true, true);
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
                 .thenReturn(new String[0]);
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
-                .thenReturn(new String[0]);
+                .thenReturn(new String[] { "test_rndis\\d" });
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
-                .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
+                .thenReturn(new String[]{ "test_wlan\\d" });
         when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
                 .thenReturn(new String[0]);
         when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
                 .thenReturn(new int[0]);
         when(mNMService.listInterfaces())
-                .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname });
+                .thenReturn(new String[] {
+                        TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME});
         when(mNMService.getInterfaceConfig(anyString()))
                 .thenReturn(new InterfaceConfiguration());
+        when(mRouterAdvertisementDaemon.start())
+                .thenReturn(true);
 
         mServiceContext = new MockContext(mContext);
         mContentResolver = new MockContentResolver(mServiceContext);
@@ -176,8 +309,6 @@
         };
         mServiceContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
-        when(mTetheringDependencies.getOffloadHardwareInterface(
-                any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface);
         mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
                                    mLooper.getLooper(), mSystemProperties,
                                    mTetheringDependencies);
@@ -264,10 +395,10 @@
     }
 
     private void verifyInterfaceServingModeStarted() throws Exception {
-        verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
+        verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(1))
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).tetherInterface(mTestIfname);
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+        verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
     }
 
     private void verifyTetheringBroadcast(String ifname, String whichExtra) {
@@ -287,7 +418,7 @@
         // per-interface state machine to start up, and telling us that
         // hotspot mode is to be started.
         if (emulateInterfaceStatusChanged) {
-            mTethering.interfaceStatusChanged(mTestIfname, true);
+            mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         }
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
@@ -297,27 +428,31 @@
         // broadcast indicating that the interface is "available".
         if (emulateInterfaceStatusChanged) {
             verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-            verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+            verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         }
         verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
     }
 
-    @Test
-    public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+    private void prepareUsbTethering(NetworkState upstreamState) {
         when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
 
         // Emulate pressing the USB tethering button in Settings UI.
         mTethering.startTethering(TETHERING_USB, null, false);
         mLooper.dispatchAll();
         verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
 
-        // Pretend we receive a USB connected broadcast. Here we also pretend
-        // that the RNDIS function is somehow enabled, so that we see if we
-        // might trip ourselves up.
-        sendUsbBroadcast(true, false, true);
-        mLooper.dispatchAll();
+        mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
+    }
+
+    @Test
+    public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+        NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        prepareUsbTethering(upstreamState);
+
         // This should produce no activity of any kind.
         verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
@@ -328,6 +463,10 @@
         // Now we should see the start of tethering mechanics (in this case:
         // tetherMatchingInterfaces() which starts by fetching all interfaces).
         verify(mNMService, times(1)).listInterfaces();
+
+        // UpstreamNetworkMonitor should receive selected upstream
+        verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any());
+        verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network);
     }
 
     @Test
@@ -348,26 +487,21 @@
         // per-interface state machine to start up, and telling us that
         // hotspot mode is to be started.
         if (emulateInterfaceStatusChanged) {
-            mTethering.interfaceStatusChanged(mTestIfname, true);
+            mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         }
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY);
         mLooper.dispatchAll();
 
         verifyInterfaceServingModeStarted();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
-        // UpstreamNetworkMonitor will be started, and will register two callbacks:
-        // a "listen all" and a "track default".
-        verify(mConnectivityManager, times(1)).registerNetworkCallback(
-                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
-                any(NetworkCallback.class), any(Handler.class));
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY);
+        verify(mUpstreamNetworkMonitor, times(1)).start();
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
         verifyNoMoreInteractions(mConnectivityManager);
@@ -375,14 +509,14 @@
         // Emulate externally-visible WifiManager effects, when hotspot mode
         // is being torn down.
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
-        mTethering.interfaceRemoved(mTestIfname);
+        mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         // TODO: Why is {g,s}etInterfaceConfig() called more than once?
-        verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+        verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, atLeastOnce())
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
         verifyNoMoreInteractions(mNMService);
@@ -390,7 +524,115 @@
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
         assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
-                mTethering.getLastTetherError(mTestIfname));
+                mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+    }
+
+    /**
+     * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator.
+     */
+    private void sendIPv6TetherUpdates(NetworkState upstreamState) {
+        // IPv6TetheringCoordinator must have been notified of downstream
+        verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream(
+                argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)),
+                eq(IControlsTethering.STATE_TETHERED));
+
+        for (TetherInterfaceStateMachine tism :
+                mTetheringDependencies.ipv6CoordinatorNotifyList) {
+            NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false);
+            tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0,
+                    upstreamState.linkProperties.isIPv6Provisioned()
+                            ? ipv6OnlyState.linkProperties
+                            : null);
+        }
+        mLooper.dispatchAll();
+    }
+
+    private void runUsbTethering(NetworkState upstreamState) {
+        prepareUsbTethering(upstreamState);
+        sendUsbBroadcast(true, true, true);
+        mLooper.dispatchAll();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv4() throws Exception {
+        NetworkState upstreamState = buildMobileIPv4UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
+    }
+
+    @Test
+    public void workingMobileUsbTethering_IPv6() throws Exception {
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_DualStack() throws Exception {
+        NetworkState upstreamState = buildMobileDualStackUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mRouterAdvertisementDaemon, times(1)).start();
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_MultipleUpstreams() throws Exception {
+        NetworkState upstreamState = buildMobile464xlatUpstreamState();
+        runUsbTethering(upstreamState);
+
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
+
+        sendIPv6TetherUpdates(upstreamState);
+        verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
+        verify(mNetd, times(1)).tetherApplyDnsInterfaces();
+    }
+
+    @Test
+    public void workingMobileUsbTethering_v6Then464xlat() throws Exception {
+        // Setup IPv6
+        NetworkState upstreamState = buildMobileIPv6UpstreamState();
+        runUsbTethering(upstreamState);
+
+        // Then 464xlat comes up
+        upstreamState = buildMobile464xlatUpstreamState();
+        when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any()))
+                .thenReturn(upstreamState);
+
+        // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES.
+        mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage(
+                Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK,
+                UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES,
+                0,
+                upstreamState);
+        mLooper.dispatchAll();
+
+        // Forwarding is added for 464xlat, and was still added only once for v6
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
+        verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
+        verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
+                TEST_XLAT_MOBILE_IFNAME);
     }
 
     @Test
@@ -420,12 +662,12 @@
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
         sendWifiApStateChanged(WIFI_AP_STATE_ENABLED);
         mLooper.dispatchAll();
 
         verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         verifyNoMoreInteractions(mConnectivityManager);
         verifyNoMoreInteractions(mNMService);
         verifyNoMoreInteractions(mWifiManager);
@@ -448,30 +690,23 @@
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
         verifyInterfaceServingModeStarted();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         verify(mNMService, times(1)).startTethering(any(String[].class));
         verifyNoMoreInteractions(mNMService);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER);
-        // UpstreamNetworkMonitor will be started, and will register two callbacks:
-        // a "listen all" and a "track default".
-        verify(mConnectivityManager, times(1)).registerNetworkCallback(
-                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback(
-                any(NetworkCallback.class), any(Handler.class));
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_TETHER);
+        verify(mUpstreamNetworkMonitor, times(1)).start();
         // In tethering mode, in the default configuration, an explicit request
         // for a mobile network is also made.
-        verify(mConnectivityManager, times(1)).requestNetwork(
-                any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(),
-                any(Handler.class));
+        verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
         verifyNoMoreInteractions(mConnectivityManager);
@@ -494,14 +729,14 @@
         // Emulate externally-visible WifiManager effects, when tethering mode
         // is being torn down.
         sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED);
-        mTethering.interfaceRemoved(mTestIfname);
+        mTethering.interfaceRemoved(TEST_WLAN_IFNAME);
         mLooper.dispatchAll();
 
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         // TODO: Why is {g,s}etInterfaceConfig() called more than once?
-        verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname);
+        verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, atLeastOnce())
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
         verify(mNMService, times(1)).stopTethering();
         verify(mNMService, times(1)).setIpForwardingEnabled(false);
         verifyNoMoreInteractions(mNMService);
@@ -509,7 +744,7 @@
         // Asking for the last error after the per-interface state machine
         // has been reaped yields an unknown interface error.
         assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE,
-                mTethering.getLastTetherError(mTestIfname));
+                mTethering.getLastTetherError(TEST_WLAN_IFNAME));
     }
 
     // TODO: Test with and without interfaceStatusChanged().
@@ -530,21 +765,21 @@
         // Emulate externally-visible WifiManager effects, causing the
         // per-interface state machine to start up, and telling us that
         // tethering mode is to be started.
-        mTethering.interfaceStatusChanged(mTestIfname, true);
-        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED);
+        mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true);
+        sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
         mLooper.dispatchAll();
 
         // We verify get/set called twice here: once for setup and once during
         // teardown because all events happen over the course of the single
         // dispatchAll() above.
-        verify(mNMService, times(2)).getInterfaceConfig(mTestIfname);
+        verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME);
         verify(mNMService, times(2))
-                .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class));
-        verify(mNMService, times(1)).tetherInterface(mTestIfname);
+                .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
+        verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verify(mConnectivityManager, atLeastOnce()).isTetheringSupported();
-        verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
+        verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER);
         // This is called, but will throw.
         verify(mNMService, times(1)).setIpForwardingEnabled(true);
         // This never gets called because of the exception thrown above.
@@ -552,9 +787,9 @@
         // When the master state machine transitions to an error state it tells
         // downstream interfaces, which causes us to tell Wi-Fi about the error
         // so it can take down AP mode.
-        verify(mNMService, times(1)).untetherInterface(mTestIfname);
+        verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME);
         verify(mWifiManager).updateInterfaceIpState(
-                mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
+                TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
 
         verifyNoMoreInteractions(mWifiManager);
         verifyNoMoreInteractions(mConnectivityManager);
@@ -596,7 +831,7 @@
 
     @Test
     public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final boolean currDisallow = false;
         final boolean nextDisallow = true;
         final int expectedInteractionsWithShowNotification = 1;
@@ -618,7 +853,7 @@
 
     @Test
     public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final boolean currDisallow = true;
         final boolean nextDisallow = false;
         final int expectedInteractionsWithShowNotification = 0;
@@ -629,7 +864,7 @@
 
     @Test
     public void testDisallowTetheringUnchanged() throws Exception {
-        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME};
         final int expectedInteractionsWithShowNotification = 0;
         boolean currDisallow = true;
         boolean nextDisallow = true;
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index f59850d..e377a47 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -70,6 +70,7 @@
 import android.os.Bundle;
 import android.os.INetworkManagementService;
 import android.os.Looper;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.support.test.filters.SmallTest;
@@ -88,6 +89,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -639,4 +642,32 @@
         lp.addRoute(new RouteInfo(new IpPrefix("::/1")));
         assertTrue(Vpn.providesRoutesToMostDestinations(lp));
     }
+
+    @Test
+    public void testDoesNotLockUpWithTooManyRoutes() {
+        final LinkProperties lp = new LinkProperties();
+        final byte[] ad = new byte[4];
+        // Actually evaluating this many routes under 1500ms is impossible on
+        // current hardware and for some time, as the algorithm is O(n²).
+        // Make sure the system has a safeguard against this and does not
+        // lock up.
+        final int MAX_ROUTES = 4000;
+        final long MAX_ALLOWED_TIME_MS = 1500;
+        for (int i = 0; i < MAX_ROUTES; ++i) {
+            ad[0] = (byte)((i >> 24) & 0xFF);
+            ad[1] = (byte)((i >> 16) & 0xFF);
+            ad[2] = (byte)((i >> 8) & 0xFF);
+            ad[3] = (byte)(i & 0xFF);
+            try {
+                lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32)));
+            } catch (UnknownHostException e) {
+                // UnknownHostException is only thrown for an address of illegal length,
+                // which can't happen in the case above.
+            }
+        }
+        final long start = SystemClock.currentThreadTimeMillis();
+        assertTrue(Vpn.providesRoutesToMostDestinations(lp));
+        final long end = SystemClock.currentThreadTimeMillis();
+        assertTrue(end - start < MAX_ALLOWED_TIME_MS);
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
index db5373a..7c77cf5 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java
@@ -31,7 +31,6 @@
 import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
 import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
-import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
 import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
 import static android.net.ConnectivityManager.TETHERING_USB;
 import static android.net.ConnectivityManager.TETHERING_WIFI;
@@ -39,12 +38,12 @@
 import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED;
 import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE;
 
-import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.RouteInfo;
+import android.net.util.InterfaceSet;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
@@ -75,6 +74,7 @@
     @Mock private IControlsTethering mTetherHelper;
     @Mock private InterfaceConfiguration mInterfaceConfiguration;
     @Mock private SharedLog mSharedLog;
+    @Mock private TetheringDependencies mTetheringDependencies;
 
     private final TestLooper mLooper = new TestLooper();
     private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
@@ -84,7 +84,7 @@
     private void initStateMachine(int interfaceType) throws Exception {
         mTestedSm = new TetherInterfaceStateMachine(
                 IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog,
-                mNMService, mStatsService, mTetherHelper);
+                mNMService, mStatsService, mTetherHelper, mTetheringDependencies);
         mTestedSm.start();
         // Starting the state machine always puts us in a consistent state and notifies
         // the rest of the world that we've changed from an unknown to available state.
@@ -111,7 +111,8 @@
     @Test
     public void startsOutAvailable() {
         mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(),
-                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper);
+                TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper,
+                mTetheringDependencies);
         mTestedSm.start();
         mLooper.dispatchAll();
         verify(mTetherHelper).updateInterfaceState(
@@ -346,7 +347,7 @@
      * Send a command to the state machine under test, and run the event loop to idle.
      *
      * @param command One of the TetherInterfaceStateMachine.CMD_* constants.
-     * @param obj An additional argument to pass.
+     * @param arg1 An additional argument to pass.
      */
     private void dispatchCommand(int command, int arg1) {
         mTestedSm.sendMessage(command, arg1);
@@ -371,7 +372,7 @@
      */
     private void dispatchTetherConnectionChanged(String upstreamIface) {
         mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
-                upstreamIface);
+                new InterfaceSet(upstreamIface));
         mLooper.dispatchAll();
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index c3b9def..9661dc2 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -147,6 +147,16 @@
     }
 
     @Test
+    public void testCallbacksRegistered() {
+        mUNM.start();
+        verify(mCM, times(1)).registerNetworkCallback(any(), any(), any());
+        verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any());
+
+        mUNM.stop();
+        verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+    }
+
+    @Test
     public void testRequestsMobileNetwork() throws Exception {
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 300c701..499f254 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -211,7 +211,7 @@
         signature != atoms.signatures.end(); signature++) {
         int argIndex;
 
-        fprintf(out, "void\n");
+        fprintf(out, "int\n");
         fprintf(out, "stats_write(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -251,7 +251,7 @@
                             " diff length: %s vs %s\");\n",
                             attributionDecl.fields.front().name.c_str(),
                             chainField.name.c_str());
-                        fprintf(out, "        return;\n");
+                        fprintf(out, "        return -EINVAL;\n");
                         fprintf(out, "    }\n");
                     }
                 }
@@ -284,7 +284,7 @@
             argIndex++;
         }
 
-        fprintf(out, "    event.write(LOG_ID_STATS);\n");
+        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
         fprintf(out, "}\n");
         fprintf(out, "\n");
     }
@@ -293,7 +293,7 @@
         signature != atoms.non_chained_signatures.end(); signature++) {
         int argIndex;
 
-        fprintf(out, "void\n");
+        fprintf(out, "int\n");
         fprintf(out, "stats_write_non_chained(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
@@ -327,7 +327,7 @@
             argIndex++;
         }
 
-        fprintf(out, "    event.write(LOG_ID_STATS);\n");
+        fprintf(out, "    return event.write(LOG_ID_STATS);\n");
         fprintf(out, "}\n");
         fprintf(out, "\n");
     }
@@ -377,7 +377,7 @@
     const AtomDecl &attributionDecl) {
     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
             signature != signatures.end(); signature++) {
-        fprintf(out, "void %s(int32_t code ", method_name.c_str());
+        fprintf(out, "int %s(int32_t code ", method_name.c_str());
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
             arg != signature->end(); arg++) {
@@ -522,7 +522,7 @@
     const AtomDecl &attributionDecl) {
     for (set<vector<java_type_t>>::const_iterator signature = signatures.begin();
         signature != signatures.end(); signature++) {
-        fprintf(out, "    public static native void %s(int code", method_name.c_str());
+        fprintf(out, "    public static native int %s(int code", method_name.c_str());
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
             arg != signature->end(); arg++) {
@@ -719,7 +719,7 @@
             result += java_type_signature(*arg);
         }
     }
-    result += ")V";
+    result += ")I";
     return result;
 }
 
@@ -732,7 +732,7 @@
         signature != signatures.end(); signature++) {
         int argIndex;
 
-        fprintf(out, "static void\n");
+        fprintf(out, "static int\n");
         fprintf(out, "%s(JNIEnv* env, jobject clazz UNUSED, jint code",
                 jni_function_name(java_method_name, *signature).c_str());
         argIndex = 1;
@@ -779,7 +779,7 @@
                             "\"java/lang/IllegalArgumentException\", "
                             "\"invalid attribution field(%s) length.\");\n",
                             chainField.name.c_str());
-                        fprintf(out, "        return;\n");
+                        fprintf(out, "        return -EINVAL;\n");
                         fprintf(out, "    }\n");
                     }
                     if (chainField.javaType == JAVA_TYPE_INT) {
@@ -822,7 +822,7 @@
 
         // stats_write call
         argIndex = 1;
-        fprintf(out, "    android::util::%s(code", cpp_method_name.c_str());
+        fprintf(out, "   int ret =  android::util::%s(code", cpp_method_name.c_str());
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
             if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
@@ -868,6 +868,7 @@
             }
             argIndex++;
         }
+        fprintf(out, "    return ret;\n");
 
         fprintf(out, "}\n");
         fprintf(out, "\n");
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index c79ba04..bd80bb4 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,4 +1,5 @@
 #!/bin/bash
+LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
 git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
-    python $ANDROID_BUILD_TOP/frameworks/base/tools/stringslint/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+    python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
 done
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 21ae3a9..b77b1ad 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -25,11 +25,13 @@
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.net.Uri;
+import android.net.wifi.WifiInfo;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.BackupUtils;
+import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -76,6 +78,8 @@
 
     /** {@hide} */
     private String mPasspointManagementObjectTree;
+    /** {@hide} */
+    private static final int MAXIMUM_RANDOM_MAC_GENERATION_RETRY = 3;
 
     /**
      * Recognized key management schemes.
@@ -798,27 +802,37 @@
      * @hide
      * Randomized MAC address to use with this particular network
      */
+    @NonNull
     private MacAddress mRandomizedMacAddress;
 
     /**
      * @hide
      * Checks if the given MAC address can be used for Connected Mac Randomization
-     * by verifying that it is non-null, unicast, and locally assigned.
+     * by verifying that it is non-null, unicast, locally assigned, and not default mac.
      * @param mac MacAddress to check
      * @return true if mac is good to use
      */
-    private boolean isValidMacAddressForRandomization(MacAddress mac) {
-        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned();
+    public static boolean isValidMacAddressForRandomization(MacAddress mac) {
+        return mac != null && !mac.isMulticastAddress() && mac.isLocallyAssigned()
+                && !MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS).equals(mac);
     }
 
     /**
      * @hide
      * Returns Randomized MAC address to use with the network.
-     * If it is not set/valid, create a new randomized address.
+     * If it is not set/valid, creates a new randomized address.
+     * If it can't generate a valid mac, returns the default MAC.
      */
-    public MacAddress getOrCreateRandomizedMacAddress() {
-        if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+    public @NonNull MacAddress getOrCreateRandomizedMacAddress() {
+        int randomMacGenerationCount = 0;
+        while (!isValidMacAddressForRandomization(mRandomizedMacAddress)
+                && randomMacGenerationCount < MAXIMUM_RANDOM_MAC_GENERATION_RETRY) {
             mRandomizedMacAddress = MacAddress.createRandomUnicastAddress();
+            randomMacGenerationCount++;
+        }
+
+        if (!isValidMacAddressForRandomization(mRandomizedMacAddress)) {
+            mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
         }
         return mRandomizedMacAddress;
     }
@@ -828,10 +842,7 @@
      * Returns MAC address set to be the local randomized MAC address.
      * Does not guarantee that the returned address is valid for use.
      */
-    public MacAddress getRandomizedMacAddress() {
-        if (mRandomizedMacAddress == null) {
-            mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
-        }
+    public @NonNull MacAddress getRandomizedMacAddress() {
         return mRandomizedMacAddress;
     }
 
@@ -839,7 +850,11 @@
      * @hide
      * @param mac MacAddress to change into
      */
-    public void setRandomizedMacAddress(MacAddress mac) {
+    public void setRandomizedMacAddress(@NonNull MacAddress mac) {
+        if (mac == null) {
+            Log.e(TAG, "setRandomizedMacAddress received null MacAddress.");
+            return;
+        }
         mRandomizedMacAddress = mac;
     }
 
@@ -1532,7 +1547,7 @@
         creatorUid = -1;
         shared = true;
         dtimInterval = 0;
-        mRandomizedMacAddress = MacAddress.ALL_ZEROS_ADDRESS;
+        mRandomizedMacAddress = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c8df087..433285b 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1317,6 +1317,7 @@
         if (pin) {
             NetworkRequest request = new NetworkRequest.Builder()
                     .clearCapabilities()
+                    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
                     .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                     .build();
             NetworkPinner.pin(mContext, request);
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index 7fe85be..758a8d5 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -22,7 +22,6 @@
 import android.annotation.SystemApi;
 import android.net.MacAddress;
 import android.net.wifi.aware.PeerHandle;
-import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -79,19 +78,24 @@
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
     private final int mRssi;
+    private final int mNumAttemptedMeasurements;
+    private final int mNumSuccessfulMeasurements;
     private final byte[] mLci;
     private final byte[] mLcr;
     private final long mTimestamp;
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
-            int distanceStdDevMm, int rssi, byte[] lci, byte[] lcr, long timestamp) {
+            int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
+            int numSuccessfulMeasurements, byte[] lci, byte[] lcr, long timestamp) {
         mStatus = status;
         mMac = mac;
         mPeerHandle = null;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mNumAttemptedMeasurements = numAttemptedMeasurements;
+        mNumSuccessfulMeasurements = numSuccessfulMeasurements;
         mLci = lci == null ? EMPTY_BYTE_ARRAY : lci;
         mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
         mTimestamp = timestamp;
@@ -99,13 +103,16 @@
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, byte[] lci, byte[] lcr, long timestamp) {
+            int distanceStdDevMm, int rssi, int numAttemptedMeasurements,
+            int numSuccessfulMeasurements, byte[] lci, byte[] lcr, long timestamp) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mNumAttemptedMeasurements = numAttemptedMeasurements;
+        mNumSuccessfulMeasurements = numSuccessfulMeasurements;
         mLci = lci == null ? EMPTY_BYTE_ARRAY : lci;
         mLcr = lcr == null ? EMPTY_BYTE_ARRAY : lcr;
         mTimestamp = timestamp;
@@ -163,7 +170,9 @@
     /**
      * @return The standard deviation of the measured distance (in mm) to the device specified by
      * {@link #getMacAddress()} or {@link #getPeerHandle()}. The standard deviation is calculated
-     * over the measurements executed in a single RTT burst.
+     * over the measurements executed in a single RTT burst. The number of measurements is returned
+     * by {@link #getNumSuccessfulMeasurements()} - 0 successful measurements indicate that the
+     * standard deviation is not valid (a valid standard deviation requires at least 2 data points).
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
      * exception.
@@ -191,6 +200,46 @@
     }
 
     /**
+     * @return The number of attempted measurements used in the RTT exchange resulting in this set
+     * of results. The number of successful measurements is returned by
+     * {@link #getNumSuccessfulMeasurements()} which at most, if there are no errors, will be 1 less
+     * that the number of attempted measurements.
+     * <p>
+     * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
+     * exception.
+     */
+    public int getNumAttemptedMeasurements() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getNumAttemptedMeasurements(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mNumAttemptedMeasurements;
+    }
+
+    /**
+     * @return The number of successful measurements used to calculate the distance and standard
+     * deviation. If the number of successful measurements if 1 then then standard deviation,
+     * returned by {@link #getDistanceStdDevMm()}, is not valid (a 0 is returned for the standard
+     * deviation).
+     * <p>
+     * The total number of measurement attempts is returned by
+     * {@link #getNumAttemptedMeasurements()}. The number of successful measurements will be at
+     * most 1 less then the number of attempted measurements.
+     * <p>
+     * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
+     * exception.
+     */
+    public int getNumSuccessfulMeasurements() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getNumSuccessfulMeasurements(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mNumSuccessfulMeasurements;
+    }
+
+    /**
      * @return The Location Configuration Information (LCI) as self-reported by the peer. The format
      * is specified in the IEEE 802.11-2016 specifications, section 9.4.2.22.10.
      * <p>
@@ -269,6 +318,8 @@
         dest.writeInt(mDistanceMm);
         dest.writeInt(mDistanceStdDevMm);
         dest.writeInt(mRssi);
+        dest.writeInt(mNumAttemptedMeasurements);
+        dest.writeInt(mNumSuccessfulMeasurements);
         dest.writeByteArray(mLci);
         dest.writeByteArray(mLcr);
         dest.writeLong(mTimestamp);
@@ -296,15 +347,17 @@
             int distanceMm = in.readInt();
             int distanceStdDevMm = in.readInt();
             int rssi = in.readInt();
+            int numAttemptedMeasurements = in.readInt();
+            int numSuccessfulMeasurements = in.readInt();
             byte[] lci = in.createByteArray();
             byte[] lcr = in.createByteArray();
             long timestamp = in.readLong();
             if (peerHandlePresent) {
                 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        lci, lcr, timestamp);
+                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
             } else {
                 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        lci, lcr, timestamp);
+                        numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
             }
         }
     };
@@ -316,7 +369,9 @@
                 mMac).append(", peerHandle=").append(
                 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
                 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
-                ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+                ", rssi=").append(mRssi).append(", numAttemptedMeasurements=").append(
+                mNumAttemptedMeasurements).append(", numSuccessfulMeasurements=").append(
+                mNumSuccessfulMeasurements).append(", lci=").append(mLci).append(", lcr=").append(
                 mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
     }
 
@@ -335,6 +390,8 @@
         return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
+                && mNumAttemptedMeasurements == lhs.mNumAttemptedMeasurements
+                && mNumSuccessfulMeasurements == lhs.mNumSuccessfulMeasurements
                 && Arrays.equals(mLci, lhs.mLci) && Arrays.equals(mLcr, lhs.mLcr)
                 && mTimestamp == lhs.mTimestamp;
     }
@@ -342,6 +399,6 @@
     @Override
     public int hashCode() {
         return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
-                mLci, mLcr, mTimestamp);
+                mNumAttemptedMeasurements, mNumSuccessfulMeasurements, mLci, mLcr, mTimestamp);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 8a3a7f5..3517984 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -18,12 +18,14 @@
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.assertFalse;
 
 import android.os.Parcel;
 import android.net.MacAddress;
 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
+import android.net.wifi.WifiInfo;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -176,22 +178,25 @@
     @Test
     public void testGetOrCreateRandomizedMacAddress_SavesAndReturnsSameAddress() {
         WifiConfiguration config = new WifiConfiguration();
-        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+        assertEquals(defaultMac, config.getRandomizedMacAddress());
 
         MacAddress firstMacAddress = config.getOrCreateRandomizedMacAddress();
         MacAddress secondMacAddress = config.getOrCreateRandomizedMacAddress();
 
+        assertNotEquals(defaultMac, firstMacAddress);
         assertEquals(firstMacAddress, secondMacAddress);
     }
 
     @Test
     public void testSetRandomizedMacAddress_ChangesSavedAddress() {
         WifiConfiguration config = new WifiConfiguration();
-        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, config.getRandomizedMacAddress());
+        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+        assertEquals(defaultMac, config.getRandomizedMacAddress());
 
         MacAddress macToChangeInto = MacAddress.createRandomUnicastAddress();
         config.setRandomizedMacAddress(macToChangeInto);
-        MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
+        MacAddress macAfterChange = config.getRandomizedMacAddress();
 
         assertEquals(macToChangeInto, macAfterChange);
     }
@@ -200,24 +205,37 @@
     public void testGetOrCreateRandomizedMacAddress_ReRandomizesInvalidAddress() {
         WifiConfiguration config =  new WifiConfiguration();
 
+        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
         MacAddress macAddressZeroes = MacAddress.ALL_ZEROS_ADDRESS;
         MacAddress macAddressMulticast = MacAddress.fromString("03:ff:ff:ff:ff:ff");
         MacAddress macAddressGlobal = MacAddress.fromString("fc:ff:ff:ff:ff:ff");
 
         config.setRandomizedMacAddress(null);
         MacAddress macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertFalse(macAfterChange.equals(null));
+        assertNotEquals(macAfterChange, null);
+
+        config.setRandomizedMacAddress(defaultMac);
+        macAfterChange = config.getOrCreateRandomizedMacAddress();
+        assertNotEquals(macAfterChange, defaultMac);
 
         config.setRandomizedMacAddress(macAddressZeroes);
         macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertFalse(macAfterChange.equals(macAddressZeroes));
+        assertNotEquals(macAfterChange, macAddressZeroes);
 
         config.setRandomizedMacAddress(macAddressMulticast);
         macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertFalse(macAfterChange.equals(macAddressMulticast));
+        assertNotEquals(macAfterChange, macAddressMulticast);
 
         config.setRandomizedMacAddress(macAddressGlobal);
         macAfterChange = config.getOrCreateRandomizedMacAddress();
-        assertFalse(macAfterChange.equals(macAddressGlobal));
+        assertNotEquals(macAfterChange, macAddressGlobal);
+    }
+
+    @Test
+    public void testSetRandomizedMacAddress_DoesNothingWhenNull() {
+        WifiConfiguration config = new WifiConfiguration();
+        MacAddress defaultMac = MacAddress.fromString(WifiInfo.DEFAULT_MAC_ADDRESS);
+        config.setRandomizedMacAddress(null);
+        assertEquals(defaultMac, config.getRandomizedMacAddress());
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 1e4cea1..ddddde9 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -78,7 +78,7 @@
         List<RangingResult> results = new ArrayList<>();
         results.add(
                 new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
-                        10, null, null, 666));
+                        10, 8, 5, null, null, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -232,13 +232,15 @@
         int distanceCm = 105;
         int distanceStdDevCm = 10;
         int rssi = 5;
+        int numAttemptedMeasurements = 8;
+        int numSuccessfulMeasurements = 3;
         long timestamp = System.currentTimeMillis();
         byte[] lci = { 0x5, 0x6, 0x7 };
         byte[] lcr = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC };
 
         // RangingResults constructed with a MAC address
         RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
-                lci, lcr, timestamp);
+                numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
 
         Parcel parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -254,7 +256,7 @@
 
         // RangingResults constructed with a PeerHandle
         result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
-                null, null, timestamp);
+                numAttemptedMeasurements, numSuccessfulMeasurements, null, null, timestamp);
 
         parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -280,14 +282,16 @@
         int distanceCm = 105;
         int distanceStdDevCm = 10;
         int rssi = 5;
+        int numAttemptedMeasurements = 10;
+        int numSuccessfulMeasurements = 3;
         long timestamp = System.currentTimeMillis();
         byte[] lci = { };
         byte[] lcr = { };
 
-        RangingResult rr1 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, lci,
-                lcr, timestamp);
-        RangingResult rr2 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, null,
-                null, timestamp);
+        RangingResult rr1 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
+                numAttemptedMeasurements, numSuccessfulMeasurements, lci, lcr, timestamp);
+        RangingResult rr2 = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
+                numAttemptedMeasurements, numSuccessfulMeasurements, null, null, timestamp);
 
         assertEquals(rr1, rr2);
     }